6. IOC 依赖注入 Dependency Injection
6.1 依赖注入的模式和类型
依赖注入的两种模式:
-
手动模式 - 配置或者编码的方式,提前安排注入规则
- xml 资源配置 bean 元信息
- 注解 @Bean 配置 bean 元信息
- Spring api 配置 bean 元信息
-
自动模式 - 实现方提供依赖自动关联的方式,按照内建的注入规则
- Autowiring 自动绑定
依赖注入的 5 种类型:
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
- 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>
- 使用注解配置中设置 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;
}
- 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'}}
- 自动绑定,这里使用 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
- 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'}}
- 使用注解 @Bean 配置 bean 时,
new UserHolder(user)将容器中的 user 实例作为构造方法的参数注入到 UserHolder 中
// 容器中必须已经存在一个User类型的bean,才能注入成功
@Bean(name = "userHolder1")
public UserHolder userHolder(User user) {
// 构造器注入
UserHolder userHolder = new UserHolder(user);
return userHolder;
}
- Spring api 依赖构造方法注入参数,创建 BeanDefinition 时,设置与构造器参数绑定的 bean 名称
builder.addConstructorArgReference("user1");
public static BeanDefinition createUserHolderBeanDefinition() {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserHolder.class);
// 构造器注入, 将user1实例作为构造器第一个参数注入
builder.addConstructorArgReference("user1");
return builder.getBeanDefinition();
}
- 自动绑定
<!-- 使用构造器注入字段, 自动绑定 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 节
- @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 静态属性,即标记无效。
- @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;
- @Inject
6.8 方法注入
方法注入就是容器会调用注解标记的方法,从容器中查找符合的 bean 注入到方法参数中,然后调用该方法。
- @Autowired
- @Resource
- @Inject
- @Bean
- 方法注入的 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;
}
- 查询是否注入成功
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 对象,用于占位符处理
- 实现 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;
}
}
- 查看 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
- 创建 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属性值类型为CityEnum: true
URL: file:/D:/MyProjects/idea/...target/classes/user.properties
6.11 集合类型注入
- 数组类型 Array:基础类型组成的数组
- 集合类型 Collection:List,Set,Properties
- pojo 类中有数组属性,List 属性
public class Docter {
private Long id;
private String name;
private CityEnum city;
private Resource resource;
private CityEnum[] workCitys;
private List<CityEnum> lifeCitys;
}
- 为数组属性 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>
- 查看集合类型的属性是否注入成功
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 名称
- 容器中存个 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;
}
- 依赖注入,使用
@Qualifier("user1")限制注入的 bean 的名称,否则会报异常 UnsatisfiedDependencyException,NoUniqueBeanDefinitionException
@Autowired
@Qualifier("user1")
private User user;
依赖注入 @Qualifier 标记的 Bean
- 容器中存在多个 User 类型的 bean
@Bean
public User user2() {
return createUser(2L);
}
@Bean
@Qualifier
public User user3() {
return createUser(3L);
}
- 依赖注入,使用
@Qualifier限制注入的 bean 必须是被@Qualifier标记的, 即user3,否则也会报异常
@Autowired
@Qualifier
private User admin;
依赖注入@Qualifier派生注解标记的 Bean
- 创建自定义注解,必须是 @Qualifier 的派生,用于标记 bean
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface UserGroup {
}
- 容器中存在多个 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);
}
- 依赖注入,通过自定义扩展
@UserGroup,限制注入被@UserGroup标记的 bean,即user4,否则会排除异常。需要注意的是使用 @Qualifier,会依赖注入被 @Qualifier 及其派生 @UserGroup 标记的 bean,即user3,user4
@Autowired
@UserGroup
private User manager;
@Autowired
@Qualifier
private Collection<User> users;
@LoadBalanced 的使用
- SpringCloud 中 @LoadBalanced 注解的使用,用来标记 RestTemplate 实例
@Bean
@LoadBalanced
// 不配置会报错 UnknownHostException: nacos-payment-provider
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
- 通过 @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() 只会返回空而不会抛出异常,保证应用的正常运行
实时注入
- 容器中不存在 User 类型的 bean,使用 @Autowired 注入到 user 属性中
// 实时注入
@Autowired
private User user;
- 创建容器并启动,加载bean并进行依赖注入,由于 bean 不存在,会报错异常UnsatisfiedDependencyException 和 NoSuchBeanDefinitionException,所以依赖注入失败
public static void main(String[] args) {
// 创建容器并启动, 加载bean, 进行依赖注入
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
}
延迟注入
- 容器中不存在 User 类型的 bean,使用 @Autowired 注入到 ObjectProvider 中,是延迟依赖注入
// 延迟注入
@Autowired
private ObjectProvider<User> objectProvider;
- 创建容器并启动,加载 bean 进行依赖注入,虽然 user bean 不存在,但是 ObjectProvider 是延迟注入,即此时不依赖注入 user,故不会报错。
public static void main(String[] args) {
// 创建容器并启动, 加载bean, 进行依赖注入
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(LazyAnnotationDependencyInjectionDemo.class);
}
- 获取 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 依赖处理过程(重点)
- 入口 - DefaultListableBeanFactory#resolveDependency
- 依赖描述符 - DependencyDescriptor
- 自动绑定候选对象处理器 - AutowireCandidateResolver
- Spring 处理依赖的流程图
- 参考:Spring依赖注入之注入Bean获取详解
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. 源码分析
- 首先创建一个容器,并且设置依赖注入,然后在 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);
}
}
- 依赖注入的入口方法,
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;
}
}
-
处理依赖,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;
}
- 查找 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;
}
- 处理集合类型,参考 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. 延迟依赖注入源码分析
- 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 源码分析
- @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
下面的例子用来讨论 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,有两个重要功能:
- 构建 @Autowired 字段元信息AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
- 处理字段进行依赖注入 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 字段元信息解析
- 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);
}
- 查找@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;
}
- 手动构建注入字段的元信息,遍历目标类的所有字段,统计被@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. 依赖注入过程
- 处理字段,从缓存中查找当前 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);
}
}
}
- 对字段进行注入,见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,似乎没有替换的必要。
- 引入 @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 注入过程分析
- 构建注入字段元信息,CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
- 构建 @PostConstruct,@PreDestroy 方法元信息,InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata
- 构建 @Resource 字段元信息,CommonAnnotationBeanPostProcessor#buildResourceMetadata
- 注入字段处理,CommonAnnotationBeanPostProcessor#postProcessProperties
- 字段的注入,InjectionMetadata.InjectedElement#inject
- 从容器中查找符合注入条件的bean,CommonAnnotationBeanPostProcessor#autowireResource
2. @Resource 注入规则分析
下面源码分析 @Resource 注入的规则:
- bean 类型优先使用@Resource 的 type 属性,如果未设置则使用字段类型
- @Resource 设置了 bean 名称即 name 属性,则根据 bean 名称 + bean 类型去容器查找,
- @Resource 未设置 bean 名称即 name 属性,则将字段名称作为 bean 名称
- 若容器中存在与字段名称的同名bean,则根据 bean 名称 + bean 类型去容器查找
- 若容器中不存在与字段名称的同名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;
}
6.18 自定义依赖注入注解
- 自定义依赖注入注解,派生 @Autowired,创建之后即可使用,不需要其他操作即可拥有与 @Autowired 同样的功能
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Autowired
public @interface MyAutowired {
boolean required() default true;
}
- 自定义依赖注入注解,自己构建 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;
}
- 使用自定义依赖注入注解
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 面试题
-
有多少种依赖注入的方式?
答:构造器注入,Setter 注入,字段注入,方法注入,接口回调注入
-
你是偏好构造器注入还是 Setter 注入?
答:两种依赖注入均可以使用,如果是强依赖,推荐使用构造器注入,Setter 用于可选依赖
-
讲述下依赖注入的处理过程,处理时机
答:参考 6.14 章节
-
@Autowired 与 @Resource 依赖注入的区别?
答:参考6.15 章节与 6.17章节
-
Spring 依赖注入的来源有哪些?
答:依赖查找的来源相比依赖注入要少一些,详细见下一章 IOC 依赖来源