目录
一、IOC介绍
1、什么是IOC
2、通过案例来了解IoC
2.1 传统程序开发
2.2 问题分析
2.3 解决方案
2.4 IoC程序开发
2.5 IoC 优势
二、DI介绍
三、IOC 详解
3.1 Bean的存储
3.1.1 @Controller(控制器存储)
3.1.2 @Service(服务存储)
3.1.3 @Repository(仓库存储)
3.1.4 @Component(组件存储)
3.1.5 @Configuration(配置存储)
3.2 为什么要这么多类注解?
3.3 方法注解 @Bean
3.3.1 方法注解要配合类注解使用
3.3.2 定义多个对象
3.3.3 重命名 Bean
四、DI 详解
4.1 属性注入
4.2 构造方法注入
4.3 Setter 注入
4.4 @Autowired存在问题
一、IOC介绍
1、什么是IOC
什么是控制反转呢? 也就是控制权反转。 什么的控制权发生了反转? 获得依赖对象的过程被反转了。也就是说, 当需要某个对象时, 传统开发模式中需要自己通过 new 创建对象, 现在不需要再进行创 建, 把创建对象的任务交给容器, 程序中只需要依赖注入 (Dependency Injection,DI)就可以了.这个容器称为:IoC容器. Spring是一个IoC容器, 所以有时Spring 也称为Spring 容器。
2、通过案例来了解IoC
案例:造一辆车。
 
2.1 传统程序开发
public class NewCarExample {
    public static void main(String[] args) {
        Car car = new Car();
        car.run();
    }
    /**
     * 汽车对象
     */
    static class Car {
        private Framework framework;
        public Car() {
            framework = new Framework();
            System.out.println("Car init....");
        }
        public void run(){
            System.out.println("Car run...");
        }
    }
    /**
     * 车身类
     */
    static class Framework {
        private Bottom bottom;
        public Framework() {
            bottom = new Bottom();
            System.out.println("Framework init...");
        }
    }
    /**
     * 底盘类
     */
    static class Bottom {
        private Tire tire;
        public Bottom() {
            this.tire = new Tire();
            System.out.println("Bottom init...");
        }
    }
    /**
     * 轮胎类
     */
    static class Tire {
        // 尺⼨
        private int size;
        public Tire(){
            this.size = 17;
            System.out.println("轮胎尺⼨:" + size);
        }
    }
} 
 2.2 问题分析
 /**
     * 轮胎类
     */
    static class Tire {
        // 尺⼨
        private int size;
        public Tire(int size){
            this.size = size;
            System.out.println("轮胎尺⼨:" + size);
        }
    } 
修改之后, 底盘类就会报错:

那么我们将底盘类的构造方法也传一个参数后,底盘类方法不再报错。但其他调用程序又会出现错误,所以我们都要进行修改。
修改后完整代码:
public class NewCarExample {
    public static void main(String[] args) {
        Car car = new Car(20);
        car.run();
    }
    /**
     * 汽⻋对象
     */
    static class Car {
        private Framework framework;
        public Car(int size) {
            framework = new Framework(size);
            System.out.println("Car init....");
        }
        public void run(){
            System.out.println("Car run...");
        }
    }
    /**
     * ⻋⾝类
     */
    static class Framework {
        private Bottom bottom;
        public Framework(int size) {
            bottom = new Bottom(size);
            System.out.println("Framework init...");
        }
    }
    /**
     * 底盘类
     */
    static class Bottom {
        private Tire tire;
        public Bottom(int size) {
            this.tire = new Tire(size);
            System.out.println("Bottom init...");
        }
    }
    /**
     * 轮胎类
     */
    static class Tire {
        // 尺⼨
        private int size;
        public Tire(int size){
            this.size = size;
            System.out.println("轮胎尺⼨:" + size);
        }
    }
} 
2.3 解决方案
 
