6. IOC 依赖注入 Dependency Injection

151 阅读31分钟

6. IOC 依赖注入 Dependency Injection

6.1 依赖注入的模式和类型

依赖注入的两种模式:

  1. 手动模式 - 配置或者编码的方式,提前安排注入规则

    • xml 资源配置 bean 元信息
    • 注解 @Bean 配置 bean 元信息
    • Spring api 配置 bean 元信息
  2. 自动模式 - 实现方提供依赖自动关联的方式,按照内建的注入规则

    • Autowiring 自动绑定

依赖注入的 5 种类型:

image-20210421225737530

Setter 方法注入与 字段注入并不相等,@Autowired 标记的字段可以不设置 Setter 方法,Spring 会使用 Java 的反射访问私有字段。blog.csdn.net/qq_19782019…

6.2 自动绑定 Autowiring 简介

Spring 并不推荐使用自动绑定,这部分不是重点。

// 补充

6.3 自动绑定 Autowiring 模式

自动绑定 Autowiring 的4 种模式:

  • no:默认值,关闭 Autowiring,Spring 并不推荐使用自动绑定,需要手动指定依赖注入的对象
  • byName:根据被注入属性的名称,作为 Bean 的名称进行依赖查找,并将对象设置到该属性
  • byType:根据被注入属性的类型,作为 Bean 的类型进行依赖查找,并将对象设置到该属性、
  • constructor:特殊的 byType 类型,用于构造器参数

6.4 自动绑定 Autowiring 限制和不足

6.5 Setter 方法依赖注入

Setter 方法依赖注入 4 种方式,这 4 种方式都会调用 Setter 方法进行依赖注入,所谓的注入就是将容器中的 bean 注入到 setter 方法的参数上。

  • xml 资源配置 bean 元信息
  • @Bean 注解配置 bean 元信息
  • spring api 配置 bean 元信息
  • 自动绑定 autowiring
  1. xml 中配置 bean 的属性,<property name="user" ref="user1"/>将 user1 注入到 user 属性中,详细见Github
<bean id="user1" class="org.geekbang.ioc.overview.lookup.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
</bean>

<!--  使用Setter注入字段  -->
<bean class="org.geekbang.dependency.injection.UserHolder">
    <!--  前面是属性名,后面是要注入的bean名称  -->
    <property name="user" ref="user1"/>
</bean>
  1. 使用注解配置中设置 bean 的属性,userHolder.setUser(user)将 user 注入到 userHolder 的属性中,详细见Github
// 容器中必须已经存在一个User类型的bean,才能注入成功
@Bean(name = "userHolder1")
public UserHolder userHolder(User user) {
    UserHolder userHolder = new UserHolder();
    // Setter注入
    userHolder.setUser(user);
    return userHolder;
}
  1. api 配置 BeanDefinition 时设置与属性绑定的 bean 名称,xml 配置文件设置 Setter 注入底层其实也是调用的 api,builder.addPropertyReference("user", "user1");

容器中已经注册了bean user1

<bean id="user1" class="org.geekbang.ioc.overview.lookup.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
</bean>

创建 BeanDefinition,将 bean user1注入到 UserHolder 中,即设置到UserHolder.user属性中

public static BeanDefinition createUserHolderBeanDefinition() {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);

    // setter 注入引用, 前面是字段名, 后面是bean名称
    builder.addPropertyReference("user", "user1");
    return builder.getBeanDefinition();
}

将 UserHolder 的 BeanDefinition 注册到容器中,然后从容器中查找 UserHolder,查看 bean user1是否注入成功

public static void main(String[] args) {
    // 创建容器, 读取xml中的bean: user
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    String location = "dependcy-setter-injection.xml";
    reader.loadBeanDefinitions(location);

    // 1.创建 userHolder BeanDefinition
    BeanDefinition userHolderBeanDefinition = createUserHolderBeanDefinition();
    // 2.注册 userHolder1 到容器
    beanFactory.registerBeanDefinition("userHolder1", userHolderBeanDefinition);

    // 3.查找bean, 查看是否注入成功
    UserHolder userHolder = beanFactory.getBean("userHolder1", UserHolder.class);
    System.out.println(userHolder);
}

输出结果:可以看到 xml 中配置的 bean user1,成功注入到了 UserHolder 中

UserHolder{user=User{id=1, name='tracccer'}}
  1. 自动绑定,这里使用 byName 的方式自动绑定,属性名要与 bean 名称相同。
<bean id="user" class="org.geekbang.ioc.overview.lookup.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
</bean>

<!--  使用Setter注入字段, 自动绑定 autowire, 属性名要与bean名称相同  -->
<bean class="org.geekbang.dependency.injection.UserHolder"
      autowire="byName">
    <!-- 使用自动绑定autowire替换掉手动配置 -->
    <!-- <property name="user" ref="user1"/> -->
</bean>
public static void main(String[] args) {
    // 加载配置文件中的bean
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("autowiring-dependcy-setter-injection.xml");

    // 依赖查找并创建bean
    UserHolder userHolder = applicationContext.getBean(UserHolder.class);
    System.out.println(userHolder);
}

输出结果:说明自动绑定成功了

UserHolder{user=User{id=1, name='tracccer'}}

6.6 构造器依赖注入

构造方法依赖注入有 4 种方式,这 4 种方式**都会调用 构造方法 进行依赖注入****,所谓的注入就是将容器中的 bean 依赖注入到构造方法的参数上。

  • xml 资源配置 bean 元信息
  • @Bean 注解配置 bean 元信息
  • spring api 构造 BeanDefinition 配置 bean 元信息
  • 自动绑定 autowiring
  1. xml 中配置 bean 的构造方法参数,<constructor-arg name="user" ref="user1"/>将容器中的 user1 实例注入到构造方法的 user 参数中
<bean id="user1" class="org.geekbang.ioc.overview.lookup.domain.User">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
</bean>

<!--  使用构造方法注入  -->
<bean class="org.geekbang.dependency.injection.UserHolder">
    <!--  前面是构造方法属性名,后面是要注入的bean名称  -->
    <constructor-arg name="user" ref="user1"/>
</bean>
public static void main(String[] args) {
    // 读取xml中的配置, xml 中配置了构造器依赖注入
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("dependcy-constructor-injection.xml");

    // 查找bean, 查看依赖注入结果
    UserHolder userHolder = applicationContext.getBean(UserHolder.class);
    System.out.println(userHolder);
}

输出结果:user 属性为 user1,说明通过构造方法依赖注入成功

UserHolder{user=User{id=1, name='tracccer'}}
  1. 使用注解 @Bean 配置 bean 时,new UserHolder(user)将容器中的 user 实例作为构造方法的参数注入到 UserHolder 中
// 容器中必须已经存在一个User类型的bean,才能注入成功
@Bean(name = "userHolder1")
public UserHolder userHolder(User user) {

    // 构造器注入
    UserHolder userHolder = new UserHolder(user);
    return userHolder;
}
  1. Spring api 依赖构造方法注入参数,创建 BeanDefinition 时,设置与构造器参数绑定的 bean 名称builder.addConstructorArgReference("user1");
