前言
本系列全部基於 Spring 5.2.2.BUILD-SNAPSHOT
版本。因為 Spring 整個體系太過於龐大,所以只會進行關鍵部分的原始碼解析。
什麼是公共註解?公共註解就是常見的Java註解,特別是JSR-250中的註解。例如:@Resource
、@PostConstructor
、@PreDestroy
等等,本文也就主要分析這三個註解在 Spring 中是如何處理的。
正文
@Resource 註解的處理
對 @Resource
註解的處理類是 CommonAnnotationBeanPostProcessor
,它通過實現 InstantiationAwareBeanPostProcessor
介面,重寫 postProcessProperties()
方法實現對標註了 @Resource
註解的欄位或方法的自動注入。
InstantiationAwareBeanPostProcessor
介面的詳細資訊可以檢視Spring IoC bean 的建立。
關於 CommonAnnotationBeanPostProcessor
這個後置處理器是怎麼加入到 beanFactory
中的,我們在 Spring IoC component-scan 節點詳解 一文中介紹過主要是通過 AnnotationConfigUtils#registerAnnotationConfigProcessors()
實現的。
public static Set<BeanDefinitionHolder> 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
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 註解詳解一文中該方法的詳解。
bean 屬性後置處理
CommonAnnotationBeanPostProcessor#postProcessProperties
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<InjectedElement> checkedElements = this.checkedElements;
// 如果checkedElements不為空就使用checkedElements,否則使用injectedElements
Collection<InjectedElement> 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
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
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));
}
上面的 lazyLookup
就是是否在屬性或方法上標註了 @Lazy
註解,該註解先暫不討論,所以呼叫後面的 getResource()
方法。
CommonAnnotationBeanPostProcessor#getResource
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<String> 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 的建立一文中分析過,按名稱查詢 bean
的 resolveBeanByName()
方法實際就是呼叫 getBean()
通過名稱和型別去獲取 bean
。
從上面的程式碼也可以看出一般情況下
@Resource
註解是按名稱注入;而@Autowired
註解時按型別注入,具體可以檢視Spring IoC @Autowired 註解詳解。
@PostConstruct、@PreDestroy 註解的處理
處理 @PostConstruct
和 @PreDestroy
註解的處理類是 InitDestroyAnnotationBeanPostProcessor
,CommonAnnotationBeanPostProcessor
繼承與該類,相當於註冊 CommonAnnotationBeanPostProcessor
時也註冊了 InitDestroyAnnotationBeanPostProcessor
。
BeanDefinition 合併後的後置處理
InitDestroyAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()
方法是通過 CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition()
方法呼叫的,如下:
CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition
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
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
這個類:
private class LifecycleMetadata {
// 目標類,也就是當前正在初始化的bean
private final Class<?> targetClass;
// 存放標註了@PostConstruct的方法
private final Collection<LifecycleElement> initMethods;
// 存放標註了@PreDestroy的方法
private final Collection<LifecycleElement> destroyMethods;
// 省略其它程式碼...
}
bean 的初始化前回撥
InitDestroyAnnotationBeanPostProcessor#postProcessBeforeInitialization
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 的初始化一文。
bean 銷燬前回撥
我們先了解一下 DestructionAwareBeanPostProcessor
,它繼承自 BeanPostProcessor
,如下:
public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {
/**
* Bean 銷燬前階段回撥
*/
void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;
/**
* bean例項是否要由此方法銷燬
*/
default boolean requiresDestruction(Object bean) {
return true;
}
}
InitDestroyAnnotationBeanPostProcessor#postProcessBeforeDestruction
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。