我们可以这样理解:当我们造一辆汽车时,如果我们不自己制作轮胎,而是把轮胎外包出去,那 么即使当用户的需求发生改变,需要改变车轮尺寸大小时,我们只需要向代理工厂下订单就行了,我们自身是不需要出力的。
2.4 IoC程序开发
public class IocCarExample {
    public static void main(String[] args) {
        Tire tire = new Tire(20);
        Bottom bottom = new Bottom(tire);
        Framework framework = new Framework(bottom);
        Car car = new Car(framework);
        car.run();
    }
    static class Car {
        private Framework framework;
        public Car(Framework framework) {
            this.framework = framework;
            System.out.println("Car init....");
        }
        public void run() {
            System.out.println("Car run...");
        }
    }
    static class Framework {
        private Bottom bottom;
        public Framework(Bottom bottom) {
            this.bottom = bottom;
            System.out.println("Framework init...");
        }
    }
    static class Bottom {
        private Tire tire;
        public Bottom(Tire tire) {
            this.tire = tire;
            System.out.println("Bottom init...");
        }
    }
    static class Tire {
        private int size;
        public Tire(int size) {
            this.size = size;
            System.out.println("轮胎尺⼨:" + size);
        }
    }
} 
 2.5 IoC 优势
 
 
 - 资源集中管理: IoC容器会帮我们管理一些资源(对象等), 我们需要使用时, 只需要从IoC容器中去取就可以了
 - 我们在创建实例的时候不需要了解其中的细节, 降低了使用资源双方的依赖程度, 也就是耦合度.
 
Spring 就是一种IoC容器, 帮助我们来做了这些资源管理。
二、DI介绍
 
三、IOC 详解
既然 Spring 是一个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:
- 存
 - 取
 Spring 容器 管理的主要是对象, 这些对象, 我们称之为"Bean". 我们把这些对象交由Spring管理, 由 Spring来负责对象的创建和销毁。我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出 对象。
3.1 Bean的存储
- 类注解:@Controller、@Service、@Repository、@Component、@Configuration.
 - 方法注解:@Bean.
 
3.1.1 @Controller(控制器存储)
使用 @Controller 存储 bean 的代码如下:
@Controller // 将对象存储到 Spring 中
public class UserController {
    public void sayHi(){
        System.out.println("hi,UserController...");
    }
} 
然后我们来验证一下Spring容器中是否已经有了该对象:
从Spring 容器中获取对象(bean):
@SpringBootApplication
public class SpringIocDemoApplication {
	public static void main(String[] args) {
		//获取Spring上下⽂对象
		ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
		//从Spring上下⽂中获取对象
		UserController userController = context.getBean(UserController.class);
		//使⽤对象
		userController.sayHi();
	}
} 
 
获取bean对象的其他方式:上述代码是根据类型来查找对象, 如果Spring容器中,同一个类型存在多个bean的话, 怎么来获取呢?ApplicationContext 也提供了其他获取bean的方式, ApplicationContext 获取bean对象的功能, 是父 类BeanFactory提供的功能.我们来看一下其父类代码:public interface BeanFactory { //以上省略... // 1. 根据bean名称获取bean Object getBean(String var1) throws BeansException; // 2. 根据bean名称和类型获取bean <T> T getBean(String var1, Class<T> var2) throws BeansException; // 3. 按bean名称和构造函数参数动态创建bean,只适⽤于具有原型(prototype)作⽤域的bean Object getBean(String var1, Object... var2) throws BeansException; // 4. 根据类型获取bean <T> T getBean(Class<T> var1) throws BeansException; // 5. 按bean类型和构造函数参数动态创建bean, 只适⽤于具有原型(prototype)作⽤域的 bean <T> T getBean(Class<T> var1, Object... var2) throws BeansException; //以下省略... }其中第一、二、四种方式,是常见的。其中有通过 bean 的名称来获取 bean。
Spring bean是Spring框架在运行时管理的对象, Spring会给管理的对象起一个名字. 给每个对象起一个名字, 根据Bean的名称(BeanId)就可以获取到对应的对象.
Bean 命名约定
比如:
类名: UserController, Bean的名称为: userController类名: AccountManager, Bean的名称为: accountManager类名: AccountService, Bean的名称为: accountService
也有一些特殊情况, 当有多个字符并且第一个和第二个字符都是大写时, 将保留原始的大小写。
比如:类名: UController, Bean的名称为: UController类名: AManager, Bean的名称为: AManager
在知道 bean 的名称约定之后,我们来看看以多种方式获取 bean 对象是如何实现的。
示例代码:
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context = SpringApplication.run(SpringIocDemoApplication.class, args);
        //从Spring上下⽂中获取对象
        //根据bean类型, 从Spring上下⽂中获取对象
        UserController userController1 = context.getBean(UserController.class);
        //根据bean名称, 从Spring上下⽂中获取对象
        UserController userController2 = (UserController)context.getBean("userController");
        //根据bean类型+名称, 从Spring上下⽂中获取对象
        UserController userController3 =
                context.getBean("userController",UserController.class);
        System.out.println(userController1);
        System.out.println(userController2);
        System.out.println(userController3);
    }
} 
运行结果:
 