public static BeanDefinition createUserHolderBeanDefinition() {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);

    // 构造器注入, 将user1实例作为构造器第一个参数注入
    builder.addConstructorArgReference("user1");
    return builder.getBeanDefinition();
}
  1. 自动绑定
<!--  使用构造器注入字段, 自动绑定 autowire -->
<bean class="org.geekbang.dependency.injection.UserHolder"
      autowire="constructor">
</bean>

这些东西都是Spring IOC 容器底层注册生命周期的基础,中间件开发基于 Spring api 开发时,也是非常重要。

6.7 字段注入

字段注入只能使用注解的方式进行依赖注入,而且没有自动绑定 Autowiring 模式

  • @Autowired
  • @Resource
  • @Inject

源码分析:

其实在启动spring IoC时,容器自动装载了一个 AutowiredAnnotationBeanPostProcessor 后置处理器,当容器扫描到@Autowied、@Resource或@Inject时,就会在IoC容器自动查找需要的bean,并装配给该对象的属性。详细见 6.15 节

  1. @Autowired,根据字段类型从容器中查找 bean,绑定到该属性上
// 字段注入,根据类型
@Autowired
private UserHolder userHolder;

public static void main(String[] args) {
    // 创建容器, 加载xml中的bean
    AnnotationConfigApplicationContext applicationContext = getApplicationContext();
    // 启动应用上下文
    applicationContext.refresh();

    // 从容器获取当前主类, 该主类已经被注册到了容器
    AnnotationDependencyFieldInjectionDemo demo = applicationContext.getBean(AnnotationDependencyFieldInjectionDemo.class);
    System.out.println(demo.userHolder);

    // 关闭应用上下文
    applicationContext.close();
}

输出结果:说明字段注入成功,都注入到了当前类的属性中。

UserHolder{user=User{id=1, name='tracccer'}}

在使用@Autowired时,首先在容器中查询对应类型的bean,     

  • 如果查询结果刚好为一个,就将该bean装配给@Autowired指定的数据          
  • 如果查询的结果不止一个,那么@Autowired会根据名称来查找。          
  • 如果查询的结果为空,那么会抛出异常。解决方法是,使用required=false  

注意 @Autowired 会忽略掉 static 静态属性,即标记无效。

  1. @Resource,有两个属性 name 和 type,如果指定了 name 或 type,则从容器中查找指定的 bean;如果都未指定,则先根据属性名作为 bean name 从容器中查找,再根据属性类型作为 bean type 从容器中查找。这个注解是属于 jdk 的,与 @Autowired 不同
@Autowired
private UserHolder userHolder2;

@Resource
private UserHolder userHolder3;

public static void main(String[] args) {
    // 从容器获取当前主类, 该主类已经被注册到了容器
    AnnotationDependencyFieldInjectionDemo demo = applicationContext.getBean(AnnotationDependencyFieldInjectionDemo.class);
    System.out.println("userHolder2: " + demo.userHolder2);
    System.out.println("userHolder3: " + demo.userHolder3);
    System.out.println("userHolder2 == userHolder3: " + (demo.userHolder2 == demo.userHolder3));
}

输出结果:字段注入成功,而且两种方式注入的字段都是同一个 bean 实例。

userHolder2: UserHolder{user=User{id=1, name='tracccer'}}
userHolder3: UserHolder{user=User{id=1, name='tracccer'}}
userHolder2 == userHolder3: true

下面是为 @Resource 配置依赖注入 bean 的名称和类型的示例。

    @Resource(name = "aaa", type = UserHolder.class, required=false)
    private UserHolder userHolder3;
  1. @Inject

6.8 方法注入

方法注入就是容器会调用注解标记的方法,从容器中查找符合的 bean 注入到方法参数中,然后调用该方法。

  • @Autowired
  • @Resource
  • @Inject
  • @Bean
  1. 方法注入的 3 个注解的演示
    private UserHolder userHolder2;

    private UserHolder userHolder3;

    // 方法注入
    @Autowired
    public void initUserHolder2(UserHolder userHolder) {
        this.userHolder2 = userHolder;
    }

    // 方法注入
    @Resource
    public void initUserHolder3(UserHolder userHolder) {
        this.userHolder3 = userHolder;
    }

    // 方法注入
    @Bean
    public UserHolder myHolder(UserHolder userHolder) {
        return userHolder;
    }
  1. 查询是否注入成功
public static void main(String[] args) {
    // 创建容器, 加载xml中的bean
    AnnotationConfigApplicationContext applicationContext = getApplicationContext();
    // 启动应用上下文
    applicationContext.refresh();

    // 从容器获取当前主类, 该主类已经被注册到了容器
    AnnotationDependencyMethodInjectionDemo demo = applicationContext.getBean(AnnotationDependencyMethodInjectionDemo.class);
    System.out.println("userHolder2: " + demo.userHolder2);
    System.out.println("userHolder3: " + demo.userHolder3);
    UserHolder myHolder = applicationContext.getBean("myHolder", UserHolder.class);
    System.out.println("myHolder: " + myHolder);

    System.out.println("userHolder2 == userHolder3: " + (demo.userHolder2 == demo.userHolder3));

    // 关闭应用上下文
    applicationContext.close();
}

输出结果:三种方式都注入成功,且都注入的是同一个 bean

userHolder2: UserHolder{user=User{id=1, name='tracccer'}}
userHolder3: UserHolder{user=User{id=1, name='tracccer'}}
myHolder: UserHolder{user=User{id=1, name='tracccer'}}
userHolder2 == userHolder3: true

6.8 接口回调方法注入

Aware 系列接口回调注入,就是将 Spring 提供的 bean 注入到我们的属性中,从而利用这些 bean 操作容器等。Aware 接口用于辅助访问 Spring 容器的功能或资源。

  • BeanFactoryAware:获取当前 IOC 容器 BeanFactory 对象

  • ApplicationContextAware:获取 Spring 应用上下文 ApplicationContext 对象

  • EnvironmentAware:获取 Environment 对象

  • ResourceLoaderAware:获取资源加载器 ResourcesLoader 对象

  • BeanClassLoaderAware:获取加载当前 Bean Class 的 ClassLoader

  • BeanNameAware:获取当前 Bean 的名称

  • MessageSourceAware:获取 MessageSource 对象,用于或计划

  • ApplicationEventPublisherAware:获取 ApplicationEventPublisher 对象,用于 Spring 事件

  • EmbeddedValueResolverAware:获取 StringValueResolver 对象,用于占位符处理

  1. 实现 Aware 回调接口
@Component
public class UserService implements BeanFactoryAware, ApplicationContextAware {

    private BeanFactory beanFactory;

    private ApplicationContext applicationContext;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
  1. 查看 Aware 回调接口注入是否成功,注入的 beanFactory 是不是当前容器 beanFactory,注入的 applicationContext 是不是当前应用上下文 applicationContext
public static void main(String[] args) {
    // 创建容器, 扫描当前包所有bean
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext("org.geekbang.dependency.injection.aware");

    // 获取bean
    UserService userService = applicationContext.getBean(UserService.class);

    // 判断aware接口回调注入的bean, 与容器中的bean是否同一个
    System.out.println(userService.getBeanFactory() == applicationContext.getBeanFactory());
    System.out.println(userService.getApplicationContext() == applicationContext);
}

输出结果:说明 Aware 回调接口注入成功,而且注入的 beanFactory 就是当前容器 beanFactory,注入的 applicationContext 就是当前应用上下文 applicationContext

true
true

6.9 依赖注入类型的选择

