Spring IoC 公共註解詳解
阿新 • • 發佈:2020-07-09
# 前言
本系列全部基於 `Spring 5.2.2.BUILD-SNAPSHOT` 版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的原始碼解析。
什麼是公共註解?公共註解就是常見的Java註解,特別是JSR-250中的註解。例如:`@Resource`、`@PostConstructor`、`@PreDestroy` 等等,本文也就主要分析這三個註解在 Spring 中是如何處理的。
# 正文
## @Resource 註解的處理
對 `@Resource` 註解的處理類是 `CommonAnnotationBeanPostProcessor`,它通過實現 `InstantiationAwareBeanPostProcessor` 介面,重寫 `postProcessProperties()` 方法實現對標註了 `@Resource` 註解的欄位或方法的**自動注入**。
> `InstantiationAwareBeanPostProcessor` 介面的詳細資訊可以檢視[Spring IoC bean 的建立](https://www.cnblogs.com/leisurexi/p/13196998.html)。
關於 `CommonAnnotationBeanPostProcessor` 這個後置處理器是怎麼加入到 `beanFactory` 中的,我們在 [Spring IoC component-scan 節點詳解](https://www.cnblogs.com/leisurexi/p/13088395.html) 一文中介紹過主要是通過 `AnnotationConfigUtils#registerAnnotationConfigProcessors()` 實現的。
```java
public static Set registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {
// 省略其他程式碼...
// 註冊用於處理@Resource、@PostConstructor、@PostDestroy註解的後置處理器
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// 省略其他程式碼...
}
```
### BeanDefinition 合併後的後置處理
#### CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
```java
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 尋找需要注入的欄位或方法,並封裝成 InjectionMetadata
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 檢查元資料中的註解資訊
metadata.checkConfigMembers(beanDefinition);
}
```
上面程式碼中的 `findAutowiringMetadata()` 方法就是利用反射遍歷類的所有欄位和方法,找到標註了 `@Resource` 註解的,並快取進 `injectionMetadataCache` 中。
> 注意:靜態欄位和靜態方法會過濾掉。
`findAutowiringMetadata() ` 方法基本和 `AutowiredAnnotationBeanPostProcessor` 中的一致,只是處理的註解不同而已,可以查檢視[Spring IoC @Autowired 註解詳解](https://www.cnblogs.com/leisurexi/p/13263542.html)一文中該方法的詳解。
### bean 屬性後置處理
#### CommonAnnotationBeanPostProcessor#postProcessProperties
```java
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 從injectionMetadataCache快取中獲取需要注入的欄位和方法
InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
try {
// 進行注入
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
}
return pvs;
}
// InjectMetadata.java
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 獲取檢查後的元素
Collection checkedElements = this.checkedElements;
// 如果checkedElements不為空就使用checkedElements,否則使用injectedElements
Collection elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {
// 遍歷elementsToIterate
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
// 進行元素注入,見下文詳解
element.inject(target, beanName, pvs);
}
}
}
```
#### 元素注入
##### InjectionMetadata#inject
```java
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
throws Throwable {
// 如果元素是欄位
if (this.isField) {
// 強轉成Field型別
Field field = (Field) this.member;
// 並設定為可訪問
ReflectionUtils.makeAccessible(field);
// 然後使用反射設定值
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
// 檢查是否跳過
if (checkPropertySkipping(pvs)) {
return;
}
try {
// 強轉成Method型別
Method method = (Method) this.member;
// 並設定為可訪問
ReflectionUtils.makeAccessible(method);
// 使用反射呼叫方法
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
```
#### 獲取注入資源
##### ResourceElement#getResourceToInject
```java
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));
}
```
上面的 `lazyLookup` 就是是否在屬性或方法上標註了 `@Lazy` 註解,該註解先暫不討論,所以呼叫後面的 `getResource()` 方法。
##### CommonAnnotationBeanPostProcessor#getResource
```java
protected Object getResource(LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {
// 省略其它程式碼...
return autowireResource(this.resourceFactory, element, requestingBeanName);
}
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {
Object resource;
Set autowiredBeanNames;
String name = element.name;
if (factory instanceof AutowireCapableBeanFactory) {
AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
DependencyDescriptor descriptor = element.getDependencyDescriptor();
// 型別匹配(預設為true) && @Resource註解name屬性不為空 && 當前beanFactory不包含名稱為name的bean
if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
// 按型別查詢bean
resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {
// 按名稱查詢bean
resource = beanFactory.resolveBeanByName(name, descriptor);
autowiredBeanNames = Collections.singleton(name);
}
}
// 省略其它程式碼...
return resource;
}
```
上面 `autowireResource()` 方法中按型別查詢的 `resolveDependency()` 方法在[Spring IoC bean 的建立](https://www.cnblogs.com/leisurexi/p/13196998.html)一文中分析過,按名稱查詢 `bean` 的 `resolveBeanByName()` 方法實際就是呼叫 `getBean()` 通過名稱和型別去獲取 `bean`。
> 從上面的程式碼也可以看出一般情況下 `@Resource` 註解是按名稱注入;而 `@Autowired` 註解時按型別注入,具體可以檢視[Spring IoC @Autowired 註解詳解](https://www.cnblogs.com/leisurexi/p/13263542.html)。
## @PostConstruct、@PreDestroy 註解的處理
處理 `@PostConstruct` 和 `@PreDestroy` 註解的處理類是 `InitDestroyAnnotationBeanPostProcessor`,`CommonAnnotationBeanPostProcessor` 繼承與該類,相當於註冊 `CommonAnnotationBeanPostProcessor` 時也註冊了 `InitDestroyAnnotationBeanPostProcessor`。
### BeanDefinition 合併後的後置處理
`InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()` 方法是通過 `CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()` 方法呼叫的,如下:
#### CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
```java
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
// 呼叫InitDestroyAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()方法
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 尋找需要注入的欄位或方法,並封裝成 InjectionMetadata
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
// 檢查元資料中的註解資訊
metadata.checkConfigMembers(beanDefinition);
}
```
#### InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
```java
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class> beanType, String beanName) {
// 尋找需要標註了@PostConstruct和@PreDestroy註解的方法,並封裝進LifecycleMetadata
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
// 檢查元資料中的註解資訊
metadata.checkConfigMembers(beanDefinition);
}
```
上面程式碼中的 `findLifecycleMetadata()` 方法,就是遍歷當前初始化的 `bean` 包括其父類中所有標註了 `@PostConstruct` 和 `@PreDestroy` 註解的方法,並封裝成 `LifecycleMetadata` (該類是 `InitDestroyAnnotationBeanPostProcessor` 中一個內部類),並放入 `lifecycleMetadataCache` 快取中。
這裡我們簡單看一下 `LifecycleMetadata` 這個類:
```java
private class LifecycleMetadata {
// 目標類,也就是當前正在初始化的bean
private final Class> targetClass;
// 存放標註了@PostConstruct的方法
private final Collection initMethods;
// 存放標註了@PreDestroy的方法
private final Collection destroyMethods;
// 省略其它程式碼...
}
```
### bean 的初始化前回調
#### InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
```java
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 從lifecycleMetadataCache快取中獲取LifecycleMetadata
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 反射呼叫所有初始化方法
metadata.invokeInitMethods(bean, beanName);
}
// 省略異常處理...
return bean;
}
```
看到這裡我們知道為什麼標註了 `@PostConstruct` 註解的方法比 `InitializingBean#afterPropertiesSet()` 方法和自定義初始化方法先呼叫了;因為其在 `bean` 的初始化前回調就已經呼叫了,而剩下的兩個是在初始化方法中呼叫的,詳情可以檢視[Spring IoC bean 的初始化](https://www.cnblogs.com/leisurexi/p/13238055.html)一文。
### bean 銷燬前回調
我們先了解一下 `DestructionAwareBeanPostProcessor`,它繼承自 `BeanPostProcessor`,如下:
```java
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
/**
* Bean 銷燬前階段回撥
*/
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
/**
* bean例項是否要由此方法銷燬
*/
default boolean requiresDestruction(Object bean) {
return true;
}
}
```
#### InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction
```java
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
// 從lifecycleMetadataCache快取中獲取LifecycleMetadata
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
// 反射呼叫所有銷燬方法
metadata.invokeDestroyMethods(bean, beanName);
}
// 省略異常處理...
}
```
和上面的 `@PostConstruct` 註解一樣,`@PreDestroy` 註解標註的方法也比 `DisposableBean#destroy()` 方法和自定義銷燬方法先呼叫。
# 總結
本文主要介紹了 `@Resource`、`@PostConstruct`、`@PreDestroy` 註解 Spring 是如何對其處理的,可以看出 Spring 的註解驅動大多依靠 實現 `BeanPostProcessor` 及其子類中的 `bean` 生命週期各個階段的回撥方法來進行實現的。
> 最後,我模仿 Spring 寫了一個精簡版,程式碼會持續更新。地址:[https://github.com/leisurexi/tiny-spring](https://github.com/leisurexi/tiny-sp