我们可以看到,地址一样,说明对象是同一个。
3.1.2 @Service(服务存储)
使用 @Service 存储 bean 的代码如下所示:
@Service
public class UserService {
    public void sayHi(String name) {
        System.out.println("Hi," + name);
    }
} 
获取 bean 的代码同 @Controller 部分的代码,我们可以获取 bean ,让 bean 对象执行 sayHi 方法,看打印结果,来判断是否获取到了bean。
此处,若把 @Service 注解去掉,会出现 与去掉 @Controller 注解 一样的错误。
3.1.3 @Repository(仓库存储)
使用@Repository 存储 bean 的代码如下所示:
@Repository
public class UserRepository {
    public void sayHi() {
        System.out.println("Hi, UserRepository~");
    }
} 
3.1.4 @Component(组件存储)
使用@Component 存储 bean 的代码如下所示:
@Component
public class UserComponent {
    public void sayHi() {
        System.out.println("Hi, UserComponent~");
    }
} 
3.1.5 @Configuration(配置存储)
@Configuration
public class UserConfiguration {
    public void sayHi() {
        System.out.println("Hi,UserConfiguration~");
    }
} 
 3.2 为什么要这么多类注解?
- @Controller:控制层, 接收请求, 对请求进行处理, 并进行响应.
 - @Servie:业务逻辑层, 处理具体的业务逻辑.
 - @Repository:数据访问层,也称为持久层. 负责数据访问操作
 - @Configuration:配置层. 处理项目中的一些配置信息.
 
3.3 方法注解 @Bean
- 使用外部包里的类, 没办法添加类注解
 - 一个类, 需要多个对象, 比如多个数据源
 
3.3.1 方法注解要配合类注解使用
@Component
public class BeanConfig {
    @Bean
    public User user(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
} 
来获取 bean 对象:
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context =
                SpringApplication.run(SpringIocDemoApplication.class, args);
        //从Spring上下⽂中获取对象
        User user = context.getBean(User.class);
        //使⽤对象
        System.out.println(user);
    }
} 
执行代码,可以看到正确结果:

3.3.2 定义多个对象
示例代码:
@Component
public class BeanConfig {
    @Bean
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2(){
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
} 
 在上述代码,我们一个类型(User),定义了多个对象。
@SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context =
                SpringApplication.run(SpringIocDemoApplication.class, args);
        //从Spring上下⽂中获取对象
        User user = context.getBean(User.class);
        //使⽤对象
        System.out.println(user);
    }
} 
 @SpringBootApplication