  • 低依赖:构造方法注入
  • 多依赖:Setter 方法注入
  • 便利性:字段注入
  • 声明类:方法注入

6.10 基础类型注入

基础类型

  • 原生类型:byte, byte, char, int, long, double....
  • 标量类型:Number,CHaracter,Boolean,Enum,Properties,UUID
  • 常规类型:Object,String,TimeZone,Calendar,Optional
  • Spring 类型:Resource,InputSource,Formatter

基础类型注入并不是一种注入方式,与 Setter 注入不是一类东西,是指所有的依赖注入时设置的类型,比如 Setter 注入时 xml 设置的 bean 属性值都是 String 类型,Spring 可以将 String 转为这些基础类型,比如配置的属性值是 classpath:/aa.xml,Spring 会将其转为属性值的类型 Resource。在构造器注入时其实也是一样的原理。

类型转换的源码分析见 Spring 类型转换 和 元信息配置 章节。

将配置的 String 类型属性转换为属性的类型,源码再 AbstractNestablePropertyAccessor#convertForProperty

  1. 创建 pojo 类,并为 bean 配置属性,其中包括 Long,Enum,Resource 类型的属性
public class Docter {
    private Long id;
    private String name;
    private CityEnum city;
    private Resource resource;

    // 省略setter/getter
    
    @Override
    public String toString() {
        return "Docter{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", city=" + city +
                ", resource=" + resource +
                '}';
    }
}
<bean id="docter" class="org.geekbang.dependency.injection.type.Docter">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
    <property name="city" value="XIAN"/>
    <property name="resource" value="user.properties"/>
</bean>

查看 bean 的属性类型是否转换成功

public static void main(String[] args) throws IOException {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("dependcy-type-injection.xml");

    Docter docter = beanFactory.getBean("docter", Docter.class);
    System.out.println("docter: " + docter);

    boolean flag = (docter.getCity() instanceof CityEnum);
    System.out.println("city属性值类型为CityEnum: " + flag);

    Resource resource = docter.getResource();
    System.out.println("URL: " + resource.getURL().toString());
}

输出结果:说明配置的 Enum 类型,Long 类型,Resource 类型的属性都成功转换

docter: Docter{id=1, name='tracccer', city=XIAN, resource=class path resource [user.properties]}
city属性值类型为CityEnumtrue
URL: file:/D:/MyProjects/idea/...target/classes/user.properties

6.11 集合类型注入

  • 数组类型 Array:基础类型组成的数组
  • 集合类型 Collection:List,Set,Properties
  1. pojo 类中有数组属性,List 属性
public class Docter {
    private Long id;
    private String name;
    private CityEnum city;
    private Resource resource;

    private CityEnum[] workCitys;
    private List<CityEnum> lifeCitys;
}
  1. 为数组属性 workCitys,List 属性 lifeCitys 配置值,用逗号分隔即可
<bean id="docter" class="org.geekbang.dependency.injection.type.Docter">
    <property name="id" value="1"/>
    <property name="name" value="tracccer"/>
    <property name="city" value="XIAN"/>
    <property name="workCitys" value="XIAN,BEIJING"/>
    <property name="lifeCitys" value="HANGZHOU,XIAN"/>
    <property name="resource" value="user.properties"/>
</bean>
  1. 查看集合类型的属性是否注入成功
public static void main(String[] args) throws IOException {
    BeanFactory beanFactory = new ClassPathXmlApplicationContext("dependcy-type-injection.xml");
    Docter docter = beanFactory.getBean("docter", Docter.class);

    System.out.println("========集合类型的配置=========");
    CityEnum[] workCitys = docter.getWorkCitys();
    System.out.println("workCitys数组:" + workCitys[0] + ", " + workCitys[1]);

    List<CityEnum> lifeCitys = docter.getLifeCitys();
    System.out.println("lifeCitys集合: " + lifeCitys);
}

输出结果:数组类型,集合类型的属性都配置成功

========集合类型的配置=========
workCitys数组:XIAN, BEIJING
lifeCitys集合: [HANGZHOU, XIAN]

下面两种配置集合属性的方式作用是一致的。

<property name="lifeCitys" value="HANGZHOU,XIAN"/>

<property name="lifeCitys">
    <list>
        <value>HANGZHOU</value>
        <value>BEIJING</value>
    </list>
</property>

6.12 限定注入

用注解 @Qualifier 限制依赖注入的 Bean,一般配合 @Autowired 使用

  • 指定 Bean 名称
  • 指定 @Qualifier 标记的 Bean(不常用)

基于注解 @Qualifier 的扩展限定注入的 Bean

  • 自定义注解 - 如 Spring Cloud 的 @LoadBalanced

依赖注入 @Qualifier 指定 Bean 名称

  1. 容器中存个 2 个 User 类型的 bean
    @Bean
    public User user1() {
        return createUser(1L);
    }

    @Bean
    public User user2() {
        return createUser(2L);
    }

    private User createUser(Long id) {
        User user = new User();
        user.setId(id);
        return user;
    }
  1. 依赖注入,使用 @Qualifier("user1")限制注入的 bean 的名称,否则会报异常 UnsatisfiedDependencyException,NoUniqueBeanDefinitionException
    @Autowired
	@Qualifier("user1")
    private User user;

依赖注入 @Qualifier 标记的 Bean

  1. 容器中存在多个 User 类型的 bean
    @Bean
    public User user2() {
        return createUser(2L);
    }

    @Bean
    @Qualifier
    public User user3() {
        return createUser(3L);
    }
  1. 依赖注入,使用@Qualifier限制注入的 bean 必须是被@Qualifier标记的, 即user3,否则也会报异常
    @Autowired
    @Qualifier
    private User admin;

依赖注入@Qualifier派生注解标记的 Bean

  1. 创建自定义注解,必须是 @Qualifier 的派生,用于标记 bean
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface UserGroup {
}
  1. 容器中存在多个 User 类型的 bean
    @Bean
    public User user2() {
        return createUser(2L);
    }

    @Bean
    @Qualifier
    public User user3() {
        return createUser(3L);
    }

    @Bean
    @UserGroup
	// 使用@UserGroup标记bean
    public User user4() {
        return createUser(4L);
    }
  1. 依赖注入,通过自定义扩展@UserGroup,限制注入被 @UserGroup标记的 bean,即user4,否则会排除异常。需要注意的是使用 @Qualifier,会依赖注入被 @Qualifier 及其派生 @UserGroup 标记的 bean,即user3,user4
    @Autowired
    @UserGroup
    private User manager;

    @Autowired
    @Qualifier
    private Collection<User> users;

@LoadBalanced 的使用

  1. SpringCloud 中 @LoadBalanced 注解的使用,用来标记 RestTemplate 实例
    @Bean  
	@LoadBalanced       
	// 不配置会报错 UnknownHostException: nacos-payment-provider
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
  1. 通过 @LoadBalanced 源码可知,是 SpringCloud 自定义的 @Qualifier 注解的派生,用来给 bean 分组。
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

6.13 延迟依赖注入

延迟依赖注入指不在容器启动时依赖注入,在查找 bean 时才进行依赖注入

