文章目錄
夯實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核心編程思想》