文章目录
夯实Spring系列|第九章:IoC 依赖注入(专题)-下
本章说明
接着上一章 夯实Spring系列|第八章:IoC 依赖注入(专题)-上 的内容,我们继续讨论 Spring IoC 依赖注入的其他方式,以及大家比较关心的@Autowired
原理分析。
1.项目环境
- jdk 1.8
- spring 5.2.2.RELEASE
- github 地址:https://github.com/huajiexiewenfeng/thinking-in-spring
- 本章模块:dependency-injection
2.依赖注入类型选择
这么多的注入类型,我们平时该怎么选择和使用呢?
-
低依赖:构造器注入
- 强制依赖注入,依赖比较少,参数比较少的情况推荐使用(官方推荐)
-
多依赖:Setter 方法注入
- 多依赖,如果依赖之间存在前后依赖关系的话,尽量不要用这种方式注入
-
便利性:字段注入
- 仅关注与需要注入的字段,但是这种官方并不推荐,在后续的 SpringBoot 版本中都属于淘汰状态
-
声明类:方法注入
- 建议多使用
@Bean
这种方式进行注入,配合其他注入方式。
- 建议多使用
3.基础类型注入
此部分用 夯实Spring系列|第二章:IoC 依赖查找 里面的例子进行演示
github 相关源码模块 ioc-container-overview
- com.huajie.thinking.in.spring.ioc.overview.dependency.lookup.DependencyLookUpDemo
基础类型
- 原生类型(Primitive):boolean、byte、char、short、int、float、long、double
- 标量类型(Scalar):Number、Character、Boolean、Enum、Locale、Charset、Currency、Porperties、UUID
- 常规类型(General):Object、String、TimeZone、Calendar、Optional 等等
- Spring 类型:Resource、InputSource、Formatter 等等
3.1 Enum 类型
比如我们 User 类里面加一个 City 枚举类型的字段,表示这个用户是哪个城市的。
我们先建一个 Enum 类型的 City 类
public enum City {
WUHAN,
BEIJING,
SHANGHAI
}
User 类更新
public class User{
private Long id;
private String name;
private Integer age;
private City city;
private City[] cities;
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public City[] getCities() {
return cities;
}
public void setCities(City[] cities) {
this.cities = cities;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
public static User createUser() {
User user = new User();
user.setName("static-user");
return user;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", city=" + city +
", cities=" + Arrays.toString(cities) +
'}';
}
@PostConstruct
public void init() {
System.out.println(beanName + "用户对象初始化...");
}
@PreDestroy
public void destroy() {
System.out.println(beanName + "用户对象销毁...");
}
}
dependency-lookup-context.xml 文件 增加 city 和 cities
<bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
<property name="name" value="xwf"/>
<property name="age" value="18"/>
<property name="id" value="1"/>
<property name="city" value="WUHAN"/>
<property name="cities" value="WUHAN,BEIJING"/>
</bean>
输出结果:
实时查找---User{id=1, name='xwf', age=18, city=WUHAN, cities=[WUHAN, BEIJING]}
3.2 Resource 类型
在上面的基础之上
User 增加 private Resource configFileReource;
和对应的 set/get 方法。
完整代码如下:
/**
* 用户类
*/
public class User{
private Long id;
private String name;
private Integer age;
private Resource configFileReource;
private City city;
private City[] cities;
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
public City[] getCities() {
return cities;
}
public void setCities(City[] cities) {
this.cities = cities;
}
public Resource getConfigFileReource() {
return configFileReource;
}
public void setConfigFileReource(Resource configFileReource) {
this.configFileReource = configFileReource;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
public static User createUser() {
User user = new User();
user.setName("static-user");
return user;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", configFileReource=" + configFileReource +
", city=" + city +
", cities=" + Arrays.toString(cities) +
'}';
}
@PostConstruct
public void init() {
System.out.println(beanName + "用户对象初始化...");
}
@PreDestroy
public void destroy() {
System.out.println(beanName + "用户对象销毁...");
}
}
在 resouces/META_INF目录下新建 user-config.properties 文件
dependency-lookup-context.xml 文件增加 configFileReource
<bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
<property name="name" value="xwf"/>
<property name="age" value="18"/>
<property name="id" value="1"/>
<property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
<property name="city" value="WUHAN"/>
<property name="cities" value="WUHAN,BEIJING"/>
</bean>
输出结果:
因为 toString() 的缘故看起来是字符串,实际上是 Reource 这个类型
实时查找---User{id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING]}
4.集合类型注入
集合类型
- 数组类型(Array):原生类型、标量类型、常规类型、Spring类型
- 集合类型(Collection):
- Collection:List、Set(SortedSet、NavigableSet、EnumSet)
- Map:Properties
4.1 数组类型
3.1 Enum类型 中 User 的 private City[] cities;
4.2 集合类型
3.1 Enum类型 中 User 新增字段 private List<City> lifeCities;
新增 set/get 方法
重写toString
方法
@Override
public String toString() {
return "User{" +
"beanName='" + beanName + '\'' +
", id=" + id +
", name='" + name + '\'' +
", age=" + age +
", configFileReource=" + configFileReource +
", city=" + city +
", cities=" + Arrays.toString(cities) +
", lifeCities=" + lifeCities +
'}';
}
xml 中 新增 lifeCities
属性
<bean id="user" class="com.huajie.thinking.in.spring.ioc.overview.domain.User">
<property name="name" value="xwf"/>
<property name="age" value="18"/>
<property name="id" value="1"/>
<property name="configFileReource" value="classpath:/META-INF/user-config.properties"/>
<property name="city" value="WUHAN"/>
<property name="cities" value="WUHAN,BEIJING"/>
<property name="lifeCities" value="WUHAN,BEIJING"/>
</bean>
debug 调试,说明注入的是一个 Arraylist 集合类型
执行结果
实时查找---User{beanName='user', id=1, name='xwf', age=18, configFileReource=class path resource [META-INF/user-config.properties], city=WUHAN, cities=[WUHAN, BEIJING], lifeCities=[WUHAN, BEIJING]}
5.限定注入
使用注解 @Qualifier 限定
- 通过 Bean 名称限定
- 通过分组限定
基于注解 @Qualifier 扩展限定
- 自定义注解 - 如 Spring Cloud @LoadBalanced
5.1 通过 Bean 名称限定
/**
* {@link Qualifier} 注解依赖注入
*/
public class QualifierNameAnnotationDependencyInjectionDemo {
@Autowired
@Qualifier("user")
private User user;
@Autowired
private User user1;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(QualifierAnnotationDependencyInjectionDemo2.class);
applicationContext.refresh();
QualifierAnnotationDependencyInjectionDemo2 beanDemo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo2.class);
System.out.println("Qualifier名称:" + beanDemo.user);
System.out.println("普通:" + beanDemo.user1);
applicationContext.close();
}
@Bean
public User user() {
return createUser("xwf-bean");
}
@Bean
@Primary
public User superUser() {
return createUser("super-xwf-bean");
}
private static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
}
执行结果:
user用户对象初始化...
superUser用户对象初始化...
名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
superUser用户对象销毁...
user用户对象销毁...
普通的 user1
对象通过类型注入的方式,注入了 superUser
对象。
而加了 @Qualifier("user")
的 user
通过 user
名称的限定,注入了 user
对象
5.2 分组
/**
* {@link Qualifier} 注解依赖注入
*/
public class QualifierNameAnnotationDependencyInjectionDemo {
@Autowired
@Qualifier("user")
private User user;
@Autowired
@Qualifier
private User user_group;
@Autowired
private User user1;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(QualifierNameAnnotationDependencyInjectionDemo.class);
applicationContext.refresh();
QualifierNameAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(QualifierNameAnnotationDependencyInjectionDemo.class);
System.out.println("Qualifier名称:" + beanDemo.user);
System.out.println("普通:" + beanDemo.user1);
System.out.println("分组:" + beanDemo.user_group);
applicationContext.close();
}
@Bean
public User user() {
return createUser("xwf-bean");
}
@Bean
@Qualifier
public User userGroup() {
return createUser("xwf-bean-group");
}
@Bean
@Primary
public User superUser() {
return createUser("super-xwf-bean");
}
private static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
}
执行结果
Qualifier名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
普通:User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
标有 @Bean
、@Qualifier
的 userGroup()
方法被注入到标有 @Qualifier
的 user_group 字段上。
5.3 集合分组
我们再加入一个 User 的定义 和 Collection<User>
,都用 @Qualifier
进行标注
@Autowired
@Qualifier
private Collection<User> allUsers_group;
@Bean
@Qualifier
@Primary
public User userGroup2() {
return createUser("xwf-bean-group2");
}
5.4 元标注分组
自定义一个注解@UserGroup
,使用 @Qualifier
进行标注
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface UserGroup {
}
在原来类的基础上加入
@Autowired
@UserGroup
private Collection<User> allUsers_group_annotation;
@Bean
@UserGroup
public User userGroup3() {
return createUser("xwf-bean-group-annotation1");
}
@Bean
@UserGroup
public User userGroup4() {
return createUser("xwf-bean-group-annotation2");
}
5.5 完整示例
/**
* {@link Qualifier} 注解依赖注入
*/
public class QualifierAnnotationDependencyInjectionDemo {
@Autowired
@Qualifier("user")
private User user;
@Autowired
@Qualifier
private User user_group;
@Autowired
private Collection<User> allUsers;
@Autowired
@Qualifier
private Collection<User> allUsers_group;
@Autowired
@UserGroup
private Collection<User> allUsers_group_annotation;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(QualifierAnnotationDependencyInjectionDemo.class);
applicationContext.refresh();
QualifierAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(QualifierAnnotationDependencyInjectionDemo.class);
System.out.println("名称:" + beanDemo.user);
System.out.println("分组:" + beanDemo.user_group);
System.out.println("集合:size-" + beanDemo.allUsers.size() + "=" + beanDemo.allUsers);
System.out.println("集合-分组:size-" + beanDemo.allUsers_group.size() + "=" + beanDemo.allUsers_group);
System.out.println("集合-分组-注解:size-" + beanDemo.allUsers_group_annotation.size() + "=" + beanDemo.allUsers_group_annotation);
applicationContext.close();
}
@Bean
public User user() {
return createUser("xwf-bean");
}
@Bean
@Qualifier
public User userGroup() {
return createUser("xwf-bean-group");
}
@Bean
@Qualifier
@Primary
public User userGroup2() {
return createUser("xwf-bean-group2");
}
@Bean
@Primary
public User superUser() {
return createUser("super-xwf-bean");
}
@Bean
@UserGroup
public User userGroup3() {
return createUser("xwf-bean-group-annotation1");
}
@Bean
@UserGroup
public User userGroup4() {
return createUser("xwf-bean-group-annotation2");
}
private static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
}
执行结果
名称:User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
分组:User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
集合:size-6=[User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='superUser', id=null, name='super-xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
集合-分组:size-4=[User{beanName='userGroup', id=null, name='xwf-bean-group', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup2', id=null, name='xwf-bean-group2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
集合-分组-注解:size-2=[User{beanName='userGroup3', id=null, name='xwf-bean-group-annotation1', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='userGroup4', id=null, name='xwf-bean-group-annotation2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
由结果可知
- 通过名称
user
限定,注入了对应的user
对象 - 通过
@Qualifier
分组,注入了 标有@Qualifier
对象,如果有多个,那么使用@Primary
表示主要依赖 - 通过集合的方式
Collection<User> allUsers
,将所有的 User 类型注入,有 6 个对象 - 通过
@Qualifier
+集合的方式Collection<User> allUsers_group
,将所有标注@Qualifier
的User 类型注入,有 4 个对象,分别为两个@Qualifier
标注,两个@UserGroup
标注 - 通过
@UserGroup
+集合的方式Collection<User> allUsers_group_annotation
,将所有标注@UserGroup
的User 类型注入,有 2 个对象
6.延迟依赖注入
使用 API ObjectFactory 延迟注入
- 单一类型
- 集合类型
使用 API ObjectProvider 延迟注入(推荐)
- 单一类型
- 集合类型
6.1 ObjectProvider
ObjectProvider 安全性高,可以减少避免一些异常
/**
* 延迟注解依赖注入
*/
public class LazyAnnotationDependencyInjectionDemo {
@Autowired
private User user;
@Autowired
private ObjectProvider<User> providerUser;
@Autowired
private ObjectProvider<Collection<User>> providerUsers;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(LazyAnnotationDependencyInjectionDemo.class);
applicationContext.refresh();
LazyAnnotationDependencyInjectionDemo beanDemo = applicationContext.getBean(LazyAnnotationDependencyInjectionDemo.class);
System.out.println(beanDemo.user);
System.out.println(beanDemo.providerUser.getIfAvailable());
System.out.println(beanDemo.user == beanDemo.providerUser.getIfAvailable());
beanDemo.providerUser.forEach(System.out::println);
System.out.println("=======================");
System.out.println(beanDemo.providerUsers.getObject());
applicationContext.close();
}
@Bean
@Primary
public User user() {
return createUser("xwf-bean");
}
@Bean
public User user2() {
return createUser("xwf-bean2");
}
private static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
}
执行结果
User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
true
User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
User{beanName='user2', id=null, name='xwf-bean2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}
=======================
[User{beanName='user', id=null, name='xwf-bean', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}, User{beanName='user2', id=null, name='xwf-bean2', age=null, configFileReource=null, city=null, cities=null, lifeCities=null}]
7.依赖处理过程
基础知识
- 入口 - DefaultListableBeanFactory#resolveDependency
- 依赖描述符 - DependencyDescriptor
- 自动绑定候选对象处理器 - AutowireCandidateResolver
7.1 入口 DefaultListableBeanFactory#resolveDependency
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
...
7.2 DependencyDescriptor 依赖处理元信息
- org.springframework.beans.factory.config.DependencyDescriptor
public class DependencyDescriptor extends InjectionPoint implements Serializable {
private final Class<?> declaringClass;
@Nullable
private String methodName;
@Nullable
private Class<?>[] parameterTypes;
private int parameterIndex;
@Nullable
private String fieldName;
private final boolean required;
private final boolean eager;
private int nestingLevel = 1;
@Nullable
private Class<?> containingClass;
@Nullable
private transient volatile ResolvableType resolvableType;
@Nullable
private transient volatile TypeDescriptor typeDescriptor;
...
7.3 debug 调试注入过程
新建测试类 AnnotationDependencyInjectionResolutionDemo
/**
* 注解驱动依赖注入过程
*/
public class AnnotationDependencyInjectionResolutionDemo {
@Autowired
private User user;
// DependencyDescriptor 元信息
// 必须 (required = true)
// 实时注入 (eager = true)
// 通过类型 (User.class)
// 字段名称 (user)
// 是否首要 (Primary = true)
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
applicationContext.refresh();
AnnotationDependencyInjectionResolutionDemo beanDemo = applicationContext.getBean(AnnotationDependencyInjectionResolutionDemo.class);
System.out.println(beanDemo.user);
applicationContext.close();
}
@Bean
@Primary
public User user() {
return createUser("xwf-bean");
}
@Bean
public User user2() {
return createUser("xwf-bean2");
}
private static User createUser(String name) {
User user = new User();
user.setName(name);
return user;
}
}
在入口方法resolveDependency
中打上断点
元信息和我们猜测的一样
// DependencyDescriptor 元信息
// 必须 (required = true)
// 实时注入 (eager = true)
// 通过类型 (User.class)
// 字段名称 (user)
// 是否首要 (Primary = true)
继续向下,前面三个判断都不满足,分别是判断依赖类型
-
依赖类型是否等于 Optional
-
依赖类型是否等于 ObjectFactory
-
依赖类型是否等于 javaxInjectProviderClass
进入到 doResolveDependency 方法,我们关注重点 doResolveDependency 中的 1250 行 findAutowireCandidates 方法,方法名称翻译过来就是找到 Autowire 候选者,找到我们通过 @Bean
定义的两个 User(user 和 user2)
1262 行 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
因为有多个 User.class 对象,通过此方法找到主要依赖
并通过 matchingBeans.get(autowiredBeanName);
获取 User.Class 对象
接着 1287 行通过 instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
最终通过 beanFactory.getBean(beanName)
依赖查找的方式获取到这个 user 对象
public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)
throws BeansException {
return beanFactory.getBean(beanName);
}
最后返回 user 对象
8.@Autowired 注入原理
@Autowired 注入过程
- 元信息解析
- org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
- 包括合并 BeanDefinition
- 依赖查找
- 获取 Bean 的对象,在这个过程中触发依赖处理的过程 DefaultListableBeanFactory#resolveDependency
- 依赖注入(字段、方法)
- 通过反射设置字段属性
8.1 细节1
接着上面的源码调试过程,我们再看一些细节,重新打开调试,idea 左下角的堆栈信息就是本次方法的调用链。
我们点击第二个 inject ,就会进入到上一层的调用方法中 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
重新打上断点,可以看到在 640行 beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
获取到 user 对象,然后在 668 行 field.set(bean, value);
通过反射注入到字段属性中。
8.2 细节2
AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
这个方法也比较重要
合并 Bean 的定义,为什么要合并呢?如果当前的类继承另一个类
比如 SuperUser 继承 User,xml 定义中 parent="user"
<bean primary="true" id="superUser" class="com.huajie.thinking.in.spring.ioc.overview.domain.SuperUser" parent="user">
<property name="address" value="wuhan"/>
</bean>
那么,就需要先将 user
定义的相关属性合并到 superUser
中,其实每个 Bean 的定义都会走这个方法,如果没有继承,那么合并的就是自己本身。
9.JSR-330 @Inject 注入原理
@Inject 注入过程
- 如果 JSR-330 存在 ClassPath 中,复用 AutowiredAnnotationBeanPostProcessor 实现
AutowiredAnnotationBeanPostProcessor#AutowiredAnnotationBeanPostProcessor() 构造器
处理 Autowired
、Value
、Inject
三个注解
autowiredAnnotationTypes
是一个LinkedHashSet
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);
public AutowiredAnnotationBeanPostProcessor() {
this.autowiredAnnotationTypes.add(Autowired.class);
this.autowiredAnnotationTypes.add(Value.class);
try {
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) {
// JSR-330 API not available - simply skip.
}
}
在构建元数据的时候调用 findAutowiredAnnotation()
获取注解信息 ,遍历 autowiredAnnotationTypes 只要找到一个就返回,换言之,不论是三个注解中的哪一个都可以。
@Nullable
private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
MergedAnnotations annotations = MergedAnnotations.from(ao);
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
MergedAnnotation<?> annotation = annotations.get(type);
if (annotation.isPresent()) {
return annotation;
}
}
return null;
}
如果我们自定义一个注解 ,将注解加入到 autowiredAnnotationTypes
这个对象中,效果应该和 @Autowired
一样,在本文 11 自定义依赖注入注解 中有相关的示例。
10.Java 通用注入原理
CommonAnnotationBeanPostProcessor
这个类和上面讲的的 AutowiredAnnotationBeanPostProcessor
都是处理依赖注入注解相关的类。AutowiredAnnotationBeanPostProcessor
主要是处理 @Autowired
、Value
、Inject
这三个注解,而本类主要是处理通用型的,主要包括下面三个注解和两个生命周期回调:
- 注入注解
- javax.xml.ws.WebServiceRef
- javax.ejb.EJB
- javax.annotation.Resource
- 生命周期注解
- javax.annotation.PostConstruct
- javax.annotation.PreDestroy
源码分析
10.1 区别1 实现细节
CommonAnnotationBeanPostProcessor 实现了 InstantiationAwareBeanPostProcessor 接口,而 AutowiredAnnotationBeanPostProcessor 则是继承了 InstantiationAwareBeanPostProcessorAdapter,原因在于 CommonAnnotationBeanPostProcessor 是Spring 5 基于 Java 8 的实现,该接口中都是 default 的方法。而原来的需要一个 Adapter 的适配器来达到这个效果。
10.2 区别2 生命周期
两个类都实现了 MergedBeanDefinitionPostProcessor 来处理 BeanDefinition 的合并操作,但是
CommonAnnotationBeanPostProcessor 中的方法多了
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
postProcessMergedBeanDefinition() 多了生命周期(Lifecycle)的处理,主要指的是 PostConstruct(初始化) 和 PreDestroy(销毁) 这两个阶段。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}
10.3 区别3 顺序 Order
BeanPostProcessor 都具有一定的顺序,本次讨论的两个继承了 InitDestroyAnnotationBeanPostProcessor
而 InitDestroyAnnotationBeanPostProcessor 实现了 PriorityOrdered 接口,CommonAnnotationBeanPostProcessor 相关源码如下:
构造方法 setOrder(Ordered.LOWEST_PRECEDENCE - 3);
...
public CommonAnnotationBeanPostProcessor() {
setOrder(Ordered.LOWEST_PRECEDENCE - 3);
setInitAnnotationType(PostConstruct.class);
setDestroyAnnotationType(PreDestroy.class);
ignoreResourceType("javax.xml.ws.WebServiceContext");
}
...
public void setOrder(int order) {
this.order = order;
}
@Override
public int getOrder() {
return this.order;
}
AutowiredAnnotationBeanPostProcessor 中
private int order = Ordered.LOWEST_PRECEDENCE - 2;
由此可见,CommonAnnotationBeanPostProcessor 会优先执行。
11.自定义依赖注入注解
基于 AutowiredAnnotationBeanPostProcessor 实现
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InjectUser {
}
我们在之前的例子 AnnotationDependencyFieldInjectDemo 中加一个字段
private User inject_user;
并标注上我们自定义的注解 @InjectUser
通过 autowiredAnnotationBeanPostProcessor()
方法将我们自定义的注解加入到 autowiredAnnotationTypes
对象中
/**
* 基于注解实现字段注入示例
*/
public class AnnotationDependencyFieldInjectDemo {
@Autowired
private UserHolder userHolder;
@Resource
private UserHolder userHolder_resource;
@InjectUser
private User inject_user;
@Bean(name = "injectUserAnnotationBeanPostProcessor")
@Order(Ordered.LOWEST_PRECEDENCE - 3)
public static AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
AutowiredAnnotationBeanPostProcessor beanPostProcessor = new AutowiredAnnotationBeanPostProcessor();
beanPostProcessor.setAutowiredAnnotationType(InjectUser.class);
return beanPostProcessor;
}
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AnnotationDependencyFieldInjectDemo.class);
applicationContext.refresh();
// @Autowired 不支持 static 修饰的属性,所以还是采用依赖查找的方式
AnnotationDependencyFieldInjectDemo demo = applicationContext.getBean(AnnotationDependencyFieldInjectDemo.class);
System.out.println("userHolder:" + demo.userHolder);
System.out.println("userHolder_resource:" + demo.userHolder_resource);
System.out.println("userHolder 和 userHolder_resource 比较:" + (demo.userHolder == demo.userHolder_resource));
System.out.println("inject_user:" + demo.inject_user);
applicationContext.close();
}
@Bean
public User user() {
User user = new User();
user.setName("xwf-bean-field");
user.setAge(20);
return user;
}
@Bean
public UserHolder userHolder(User user) {
return new UserHolder(user);
}
}
执行结果:
userHolder:UserHolder{user=User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}}
userHolder_resource:UserHolder{user=User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}}
userHolder 和 userHolder_resource 比较:true
inject_user:User{beanName='user', id=null, name='xwf-bean-field', age=20, configFileReource=null, city=null, cities=null, lifeCities=null}
自定义实现(比较复杂,就不演示了)
- 生命周期处理
- InstantiationAwareBeanPostProcessor
- MergedBeanDefinitionPostProcessor
- 元数据
- InjectElement
- InjectionMetadata
12.面试题
1.有多少种依赖注入的方式?
- 构造器注入
- Setter 注入
- 字段注入
- 方法注入
- @Bean
- 接口回调注入
- Aware
这里还可以结合本文 2. 依赖注入类型选择 来进行回答。
2.你偏好构造器注入还是Setter注入?
两种依赖注入的方式都可以使用,如果是必须依赖的话,那么推荐使用构造器注入,Setter 注入用于可选依赖。
如果参数比较少的情况下,还是使用构造器注入,这也是 Spring 官方推荐的注入方式。
3.Spring 依赖注入的来源有哪些?
答案将在下一章中详细讨论。
13.参考
- 极客时间-小马哥《小马哥讲Spring核心编程思想》