  • 使用 ObjectFactory 延迟注入
    • 单一 bean
    • 多个 bean
  • 使用 ObjectProvider 延迟注入(推荐)
    • 单一 bean
    • 多个 bean

我创建了一个类,然后依赖多个 bean,但是这些 bean 是可配可不配的,就像是一些属性是可配可不配的,如果使用 @Autowired,找不到就会报异常 NoSuchBean,但是如果找不到 ObjectProvider#getObject() 只会返回空而不会抛出异常,保证应用的正常运行

实时注入

  1. 容器中不存在 User 类型的 bean,使用 @Autowired 注入到 user 属性中
// 实时注入
@Autowired
private User user;
  1. 创建容器并启动,加载bean并进行依赖注入,由于 bean 不存在,会报错异常UnsatisfiedDependencyException 和 NoSuchBeanDefinitionException,所以依赖注入失败
public static void main(String[] args) {
    // 创建容器并启动, 加载bean, 进行依赖注入
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
}

延迟注入

  1. 容器中不存在 User 类型的 bean,使用 @Autowired 注入到 ObjectProvider 中,是延迟依赖注入
// 延迟注入
@Autowired
private ObjectProvider<User> objectProvider;
  1. 创建容器并启动,加载 bean 进行依赖注入,虽然 user bean 不存在,但是 ObjectProvider 是延迟注入,即此时不依赖注入 user,故不会报错。
public static void main(String[] args) {
    // 创建容器并启动, 加载bean, 进行依赖注入
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
}
  1. 获取 bean,此时才进行依赖注入,故称之为延迟依赖注入,因为 bean 不存在,报异常UnsatisfiedDependencyException 和 NoSuchBeanDefinitionException
public static void main(String[] args) {
    // 创建容器并启动, 加载bean, 进行依赖注入
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
    LazyAnnotationDependencyInjectionDemo demo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);
    
    // 此时才进行依赖注入, 会报异常
    User user = demo.objectProvider.getObject();
}

6.14 依赖处理过程(重点)

public interface AutowireCapableBeanFactory extends BeanFactory {
	@Nullable
	Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;


	@Nullable
	Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;

}

依赖描述类,描述依赖的相关信息,包括注入的类,注入的字段,注入的字段名称等。

public class DependencyDescriptor extends InjectionPoint implements Serializable {

    // 前3个属性继承自InjectionPoint
    // 注入的方法参数
	protected MethodParameter methodParameter;

	// 注入的字段
	protected Field field;

	// 注入的字段注解
	private volatile Annotation[] fieldAnnotations;
    
    // 注入的类名称
	private final Class<?> declaringClass;

	// 注入的方法名称
	private String methodName;

	// 注入方法的参数类型, Setter/构造器/自定义方法
	private Class<?>[] parameterTypes;

    // 注入方法的参数索引
	private int parameterIndex;

	// 对应注入的字段名称
	private String fieldName;

    // 对应@Autowired的required
	private final boolean required;

    // 饥饿, @Lazy为false, 实时为true
	private final boolean eager;

	private int nestingLevel = 1;
    
	// 
	private Class<?> containingClass;

    // 泛型处理
	private transient volatile ResolvableType resolvableType;
	// 类型描述
	private transient volatile TypeDescriptor typeDescriptor;

1. 源码分析

  1. 首先创建一个容器,并且设置依赖注入,然后在 DefaultListableBeanFactory#resolveDependency 设置断点,进行 debug
public class AnnotationDependencyInjectionResolutionDemo {
    /**
     * DependencyDescriptor:
     * 必须(required=true)
     * 实时注入(eager=true)
     * 通过类型(User.class)依赖查找
     * 字段名称("user")
     * 是否首要(primary=true)
     */
    @Autowired
    private User user;

    public static void main(String[] args) {
        // 创建容器, 加载当前类中的bean, 加载xml配置中的bean
        AnnotationConfigApplicationContext applicationContext = getApplicationContext();

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

        AnnotationDependencyInjectionResolutionDemo demo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);
        System.out.println(demo.user);
    }
}
  1. 依赖注入的入口方法DefaultListableBeanFactory#resolveDependency,在应用上下文启动完成 bean 实例化refresh()->finishBeanFactoryInitialization(beanFactory)的阶段进行调用
@Override
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    
    // descriptor.getDependencyType() 返回需要依赖注入字段的类型
    // 若注入的字段是Optional类型
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
    }
    // 若注入的字段时ObjectFactory或ObjectProvider类型,即延迟注入
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
             ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }

    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    }
    // 默认处理依赖
    else {
        // 这里返回null, 如果是@Lazy会返回CGLIB代理对象
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
        if (result == null) {
            // 处理依赖(重点)
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}
  1. 处理依赖,doXXX,是真正有业务逻辑的代码,处理依赖的逻辑非常简单,只是夹杂了很多判断和其他代码,处理依赖的步骤如下:

    1. 如果被注入的字段是集合类型,直接返回所有候选 bean
    2. 如果被注入的字段不是集合类型
       1. 从容器中根据类型查找 bean
       2. 如果只找到一个候选 bean,则直接返回
       3. 如果找到多个 bean,则根据优先级,primary 找到唯一符合条件的 bean
       4. 如果没找到符合条件的候选 bean,或者存在多个符合条件的候选 bean,则抛出异常
    

    更详细可以参考Spring 处理依赖的流程图

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
        Object shortcut = descriptor.resolveShortcut(this);
        if (shortcut != null) {
            return shortcut;
        }

        // 获取需要依赖注入字段的类型, 这里是User.class
        Class<?> type = descriptor.getDependencyType();
        
        // 这里value为null, 跳过, 处理@Value注解, 见7.7章节
        Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
        if (value != null) {}

        // 跳过,处理集合类型的bean, 见下方代码块
        // 如果返回为null说明不是集合类型, 如果是集合类型, 最多返回空的集合
        Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
        if (multipleBeans != null) {
            // 直接返回
            return multipleBeans;
        }

        // (重点)查找需要注入的bean User,可能存在多个满足条件的候选bean
        // 这里返回两个 user, superUser
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
        
        // 符合条件的候选bean为空, 抛出异常NoSuchBeanDefinitionException
        if (matchingBeans.isEmpty()) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            return null;
        }

        String autowiredBeanName;
        Object instanceCandidate;

        // 候选bean存在多个
        if (matchingBeans.size() > 1) {
            // 见第4步
            // 判断哪个候选bean是需要注入的bean, 判断条件包括@primary, 优先级@Order
            // 底层会使用AbstractBeanDefinition#isPrimary判断bean元信息中是否有primary
            // 这里superUser元信息中有primary, 所以返回"superUser"
            // 如果没有括@primary和@Priority,则按照bean名称进行查找, @Autowired使用字段名称作为bean名称
            autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
            
            // 存在多个符合条件的bean,无法确定注入的bean名称
            if (autowiredBeanName == null) {
                if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                    // 如果依赖是必须的required-true, 则抛出异常NoUniqueBeanDefinitionException
                    return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                }
                else {
                	return null;
                }
            }
            // 根据注入bean名称"superUser", 从候选者中获取bean实例
            instanceCandidate = matchingBeans.get(autowiredBeanName);
        } else {
            // 只存在一个候选bean, 直接返回bean实例和bean名称即可
            Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
            autowiredBeanName = entry.getKey();
            instanceCandidate = entry.getValue();
        }

        // 将要注入的bean名称superUser加入到Set集合中
        if (autowiredBeanNames != null) {
            autowiredBeanNames.add(autowiredBeanName);
        }
        if (instanceCandidate instanceof Class) {
            // 底层使用beanFactory.getBean(beanName)查找到bean实例
            // this.getBean(autowiredBeanName), 用不到type
            // 之前是SuperUser.class,之后是 superUser对象
            instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
        }
        
        Object result = instanceCandidate;
        // 跳过
        if (result instanceof NullBean) {
            if (isRequired(descriptor)) {
                raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
            }
            result = null;
        }
        // 跳过
        if (!ClassUtils.isAssignableValue(type, result)) {
            throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
        }
        // 返回bean
        return result;
    }
    finally {
        ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
    }
}

