1. 程式人生 > >Spring IoC 公共註解詳解

Spring IoC 公共註解詳解

# 前言 本系列全部基於 `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