前言

最近做Spring相关的分享,对Spring IoC容器做个总结


概念

IoC

IoC(Inversion of Control,控制反转)其实是一种设计思想,在Spring,对象的创建和依赖关系的维护不再由应用程序代码直接控制,而是由容器来完成的。

具体理解就是由容器来控制对象的创建、销毁等生命周期及对象与对象之间的依赖关系,而不是传统的通过new的方式。你只需要在Spring IoC容器中登记,「你是什么」「需要什么」即可。所以可以看出来,对于某一个对象而言,控制对象的生存周期不再是引用它的对象,而是Spring IoC容器,这就叫做控制反转

通俗的说,对象的创建不用自己new和注入依赖对象,而是Spring创建和管理,本质是控制权从应用代码转移到了外部容器(IoC容器),控制器的转移就是反转。

IoC容器

在Spring框架中实现控制反转,需要依赖Spring容器,又叫IoC容器,IoC容器是Spring框架的核心,Spring中IoC容器由两种类型:BeanFactory和ApplicationContext。

BeanFactory是最基本、最底层的IoC容器,提供了基本的IoC功能。

ApplicationContext是BeanFactory的子接口,提供了更多的功能,如国际化文案MessageSource、事件ApplicationEvent等等。

两者的关系属于装饰器模式的一种实现,ApplicationContext借助BeanFactory具备了IoC容器的能力,同时扩展其它功能。

DI

DI(Dependency Injection,依赖注入)应用程序在运行时依赖IoC容器来动态注入对象所需要的外部资源,它也是IoC的一个别名,IoC是一种思想,DI是一种具体的技术实现手段。

一个比喻

人、凳子、教室

人是对象,凳子是对象,上课的教室是容器
小学:人 搬凳子 到教室上课
大学:教室有凳子,人到教室上课

先不考虑人是怎么创建的,这个是科学问题。

凳子,由人创建,变成了教室具备,凳子这个资源的变化,可以理解成控制反转。
凳子,由人给自己,变成了教室把凳子这个资源赋予人,这个过程可以理解成依赖注入。


依赖注入

在Spring中,由IoC容器管理的对象都称为Bean。一个Bean包括类名、属性、依赖关系等信息。

spring提供了3种常见的注入方式:构造器注入、Setter方法注入、字段注入。

构造器注入

构造器注入是通过类构造器来注入依赖关系的。在类中定义一个构造器,参数为依赖的引用,当Spring容器实例化类时,IoC容器将自动注入这些依赖关系。

示例:

public class MyClass {
    private MyDependency myDependency;