存在多个候选 bean 时,会根据 @Primary,@Order,目标 bean 名称,确定唯一要进行依赖注入的 bean,如果确定不了唯一的一个,则返回 null

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
    // 获取目标bean的类型
    Class<?> requiredType = descriptor.getDependencyType();
    // 找到标记了@Primary的bean
    String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
    if (primaryCandidate != null) {
        return primaryCandidate;
    }
    // 找到标记了@Order且优先级高的bean
    String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
    if (priorityCandidate != null) {
        return priorityCandidate;
    }
    
    // 遍历所有候选bean
    for (Map.Entry<String, Object> entry : candidates.entrySet()) {
        String candidateName = entry.getKey();
        Object beanInstance = entry.getValue();
        // 条件1: 目标bean实例不能为null, 且是游离对象,直接返回
        // 条件2: 目标bean名称与候选bean的名称或别名相同, 直接返回
        // 这里的目标bean名称就是@Autowired的字段名称, @Qualifier限定的字段名称不在这处理
        // 两个条件满足 或 关系,
        if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
            matchesBeanName(candidateName, descriptor.getDependencyName())) {
            
            return candidateName;
        }
    }
    return null;
}
  1. 查找 bean(Demo) 需要注入的依赖 bean(User)
// 查找依赖注入的候选bean
protected Map<String, Object> findAutowireCandidates(
    @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

    // 从当前容器this查找所有类型为requiredType=User.class的候选bean名称, 
    // 包含非单例的, 且非@Lazy的, 这里会查找到两个
    // BeanFactoryUtils 我们之前使用过,会进行层次性查找祖先容器
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    // 用于保存所有满足条件的候选bean: user,superUser
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);

    // 跳过, 处理FactoryBean等接口的实现类
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
    }
    // 遍历候选bean, 将候选bean加入 result
    for (String candidate : candidateNames) {
        // 1.要注入的候选bean与被注入的bean名称不能相同,
        // 比如Userservice中注入了自己 private Userserivce us,那么容器中可能还有Userserivce类型的bean,应该把自身排除出去
        // 2.根据@Qualifier条件筛选bean
        // isAutowireCandidate()底层使用checkQualifiers()检查bean是否符合@Qualifier限定条件
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    
    // 这里不为空, 跳过
    if (result.isEmpty()) {
    }
    
    return result;
}
  1. 处理集合类型,参考 6.11 章节的集合类型注入,下面的代码就是处理集合类型的依赖注入,Spring 支持 4 种集合类型:Stream, Array, Collection, Map。
@Nullable
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
                                    @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

    
    // 获取集合类型, Spring支持4种集合类型 Stream,Array,Collection,Map
    final Class<?> type = descriptor.getDependencyType();

    if (descriptor instanceof StreamDependencyDescriptor) {
        // 处理Stream类型
    }
    else if (type.isArray()) {
        // 处理数组类型
    }
    else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
		// 处理Collection类型
    }
    else if (Map.class == type) {
        // 获取Map中注入的bean类型, 这里为User.class
        ResolvableType mapType = descriptor.getResolvableType().asMap();
        // 获取key类型
        Class<?> keyType = mapType.resolveGeneric(0);
        if (String.class != keyType) {
            return null;
        }
        // 获取value类型
        Class<?> valueType = mapType.resolveGeneric(1);
        if (valueType == null) {
            return null;
        }
        
        // 从容器中查找所有User类型的bean
        Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
                                               		new MultiElementDescriptor(descriptor));
        if (matchingBeans.isEmpty()) {
            return null;
        }
        // 将候选bean加入Set集合
        if (autowiredBeanNames != null) {
            autowiredBeanNames.addAll(matchingBeans.keySet());
        }
        // 返回所有候选bean
        return matchingBeans;
    }
    else {
        // 非上述4种集合类型, 返回null
        return null;
    }
}

2. 延迟依赖注入源码分析

  1. ObjectProvider 延迟依赖注入源码分析,下面是一个延迟依赖注入的 demo
public class LazyAnnotationDependencyInjectionDemo {
    // 延迟注入
    @Autowired
    private ObjectProvider<User> objectProvider;

    public static void main(String[] args) {
        // 创建容器并启动, 加载bean, 进行依赖注入,
        // 虽然 user bean 不存在, 但是ObjectProvider是延迟注入,所以不会报错
        // 这里会new DependencyObjectProvider()赋值给objectProvider 
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
        System.out.println("依赖注入成功,延迟依赖未进行注入");

        LazyAnnotationDependencyInjectionDemo demo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);
        System.out.println("查找bean,延迟注入的bean需要注入了...");
        User user = demo.objectProvider.getObject();
        System.out.println(user);
    }
    // @Bean
    // 注释掉@Bean, 虽然容器中不存在bean, 因为是延迟注入, 所以容器启动时不会报错,
    // 只有在objectProvider.getObject() 时才会进行依赖注入, 会报错
    public User user1() {
        User user = new User();
        user.setId(1L);
        return user;
    }
}

容器启动,处理依赖,对于延迟依赖注入的情况,即被注入字段是 ObjectFactory 或 ObjectProvider 类型,直接返回一个 DependencyObjectProvider 对象,注入到相应字段

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
	// ....
    
    // 处理延迟依赖注入, 被注入字段是ObjectFactory和ObjectProvider
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
             ObjectProvider.class == descriptor.getDependencyType()) {
        // 创建一个ObjectProvider对象并返回, 当后面查找对象时, 才进行依赖注入
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
	// ....
}

在获取 bean 时,即调用 getObject() 时才进行依赖查找,依赖查找过程与普通的依赖注入过程一致

DefaultListableBeanFactory.DependencyObjectProvider#getObject()

@Override
public Object getObject() throws BeansException {
    if (this.optional) {
        return createOptionalDependency(this.descriptor, this.beanName);
    }
    else {
        // 解析依赖, 参考第3步
        Object result = doResolveDependency(this.descriptor, this.beanName, null, null);
        if (result == null) {
            throw new NoSuchBeanDefinitionException(this.descriptor.getResolvableType());
        }
        return result;
    }
}

