起因
羣裏有朋友拋出了個問題,問爲什麼Spring Cache註解未生效,示例代碼如下:
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class DemoApplicationTests {
@Test
public void contextLoads() {
testGetFromDB(1);
testGetFromDB(1);
testGetFromDB(1);
}
@Cacheable("foo")
public String testGetFromDB(Integer i) {
log.info("testGetFromDB...");
return "retVal" + i;
}
}
我說,你把@Cacheable
註解的方法搬到別的類,然後再試試,結果,好了
大家如果有過類似經驗,大概能一眼看出問題所在:Spring AOP方法內部調用,切面邏輯是不生效的(動態代理)。解決方案有兩個,一是AopContext#currentProxy
,二是在當前類中注入自己,再用該對象進行方法內部調用
但實際上,這裏隱藏了一個問題,即便通過上述的兩種方式,在本案例中,@Cacheable
仍然不會生效。第一種方式會因獲取到的proxy對象爲空而拋出IllegalStateException
異常,第二種方式會因獲取不到DemoApplicationTests
Bean而拋出NoSuchBeanDefinitionException
異常
public static Object currentProxy() throws IllegalStateException {
Object proxy = currentProxy.get();
if (proxy == null) {
throw new IllegalStateException(
"Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.");
}
return proxy;
}
NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.demo.DemoApplicationTests' available
這就很詭異了,爲啥會獲取不到?難道IOC容器裏沒有DemoApplicationTests
對象?可是,如果沒有這個對象,那平時寫單測的時候,那些@Resouce
、@Autowired
、@Value
註解(Spring稱爲:annotation-driven injection)又是如何生效的,Bean是如何注入的呢
實際上,IOC容器裏還真沒有DemoApplicationTests
對象,也就是說,Spring在IOC容器裏壓根找不到該Bean,也就不難理解方式一、二都拋了異常。
在一般的認知中,如果某個Bean想使用Spring的功能,需要讓Bean註冊到Spring IOC容器中,被Spring管理。那麼問題就來了,既然Spring 並沒有管理DemoApplicationTests
Bean,那annotation-driven injection是如何生效的呢?
解決方案
AutowireCapableBeanFactory
- BeanFactory的直接子接口,擁有自動裝配的能力
- 一般不會在普通應用中直接使用
AutowireCapableBeanFactory
,因此在設計上ApplicationContext
們並沒有直接實現該接口(畫外音:Spring的設計者們不希望用戶在應用代碼中直接使用AutowireCapableBeanFactory
)。儘管如此,卻提供了該問該接口的能力:通過ApplicationContext#getAutowireCapableBeanFactory
方法可拿到其實例對象 - 它真正的作用在於整合其它框架:能讓Spring管理的Bean去裝配和填充那些不被Spring託管的Bean(wire and populate existing bean instances that Spring does not control the lifecycle of)[重要]
原理
源碼基於Spring 5.1.11.RELEASE
當在Junit測試類使用spring-test\spring-boot-test註解(如:@SpringBootTest
),並啓動測試方法時,Junit框架會準備相關的測試資源,並且加載Spring環境,之後對測試類實例使用Spring進行裝配,以保證依賴的服務處於可用狀態。測試方法啓動過程會經歷如下方法:
// org.springframework.test.context.support.DependencyInjectionTestExecutionListener
protected void injectDependencies(TestContext testContext) throws Exception {
Object bean = testContext.getTestInstance(); // 測試類實例
Class<?> clazz = testContext.getTestClass(); // 測試類class(DemoApplicationTests)
AutowireCapableBeanFactory beanFactory = testContext.getApplicationContext().getAutowireCapableBeanFactory();
// 1. 使用AutowireCapableBeanFactory去裝配不受Spring管理的bean
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
// 2. 進行初始化
beanFactory.initializeBean(bean, clazz.getName() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX);
testContext.removeAttribute(REINJECT_DEPENDENCIES_ATTRIBUTE);
}
從上述源碼中可以看到,Bean(DemoApplicationTests)是從testContext中獲取,而並沒有被Spring管理,然後藉助AutowireCapableBeanFactory
的能力對Bean進行裝配與初始化,讓Spring爲非Spring託管的Bean賦能
其中,對於@Resouce、@Autowired
注入的能力,是通過beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
來實現的,代碼如下:
public void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException {
if (autowireMode == AUTOWIRE_CONSTRUCTOR) {
throw new IllegalArgumentException("AUTOWIRE_CONSTRUCTOR not supported for existing bean instance");
}
// Use non-singleton bean definition, to avoid registering bean as dependent bean.
RootBeanDefinition bd =
new RootBeanDefinition(ClassUtils.getUserClass(existingBean), autowireMode, dependencyCheck);
bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
BeanWrapper bw = new BeanWrapperImpl(existingBean);
initBeanWrapper(bw);
// 填充屬性
populateBean(bd.getBeanClass().getName(), bd, bw);
}
接着看populateBean
方法,該方法作用是爲給定的bean填充屬性,相信大家已經很熟悉這段代碼了,篇幅原因,把非關鍵代碼省略
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ...(省略)
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// @Resouce、@Autowired 註解起作用的地方
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
// ...(省略)
}
@Resouce
、@Autowired
註解之所以能生效,是因爲最終執行了PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
這一行代碼。有兩個InstantiationAwareBeanPostProcessor
分別是CommonAnnotationBeanPostProcessor
、AutowiredAnnotationBeanPostProcessor
,分別用於處理@Resouce
、@Autowired
,達到屬性填充的目的
接下來看看AutowireCapableBeanFactory
接口,一共有6個屬性,15個方法
public interface AutowireCapableBeanFactory extends BeanFactory {
// ---------------------- 屬性 ----------------------
// 裝配模式
int AUTOWIRE_NO = 0;
int AUTOWIRE_BY_NAME = 1;
int AUTOWIRE_BY_TYPE = 2;
int AUTOWIRE_CONSTRUCTOR = 3;
// @deprecated as of Spring 3.0
int AUTOWIRE_AUTODETECT = 4;
String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";
// ---------------------- 方法 ----------------------
<T> T createBean(Class<T> beanClass) throws BeansException;
void autowireBean(Object existingBean) throws BeansException;
Object configureBean(Object existingBean, String beanName) throws BeansException;
Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException;
void destroyBean(Object existingBean);
<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
}
5個AUTOWIRE_*指的是裝配模式,接口方法中的autowireMode參數取值就從這5個裏邊選擇
- int AUTOWIRE_NO = 0;不裝配
- int AUTOWIRE_BY_NAME = 1;根據名稱裝配
- int AUTOWIRE_BY_TYPE = 2;根據類型裝配
- int AUTOWIRE_CONSTRUCTOR = 3;根據構造器裝配
- int AUTOWIRE_AUTODETECT = 4;從Spring 3.0就過期了,不作介紹
還有1個屬性String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";
,該屬性是一種約定俗成的用法:以類全限定名+.ORIGINAL 作爲Bean Name,用於告訴Spring,在初始化的時候,需要返回原始給定實例,而別返回代理對象
上面的方法看似挺多,經過分類之後,也能比較好理解與記憶。其實從方法的命名上也能看出來各自的作用,這也是Spring框架優秀讓人着迷的地方之一:方法命名在很多時候能讓人見名知義,足夠抽象而又不失準確的概括。按照接口提供能力的粒度,大體上分爲三類:
- 粗粒度創建、裝配Bean的方法
<T> T createBean(Class<T> beanClass) throws BeansException;
void autowireBean(Object existingBean) throws BeansException;
Object configureBean(Object existingBean, String beanName) throws BeansException;
- 細粒度控制Bean生命週期的方法(創建、裝配Bean的過程涉及Bean的生命週期)。細粒度體現在兩個方面,一是提供了可選的裝配模式(autowireMode),二是把裝配Bean的過程細化爲 屬性填充(populateBean) 與 初始化(initializeBean) 兩個階段。此外,還提供了BeanPostProcessors的前後置處理邏輯回調,以及銷燬Bean的邏輯回調。
Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck) throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException;
void destroyBean(Object existingBean);
- 解析注入點的代理方法,這些方法是輔助類方法,一般不需要直接使用
<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) throws BeansException;
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException;
上面是官方的分法,下面將按照接口的功能進行劃分
- 創建Bean
- createBean(Class beanClass): 用給定的class創建一個Bean實例,完整經歷一個Bean創建過程的生命週期節點回調,但不執行傳統的autowiring
- 提供autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck)所有能力
- 提供initializeBean(Object existingBean, String beanName)所有能力
- createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck): 與上邊類似,主要區別是可以指定
autowireMode
,即可能會執行傳統的autowiring
- createBean(Class beanClass): 用給定的class創建一個Bean實例,完整經歷一個Bean創建過程的生命週期節點回調,但不執行傳統的autowiring
- 屬性裝配
- autowireBean(Object existingBean): 主要用於(再次)填充指定Bean被註解的元素或方法(如@Resource @Autowired),不執行傳統的autowiring
- 執行InstantiationAwareBeanPostProcessor的後置處理邏輯,因爲這是在
populateBean()
中執行的 - 不執行傳統的autowiring
- 不執行initializeBean方法,因此也就不執行標準的BeanPostProcessor
- 執行InstantiationAwareBeanPostProcessor的後置處理邏輯,因爲這是在
- autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck): 與上邊類似,主要區別是可以指定
autowireMode
,即可能會執行傳統的autowiring - autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck): 與上邊類似,主要區別是該方法首個入參是Class,框架會幫我們實例化,而上邊是已實例化的對象
- autowireBean(Object existingBean): 主要用於(再次)填充指定Bean被註解的元素或方法(如@Resource @Autowired),不執行傳統的autowiring
- 初始化
- initializeBean(Object existingBean, String beanName): 初始化給定的Bean
- 執行invokeAwareMethods方法(xxxAware,如BeanNameAware)
- 執行BeanPostProcessor前置處理邏輯
- 執行invokeInitMethods進行初始化,如InitializingBeanorg#afterPropertiesSet等
- 執行BeanPostProcessor後置處理邏輯
- initializeBean(Object existingBean, String beanName): 初始化給定的Bean
- 配置Bean
- configureBean(Object existingBean, String beanName): 作爲
initializeBean
的超集存在,功能跟createBean(Class<T> beanClass)
類似,但要求beanName
對應的BeanDefinition
存在,否則會拋出NoSuchBeanDefinitionException
異常,使用場景很少
- configureBean(Object existingBean, String beanName): 作爲
- 銷燬Bean
- destroyBean(Object existingBean): 銷燬Bean前,執行
DestructionAwareBeanPostProcessor
與DisposableBean
等的銷燬邏輯,用於資源回收
- destroyBean(Object existingBean): 銷燬Bean前,執行
- 對Bean初始化前後應用邏輯處理器
- applyBeanPostProcessorsBeforeInitialization: 執行BeanPostProcessor前置處理邏輯
- applyBeanPostProcessorsAfterInitialization: 執行BeanPostProcessor後置處理邏輯
- applyBeanPropertyValues: 真正執行Bean屬性值的填充
- 解析方法,輔助類方法
- resolveNamedBean(Class requiredType): 根據傳入的類型,從Spring容器(包括父子容器)中查找出指定類型下唯一的Bean,並將
beanName
與beanInstance
包裝成NamedBeanHolder對象返回。如果該類型下有不止一個對象(非唯一),返回null。該方法其實是BeanFactory#getBean(java.lang.Class<T>)
方法的變種,二者底層實現是一樣的 - resolveBeanByName(String name, DependencyDescriptor descriptor): 根據name跟descriptor解析出一個Bean實例,該方法是
BeanFactory#getBean(java.lang.String, java.lang.Class<T>)
方法的變種 - resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName): 直接調用下邊的方法
- resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter): 解析Bean依賴,非常重要的方法
- resolveNamedBean(Class requiredType): 根據傳入的類型,從Spring容器(包括父子容器)中查找出指定類型下唯一的Bean,並將
AbstractAutowireCapableBeanFactory
是AutowireCapableBeanFactory
的一個抽象實現類,該類的源碼如果不往更底層看,其實還是比較簡單(簡潔)的,大家以前學習Spring Bean生命週期的時候應該見到過,因此也就不貼源碼了,感興趣的同學可以自行翻看一下
除了Junit,還有Quartz定時任務框架,與Spring集成的時候,也會用上AbstractAutowireCapableBeanFactory
。原因是定時任務Job一般需要藉助Spring 管理的Service來進行業務邏輯的處理,但Job本身並沒有交給Spring管理,而是通過反射生成實例,因此想要借用@Resouce
等註解,就得讓AbstractAutowireCapableBeanFactory
爲Job Bean 做自動裝配。具體分析可參看此處
總結
Spring 提供了一種機制,能夠爲第三方框架賦能,讓Spring管理的Bean去裝配和填充那些不被Spring託管的Bean,這種機制叫AutowireCapableBeanFactory
。目前瞭解到的有兩款著名的開源框架Junit
與Quartz
借用了這種機制爲自己賦能,達到更好地與Spring契合協作的目的,也從他們的整合姿勢中學習到,當我們需要設計一個框架,而且需要擁抱Spring生態,爲框架使用者提供更便利的無縫整合,需以微內核 + 插件機制思想,由內核層想辦法拿到AutowireCapableBeanFactory
,並在構造插件的時候,藉助AutowireCapableBeanFactory
去爲插件賦能,做到無限擴展的可能性