    public MyClass(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

Setter方法注入

Setter方法注入是通过Setter方法来注入依赖关系的。在类中定义一个Setter方法,该方法的参数是依赖关系的实例。当Spring容器实例化类时,IoC容器将自动调用这些Setter方法并注入依赖关系。

示例:

public class MyClass {
    private MyDependency myDependency;

    public void setMyDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

字段注入

字段注入是通过字段来注入依赖关系的。在类中定义一个字段,该字段被注解为@Autowired,当Spring容器实例化类时,将自动注入依赖关系。

示例:

public class MyClass {
    @Autowired
    private MyDependency myDependency;
}

依赖注入示例

创建User类

/**
 * 用户实体
 */
@Setter
@Getter
@ToString
public class User {

    private Long id;

    private String userName;

}
/**
 * 依赖注入demo
 */
public class DependencyInjectionDemo {

    @Autowired
    private User userByAutowired;

    private User userByConstructor;

    private User userBySetter;

    public DependencyInjectionDemo(User user) {
        this.userByConstructor = user;
    }

    @Autowired
    public void setUserBySetter(User user) {
        this.userBySetter = user;
    }

    /**
     * user元数据配置
     * 需要为static修饰,不然User bean需要先创建DependencyInjectionDemo,DependencyInjectionDemo创建又依赖User,导致循环引用
     * 如果是static,@Bean处理时逻辑有所不同
     */
    @Bean
    public static User createUser () {
        User user = new User();
        user.setId(1L);
        user.setUserName("kz");
        return user;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册当前类 作为配置类
        applicationContext.register(DependencyInjectionDemo.class);

        // 启动上下文
        applicationContext.refresh();

        DependencyInjectionDemo injectionDemo = applicationContext.getBean(DependencyInjectionDemo.class);
        System.out.println(injectionDemo.userByAutowired);

        System.out.println(injectionDemo.userByAutowired == injectionDemo.userByConstructor);

        System.out.println(injectionDemo.userByAutowired == injectionDemo.userBySetter);

        // 关闭
        applicationContext.close();
    }

}

执行结果:

User(id=1, userName=kz)
true
true


依赖查找

是指在Spring容器中通过名称或类型查找Bean实例的过程。通过依赖查找,我们可以获取到Spring容器中创建的Bean实例,在Spring中,我们通常使用ApplicationContext或BeanFactory来进行依赖查找。

依赖查找示例

public class DependencyLookUpDemo {

    @Autowired
    private User userByAutowired;

    /**
     * bean名称为方法名:createUser
     * @return
     */
    @Bean
    public static User createUser () {
        User user = new User();
        user.setId(1L);
        user.setUserName("kz");
        return user;
    }

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册当前类 作为配置类
        applicationContext.register(DependencyLookUpDemo.class);

        // 启动上下文
        applicationContext.refresh();
        DependencyLookUpDemo lookUpBeanDemo = applicationContext.getBean(DependencyLookUpDemo.class);

        User user = applicationContext.getBean(User.class);
        System.out.println(user);

        User user2 = applicationContext.getBean("createUser", User.class);

        System.out.println(user == lookUpBeanDemo.userByAutowired);
        System.out.println(user2 == lookUpBeanDemo.userByAutowired);

        Arrays.stream(applicationContext.getBeanFactory().getBeanNamesForType(User.class)).forEach(System.out::println);

        // 关闭
        applicationContext.close();
    }

}

执行结果:

User(id=1, userName=kz)
true
true
createUser


扩展

依赖来源(支持依赖注入与依赖查找)

1、自定义 Bean (自定义BeanDefinition或单例对象)

自定义的beanDefinition包括xml、@Bean、BeanDefinitionBuilder等方式配置。

2、容器內建 BeanDefinition(如ConfigurationClassPostProcessor、CommonAnnotationBeanPostProcessor等)

具体见org.springframework.context.annotation.AnnotationConfigUtils)

3、容器內建单例对象(如Environment、MessageSource、LifecycleProcessor、ApplicationEventMulticaster)

具体见org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory
org.springframework.context.support.AbstractApplicationContext#initLifecycleProcessor
org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
org.springframework.context.support.AbstractApplicationContext#initMessageSource

另:关于依赖来源的说明:
image-1681828088358

出自Spring相关极客专栏

依赖注入与依赖查找区别

1、获取方式和时机:
依赖查找需要主动调用容器提供的方法来获取所需的Bean实例
依赖注入则是在容器创建Bean实例时自动完成的

2、Spring 内建依赖(依赖来源的一种)不支持依赖查找,只能通过依赖注入BeanFactory、ApplicationEventPublisher、ApplicationnContext、ResourceLoader。

具体见org.springframework.context.support.AbstractApplicationContext#prepareBeanFactory

示例:

/**
 * 内建依赖 示例
 */
public class ResolvableDependencyDemo {

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;


    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();

        // 注册当前类 作为配置类
        applicationContext.register(ResolvableDependencyDemo.class);

        // 启动上下文
        applicationContext.refresh();
        ResolvableDependencyDemo resolvableDependencyDemo = applicationContext.getBean(ResolvableDependencyDemo.class);
        // 注入的
        System.out.println("resolvableDependencyDemo.applicationEventPublisher :" + resolvableDependencyDemo.applicationEventPublisher);

        // lookup,报错:org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.context.ApplicationEventPublisher' available
        // System.out.println(applicationContext.getBeanFactory().getBean(ApplicationEventPublisher.class));

        // 关闭
        applicationContext.close();
    }

}

总结

所谓IoC/DI,就是由Spring容器来管理对象生命周期及对象间依赖关系。

依赖注入相比依赖查找一个很大的区别就是,依赖注入可以注入Spring内建依赖,如BeanFactory,属于依赖来源的一种。