3. 延迟依赖注入 @Lazy 源码分析

  1. @Lazy 标记在字段上表示延迟依赖注入,会给字段注入一个新创建 CGLIB 代理对象,下面是演示代码
public class AnnotationDependencyInjectionResolutionDemo {
    // DependencyDescriptor:
    /**
     * DependencyDescriptor:
     * 必须(required=true)
     * 实时注入(eager=true)
     * 通过类型(User.class)依赖查找
     * 字段名称("user")
     * 是否首要(primary=true)
     */
    @Autowired
    private User user;

    @Autowired
    @Lazy
    private User lazyUser;    // superUser

    public static void main(String[] args) {
        // 创建容器, 加载当前类中的bean, 加载xml配置中的两个bean
        AnnotationConfigApplicationContext applicationContext = getApplicationContext();
        // 启动 Spring 应用上下文
        applicationContext.refresh();

        AnnotationDependencyInjectionResolutionDemo demo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);
        //期待输出 superUser
        System.out.println("User user: " + demo.user);

        // lazyUser是一个代理对象, 虽然和user属性完全一致, 但二者并不相等
        // 这里会调用targetSource.getTarget(), 然后处理依赖, 见debug点
        System.out.println("User lazyUser: " + demo.lazyUser);
        System.out.println("lazyUser==user: " + (demo.lazyUser == demo.user));
    }
}

入口方法,容器启动,处理依赖,对于被注入字段 @Lazy 标记,直接返回一个CGLIB 对象,注入到对应字段

public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
	// ...
    else {
        // 见下方代码块
        // 获取CGLIB对象,直接返回,见下方代码块
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}

创建 CGLIB 代理对象并返回,当使用该对象时,会调用targetSource.getTarget()获取被代理的对象,即第 3 步处理依赖,从容器中查找符合条件的 bean 并返回。

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
    Assert.state(getBeanFactory() instanceof DefaultListableBeanFactory,
                 "BeanFactory needs to be a DefaultListableBeanFactory");
    final DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) getBeanFactory();
    TargetSource ts = new TargetSource() {
        @Override
        public Class<?> getTargetClass() {
            return descriptor.getDependencyType();
        }
        @Override
        public boolean isStatic() {
            return false;
        }
        // debug点
        @Override
        public Object getTarget() {
            // 处理依赖, 查找符合条件的bean, 见第3步
            Object target = beanFactory.doResolveDependency(descriptor, beanName, null, null);
            if (target == null) {
				// 处理找不到bean的情况, 返回空集合或抛出异常
            }
            // 返回符合条件的bean
            return target;
        }
        @Override
        public void releaseTarget(Object target) {
        }
    };
    // 创建代理工厂, 设置被代理的对象
    ProxyFactory pf = new ProxyFactory();
    pf.setTargetSource(ts);
    
    Class<?> dependencyType = descriptor.getDependencyType();
    if (dependencyType.isInterface()) {
        pf.addInterface(dependencyType);
    }
    // 返回代理对象
    return pf.getProxy(beanFactory.getBeanClassLoader());
}

// 补充:debug FactoryBean,List 集合注入,@Lazy,@Resource,Setter,构造器依赖注入,本章前面的各种依赖注入方式

看源码要多用折叠,将不会进入的条件分支,不是主业务逻辑的边角代码折叠起来

6.15 @Autowired 注入原理

@Autowired 注入过程的核心类是 AutowiredAnnotationBeanPostProcessor,分为以下两个步骤:

  • 构建 @Autowired 字段元信息
  • 处理字段进行依赖注入,会根据字段类型进行查找,如果存在多个,会先根据 @Qualifier 筛选,然后根据字段名称筛选
  • 核心方法:AutowiredAnnotationBeanPostProcessor#postProcessProperties
  • 参考 Spring依赖注入@AutoWired

image-20210428134507592

下面的例子用来讨论 bean 实例被创建时,以下统称为 Demo,Demo 使用@Autowired 注入字段 user

public class AnnotationDependencyInjectionResolutionSimpleDemo {
    @Autowired
    private User user;

    public static void main(String[] args) {
        // 创建容器, 加载当前类中的bean, 加载xml配置中的两个bean
        // user, superUser, superUser有primary属性, 优先匹配, 且有 address属性
        AnnotationConfigApplicationContext applicationContext = getApplicationContext();
        // 启动 Spring 应用上下文
        applicationContext.refresh();

        AnnotationDependencyInjectionResolutionSimpleDemo demo = applicationContext.getBean(AnnotationDependencyInjectionResolutionSimpleDemo.class);
        //期待输出 superUser
        System.out.println("User user: " + demo.user);
    }
}

user 字段被解析为注入元信息 InjectionMetadata,源码如下所示

public class InjectionMetadata {
	// 被注入的类,这里是 Demo
    private final Class<?> targetClass;
	// 被注入的字段或方法,如user
	private final Collection<InjectedElement> injectedElements;
    
	private volatile Set<InjectedElement> checkedElements;
}

public abstract static class InjectedElement {
	// 被@Autowired标记的字段或方法
    protected final Member member;
	// 被标记的成员是否为字段
    protected final boolean isField;

    // 使用它对属性进行反射的读和谐
    protected final PropertyDescriptor pd;
}

@Autowired 注入的核心类是 AutowiredAnnotationBeanPostProcessor,有两个重要功能:

  1. 构建 @Autowired 字段元信息AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
  2. 处理字段进行依赖注入 AutowiredAnnotationBeanPostProcessor#postProcessProperties
// 用来表示处理的注解类型, 有序的
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);

// 构造方法, 表示这个类会处理@Autowired @Value @Inject 注解
public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        // 如果@Inject.class存在,则会处理该注解,如果不存在则忽略
        // @Inject需要手动引入依赖
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                                          ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // 找不到javax.inject.Inject,忽略该异常
    }
}

1. @Autowired 字段元信息解析

  1. AutowiredAnnotationBeanPostProcessor 用来处理 @Autowired 标记的需要依赖注入的字段或方法记录下来,方便后续 populateBean() 创建bean实例时使用,方法中 merge 的含义是合并 bean 的字段与其父类的字段。