public class SpringIocDemoApplication {
    public static void main(String[] args) {
        //获取Spring上下⽂对象
        ApplicationContext context =
                SpringApplication.run(SpringIocDemoApplication.class, args);
        //根据bean名称, 从Spring上下⽂中获取对象
        User user1 = (User) context.getBean("user1");
        User user2 = (User) context.getBean("user2");
        System.out.println(user1);
        System.out.println(user2);
    }
} 
   运行结果:

3.3.3 重命名 Bean
我们可以通过设置 name 属性给 Bean 对象进行重命名操作,如下代码所示:
@Bean(name = {"u1","user1"})
public User user1(){
    User user = new User();
    user.setName("zhangsan");
    user.setAge(18);
    return user;
} 
代码中,name={} 可以省略,直接写为:@Bean({"u1","user1"})
此时我们使用名字 u1 就可以获取到 User 对象了。
注意:
使用五大注解声明的bean,不一定生效,要想生效,就必须让Spring扫描到这些注解。
启动类默认扫描的范围是 SpringBoot启动类所在包及其子包。若想让 Spring 扫描到默认范围以外的地方,就需要通过 @ComponentScan 来配置扫描路径。
四、DI 详解
- 属性注入
 - 构造方法注入
 - Setter 注入
 
4.1 属性注入
属性注入是使用 @Autowired 实现的。
我们以 Service 类注入到 Controller 类中 为例:
@Service
public class UserService {
    public void sayHi() {
        System.out.println("Hi,UserService");
    }
} 
Controller 类的实现代码如下:
@Controller
public class UserController {
    //注⼊⽅法1: 属性注⼊
    @Autowired
    private UserService userService;
    public void sayHi(){
        System.out.println("hi,UserController...");
        userService.sayHi();
    }
} 
如果不加 @Autowired 注解,也就是userService对象没有注入进来,当执行到 userService.sayHi() 时,会报空指针异常。
4.2 构造方法注入
@Controller
public class UserController2 {
    //注⼊⽅法2: 构造⽅法
    private UserService userService;
    @Autowired
    public UserController2(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController2...");
        userService.sayHi();
    }
} 
 4.3 Setter 注入
@Controller
public class UserController3 {
    //注⼊⽅法3: Setter⽅法注⼊
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public void sayHi(){
        System.out.println("hi,UserController3...");
        userService.sayHi();
    }
} 
 4.4 @Autowired存在问题
当同一类型存在多个bean时, 使用@Autowired会存在问题:
示例代码:
@Component
public class BeanConfig {
    @Bean("u1") //bean 重命名为u1
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
} 
@Controller
public class UserController {
    //注⼊user
    @Autowired
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        System.out.println(user);
    }
} 
运行代码发现会报错,报错的原因是,非唯一的 Bean 对象。
- @Primary
 - @Qualifier
 - @Resource
 
使用@Primary注解:当存在多个相同类型的Bean注入时,加上@Primary注解,来确定默认的实现.
@Component
public class BeanConfig {
    @Primary //指定该bean为默认bean的实现
    @Bean("u1") //bean 重命名为u1
    public User user1(){
        User user = new User();
        user.setName("zhangsan");
        user.setAge(18);
        return user;
    }
    @Bean
    public User user2() {
        User user = new User();
        user.setName("lisi");
        user.setAge(19);
        return user;
    }
} 
@Controller
public class UserController {
    @Qualifier("user2") //指定bean名称
    @Autowired
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        System.out.println(user);
    }
} 
 @Controller
public class UserController {
    @Resource(name = "user2")
    private User user;
    public void sayHi(){
        System.out.println("hi,UserController...");
        System.out.println(user);
    }
} 
@Autowird 与 @Resource的区别:
- @Autowired 是spring框架提供的注解,而@Resource是JDK提供的注解。
 - @Autowired 默认是按照类型注入,而@Resource是按照名称注入. 相比于 @Autowired 来说,@Resource 支持更多的参数设置,例如 name 设置,根据名称获取 Bean。
 
关于 Spring IOC(控制权反转)、DI(依赖注入)就先介绍到这里了,希望可以给你带来帮助呀!


