AutowiredAnnotationBeanPostProcessor.java
    
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 查找@Autowired标记字段的元信息
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    // 将需要Spring依赖注入的字段保存到metadata.checkedElements
    // metadata调用inject()方法进行属性的注入, 具体参考6.14章节
    metadata.checkConfigMembers(beanDefinition);
}
  1. 查找@Autowired标记字段的注入元信息,会先从缓存中查找,如果不存在则会手动构建。这里会将构建的注入元信息加入到缓存中,供后续创建bean处理依赖时使用。
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
	// bean名称作为缓存的key
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
	// 根据bean名称从缓存中查找bean的注入元信息是否存在, 第一次肯定不存在
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);

    // metadata不为null则返回true, 且metadata要注入的目标类与clazz相同
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            
            metadata = this.injectionMetadataCache.get(cacheKey);
            // 双重检测,保证缓存中没有需要当前bean需要注入的元信息
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                
                // (重点)创建注入的元信息,见下方代码块
                metadata = buildAutowiringMetadata(clazz);
                // 将目标类cacheKey的@Autowired字段元信息保存到缓存中
                // 依赖注入时会从缓存中查找
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    return metadata;
}
  1. 手动构建注入字段的元信息,遍历目标类的所有字段,统计被@Autowired或@Value标记的字段
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    }

    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
        // 用来保存注入元信息
        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

        // 处理被@Autowired或@Value标记的字段
        // doWithLocalFields 获取targetClass的所有字段, 见下方代码块
        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            // 如果未被@Autowired或@Value标记返回null
            MergedAnnotation<?> ann = findAutowiredAnnotation(field);
            if (ann != null) {
                // 如果字段是static,则返回
                if (Modifier.isStatic(field.getModifiers())) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Autowired annotation is not supported on static fields: " + field);
                    }
                    return;
                }
                // 获取@Autowired 的required属性
                boolean required = determineRequiredStatus(ann);
                // 将注入元信息即字段和required封装为AutowiredFieldElement加入到List集合中
                currElements.add(new AutowiredFieldElement(field, required));
            }
        });

        // 处理方法,这里省略
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {});
		// 将注入元信息加入list
        elements.addAll(0, currElements);
        // 获取当前bean的父类, 需要对父类中依赖注入的属性进行处理
        targetClass = targetClass.getSuperclass();
    } while (targetClass != null && targetClass != Object.class);
    // 循环处理, 直至其父类是Object.class

    // 将bean的class作为key,注入字段的元信息作为value,封装为InjectionMetadata
    return (elements.isEmpty() ? InjectionMetadata.EMPTY : new InjectionMetadata(clazz, elements));}

获取 clazz 的所有字段,遍历所有字段,将字段作为参数,执行前面设置的函数表达式

public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
    for (Field field : getDeclaredFields(clazz)) {
        try {
            fc.doWith(field);
        }
        catch (IllegalAccessException ex) {
            throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
        }
    }
}

2. 依赖注入过程

  1. 处理字段,从缓存中查找当前 bean 的注入元信息,然后进行注入
AutowiredAnnotationBeanPostProcessor.java
    
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 从缓存中查找当前bean的注入元信息, 上一步解析将注入元信息加入到缓存中
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        // 进行依赖注入,见下方代码块
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}
InjectionMetadata.java

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	// 从InjectionMetadata中获取需要注入的字段或方法
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate =
        (checkedElements != null ? checkedElements : this.injectedElements);
    
    if (!elementsToIterate.isEmpty()) {
        // 遍历需要注入的字段或方法,即注入元信息
        for (InjectedElement element : elementsToIterate) {
            if (logger.isTraceEnabled()) {
                logger.trace("Processing injected element of bean '" + beanName + "': " + element);
            }
            // 进行注入, 目标类是target, 注入字段的名称在element.field中
            // 见下方代码块
            element.inject(target, beanName, pvs);
        }
    }
}
  1. 对字段进行注入,见6.14章节会调用 beanFactory.resolveDependency() 进行依赖查找,获取符合注入条件的 bean,通过反射将其写入到需要被注入的字段中。若使用 @Qualifier 设置了 bean 名称,也会在 beanFactory.resolveDependency() 依赖查找时,调用QualifierAnnotationAutowireCandidateResolver#checkQualifiers 来判断候选 bean 是否符合条件。
// 该类是InjectedElement的子类,拥有注入元信息
AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement.java
    
// 向bean的字段注入依赖
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    // 获取被注入的字段, 从InjectedElement.member获取
    Field field = (Field) this.member;
    Object value;
    
    // 当前字段的注入元信息是否在缓存中, 第一次为false
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
        // 获取被注入字段的依赖描述
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // 解析依赖, 从容器中查找要注入的bean,见6.14节
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        }
        catch (BeansException ex) {
            // 存在多个符合条件的bean或者不存在bean,抛出异常
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        
        // 将注入元信息加入到缓存中
        synchronized (this) {
			// ...
        }
    }
    if (value != null) {
        // 使用反射将private字段修改为可访问
        // 这也说明了@Autowired本质上不是setter注入
        ReflectionUtils.makeAccessible(field);
        // 将bean的字段field设置值为value, 当然value也是容器中的bean
        field.set(bean, value);
    }
}

经过这个步骤,@Autowired 的注入过程就完全结束了,需要注意的是,这是一个递归的过程,Demo 的被@Autowired标记的字段注入时,会从容器中查找符合条件的bean,查找时如果这个 bean 也存在被@Autowired标记的字段,就会重复这个过程。

// 补充: 1. 本章节多参考 Spring 源码第8-6章节 攻坚Bean实例的创建 2@Autowired 注入源码分析和自定义依赖注入 3. 补充流程图 4. Spring依赖注入之注入Bean获取详解

6.16 @Inject 注入原理

@Inject 可以和 @Named 配合使用,等价于 @Autowired + @Qualifier。JSR-330引入 @Inject 的目的是与 Spring 框架解耦,这样编写的代码,将 Spring 框架替换为支持 JSR-330 的其他 IOC 框架,依然可以正常使用。不过话说回来,最好的 IOC 框架依然是 Spring,似乎没有替换的必要。

  1. 引入 @Inject 依赖
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

@Inject 注入原理与 @Autowired 完全相同,都是 AutowiredAnnotationBeanPostProcessor 进行处理,其构造方法源码可以看到,会处理 @Autowired @Value @Inject 注解的注入操作。

AutowiredAnnotationBeanPostProcessor.java

// 用来表示处理的注解类型, 有序的
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);

// 构造方法, 表示这个类会处理@Autowired @Value @Inject 注解
public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);
    try {
        // 如果@Inject.class存在,则会处理该注解,如果不存在则忽略
        // @Inject需要手动引入依赖
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
                                          ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // 找不到javax.inject.Inject,忽略该异常
    }
}

在字段元信息解析步骤中(见6.15.1.3章节),会查询字段的注解,如果是 @Autowired @Value @Inject 这三种类型,则进行处理。

AutowiredAnnotationBeanPostProcessor.java

private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
    MergedAnnotations annotations = MergedAnnotations.from(ao);
    // 查询字段注解是否在autowiredAnnotationTypes中
    for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
        MergedAnnotation<?> annotation = annotations.get(type);
        if (annotation.isPresent()) {
            return annotation;
        }
    }
    return null;
}

6.17 Java 通用注解注入原理

将两个章节讲解了 AutowiredAnnotationBeanPostProcessor 处理 @Autowired,@Inject 注解。

这一节讲解 CommonAnnotationBeanPostProcessor,也是注入处理的核心类,主要处理以下注解标记的字段:

  • java.annotation.Resource
  • java.annotation.PostConstruct
  • javax.annotation.PreDestroy
  • javax.xml.ws.WebServiceRef
  • java.ejb.EJB
CommonAnnotationBeanPostProcessor.java

private static Class<? extends Annotation> webServiceRefClass;

private static Class<? extends Annotation> ejbRefClass;

private static Set<Class<? extends Annotation>> resourceAnnotationTypes = new LinkedHashSet<>(4);

static {
    // 处理的注解包括@WebServiceRef
    Class<? extends Annotation> clazz = (Class<? extends Annotation>)
        ClassUtils.forName("javax.xml.ws.WebServiceRef", CommonAnnotationBeanPostProcessor.class.getClassLoader());
    webServiceRefClass = clazz;

    // 处理的注解包括@EJB
    Class<? extends Annotation> clazz = (Class<? extends Annotation>)
        ClassUtils.forName("javax.ejb.EJB", CommonAnnotationBeanPostProcessor.class.getClassLoader());
    ejbRefClass = clazz;

    // 处理的注解包括@Resource
    resourceAnnotationTypes.add(Resource.class);
    if (webServiceRefClass != null) {
        resourceAnnotationTypes.add(webServiceRefClass);
    }
    if (ejbRefClass != null) {
        resourceAnnotationTypes.add(ejbRefClass);
    }
}

// 构造方法, 处理的注解包括@PostConstruct @PreDestroy @WebServiceContext
public CommonAnnotationBeanPostProcessor() {
    setOrder(Ordered.LOWEST_PRECEDENCE - 3);
    setInitAnnotationType(PostConstruct.class);
    setDestroyAnnotationType(PreDestroy.class);
    ignoreResourceType("javax.xml.ws.WebServiceContext");
}

1. @Resource 注入过程分析

  1. 构建注入字段元信息,CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
  2. 构建 @PostConstruct,@PreDestroy 方法元信息,InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
  3. 构建 @Resource 字段元信息,CommonAnnotationBeanPostProcessor#buildResourceMetadata
  4. 注入字段处理,CommonAnnotationBeanPostProcessor#postProcessProperties
  5. 字段的注入,InjectionMetadata.InjectedElement#inject
  6. 从容器中查找符合注入条件的bean,CommonAnnotationBeanPostProcessor#autowireResource

2. @Resource 注入规则分析

下面源码分析 @Resource 注入的规则:

  1. bean 类型优先使用@Resource 的 type 属性,如果未设置则使用字段类型
  2. @Resource 设置了 bean 名称即 name 属性,则根据 bean 名称 + bean 类型去容器查找,
  3. @Resource 未设置 bean 名称即 name 属性,则将字段名称作为 bean 名称
    1. 若容器中存在与字段名称的同名bean,则根据 bean 名称 + bean 类型去容器查找
    2. 若容器中不存在与字段名称的同名bean,则根据 bean 类型去容器查找,只有这一种情况会使用类型查找
    /**
     * 字段名称与bean名称相同的情况
     * factory.containsBean(name)为false
     * 将字段名作为bean名称, 字段类型作为bean类型去容器查找, 返回user
     */
    @Resource
    private User user;

    /**
     * 字段名称与bean名称不同的情况
     * element.isDefaultName为true, !factory.containsBean(name)为true
     * 根据字段类型去容器查找, 能找到两个, 返回primary类型的 superUser
     */
    @Resource
    private User user2;

    /**
     * 指定bean名称的情况
     * element.isDefaultName为false
     * 将name作为bean名称, 字段类型作为bean类型去容器查找, 返回user
     */
    @Resource(name="user")
    private User user3;

    /**
     * 指定bean类型的情况
     * !factory.containsBean(name)为false
     * 将type作为bean类型, 去容器查找, 返回superUser
     */
    @Resource(type= SuperUser.class)
    private User user4;
CommonAnnotationBeanPostProcessor.java

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
    throws NoSuchBeanDefinitionException {

    Object resource;
    Set<String> autowiredBeanNames;
    // @Resource未设置name时默认为字段名称
    String name = element.name;

    // 判断容器类型
    if (factory instanceof AutowireCapableBeanFactory) {
        AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
        DependencyDescriptor descriptor = element.getDependencyDescriptor();

        // 若@Resource未显式设置bean名称,且容器中不包含与字段名的同名bean,则进入该分支,根据类型查找bean
        // @Resource未显式设置name使用字段名作为默认名称,则isDefaultName为true
        // !factory.containsBean(name)容器中存在名称为name的bean时返回false
        if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
            autowiredBeanNames = new LinkedHashSet<>();
            
            // 根据类型去容器中查找bean,见6.13节
            resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
            if (resource == null) {
                throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
            }
        }
        // @Resource显式设置了name, 或容器中存在名称为name的bean时, 则进入该分支
        else {
            // 根据名称+类型的方式去容器查找, descriptor.getDependencyType()获取字段类型
            resource = beanFactory.resolveBeanByName(name, descriptor);
            autowiredBeanNames = Collections.singleton(name);
        }
    }
    
    // ....

    return resource;
}

img

6.18 自定义依赖注入注解

  1. 自定义依赖注入注解,派生 @Autowired,创建之后即可使用,不需要其他操作即可拥有与 @Autowired 同样的功能
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Autowired
public @interface MyAutowired {
    boolean required() default true;
}
  1. 自定义依赖注入注解,自己构建 PostProcessor 处理该注解
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InjectedUser {
}
// 必须是static类型, 要在普通bean被读取之前加载
// 该bean与内建bean internalAutowiredAnnotationProcessor 都会生效,依次调用
@Bean
public static AutowiredAnnotationBeanPostProcessor beanPostProcessor() {
    AutowiredAnnotationBeanPostProcessor postProcessor = new AutowiredAnnotationBeanPostProcessor();
    // 替换AutowiredAnnotationBeanPostProcessor处理的注解类型,使其处理@InjectedUser标记的字段
    postProcessor.setAutowiredAnnotationType(InjectedUser.class);

    return postProcessor;
}
  1. 使用自定义依赖注入注解
public class AnnotationDependencyCustomInjectionDemo {
    @Autowired
    private User user;

    @MyAutowired
    private User myAutowiredUser;

    @InjectedUser
    private User myInjectedUser;

    public static void main(String[] args) {
        // 创建容器, 加载当前类中的bean, 加载xml配置中的两个bean
        // user, superUser, superUser有primary属性, 优先匹配, 且有 address属性
        AnnotationConfigApplicationContext applicationContext = getApplicationContext();
        // 启动 Spring 应用上下文
        applicationContext.refresh();

        AnnotationDependencyCustomInjectionDemo demo = applicationContext.getBean(AnnotationDependencyCustomInjectionDemo.class);
        //期待输出 user
        System.out.println("User user: " + demo.user);
        System.out.println("User myAutowiredUser: " + demo.myAutowiredUser);
        System.out.println("User myInjectedUser: " + demo.myInjectedUser);
    }

输出结果

User user: User{id=1, name='tracccer, address='杭州'}
User myAutowiredUser: User{id=1, name='tracccer, address='杭州'}
User myInjectedUser: User{id=1, name='tracccer, address='杭州'}

常见异常

UnsatisfiedDependencyException

6.19 面试题

  1. 有多少种依赖注入的方式?

    答:构造器注入,Setter 注入,字段注入,方法注入,接口回调注入

  2. 你是偏好构造器注入还是 Setter 注入?

    答:两种依赖注入均可以使用,如果是强依赖,推荐使用构造器注入,Setter 用于可选依赖

  3. 讲述下依赖注入的处理过程,处理时机

    答:参考 6.14 章节

  4. @Autowired 与 @Resource 依赖注入的区别?

    答:参考6.15 章节与 6.17章节

  5. Spring 依赖注入的来源有哪些?

    答:依赖查找的来源相比依赖注入要少一些,详细见下一章 IOC 依赖来源