Spring 冷知識:一個提前 AOP 的機會
今天再來聊一個 Spring 中的冷門知識:Bean 的處理不走正常流程,而是提前進行 AOP。
1. Bean 創(chuàng)建流程
在 Bean 創(chuàng)建的過程中,會先給 BeanPostProcessor 一個返回代理對象的機會:
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
//省略。。。
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
//省略。。。
}
小伙伴們看,這里的 resolveBeforeInstantiation 方法就是給 BeanPostProcessor 一個返回代理對象的機會,在這個方法中,最終就會觸發(fā)到 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法,而在 postProcessBeforeInstantiation 方法中,會先判斷當前 bean 是否是 AOP 相關(guān)類等:
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
這里主要來說說 getCustomTargetSource 中的邏輯。
先來說什么情況下會走到 getCustomTargetSource 方法:當前 Bean 不是代理對象,也不是 AOP 相關(guān)的類,就是一個普普通通的常規(guī)類,那么就會走到 getCustomTargetSource 方法這里來,這里失去查找到一個 TargetSource 對象,然后根據(jù)該對象創(chuàng)建當前 bean 的代理對象并返回,如果返回了代理對象,那么后續(xù)的 bean 創(chuàng)建流程就不執(zhí)行了。
我們來看下這個方法的源碼:
@Nullable
protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
// We can't create fancy target sources for directly registered singletons.
if (this.customTargetSourceCreators != null &&
this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
TargetSource ts = tsc.getTargetSource(beanClass, beanName);
if (ts != null) {
return ts;
}
}
}
// No custom TargetSource found.
return null;
}
可以看到,這里就是當前類 AbstractAutoProxyCreator 中有一個 customTargetSourceCreators 變量,現(xiàn)在就是遍歷該變量,通過這個集合中保存的 TargetSourceCreator 來創(chuàng)建 TargetSource 對象。
TargetSourceCreator 是一個接口,這個接口只有一個抽象類 AbstractBeanFactoryBasedTargetSourceCreator,我們來看下 AbstractBeanFactoryBasedTargetSourceCreator 中的 getTargetSource 方法是怎么執(zhí)行的:
@Override
@Nullable
public final TargetSource getTargetSource(Class<?> beanClass, String beanName) {
AbstractBeanFactoryBasedTargetSource targetSource =
createBeanFactoryBasedTargetSource(beanClass, beanName);
if (targetSource == null) {
return null;
}
DefaultListableBeanFactory internalBeanFactory = getInternalBeanFactoryForBean(beanName);
// We need to override just this bean definition, as it may reference other beans
// and we're happy to take the parent's definition for those.
// Always use prototype scope if demanded.
BeanDefinition bd = getConfigurableBeanFactory().getMergedBeanDefinition(beanName);
GenericBeanDefinition bdCopy = new GenericBeanDefinition(bd);
if (isPrototypeBased()) {
bdCopy.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
internalBeanFactory.registerBeanDefinition(beanName, bdCopy);
// Complete configuring the PrototypeTargetSource.
targetSource.setTargetBeanName(beanName);
targetSource.setBeanFactory(internalBeanFactory);
return targetSource;
}
首先,TargetSource 對象是通過 createBeanFactoryBasedTargetSource 方法來創(chuàng)建的,這個方法是一個抽象方法,將來在子類中被實現(xiàn)。
接下來會調(diào)用 getInternalBeanFactoryForBean 方法創(chuàng)建一個新的內(nèi)部容器 internalBeanFactory,本質(zhì)上這個 internalBeanFactory 其實是一個子容器,現(xiàn)有的容器將作為這個子容器的父容器。
接下來就是獲取到當前 beanName 所對應(yīng)的 BeanDefinition,然后進行屬性配置,并注冊到內(nèi)部容器中,最后返回 targetSource 對象。
我們來看下這里的 getInternalBeanFactoryForBean 方法:
protected DefaultListableBeanFactory getInternalBeanFactoryForBean(String beanName) {
synchronized (this.internalBeanFactories) {
return this.internalBeanFactories.computeIfAbsent(beanName,
name -> buildInternalBeanFactory(getConfigurableBeanFactory()));
}
}
protected DefaultListableBeanFactory buildInternalBeanFactory(ConfigurableBeanFactory containingFactory) {
// Set parent so that references (up container hierarchies) are correctly resolved.
DefaultListableBeanFactory internalBeanFactory = new DefaultListableBeanFactory(containingFactory);
// Required so that all BeanPostProcessors, Scopes, etc become available.
internalBeanFactory.copyConfigurationFrom(containingFactory);
// Filter out BeanPostProcessors that are part of the AOP infrastructure,
// since those are only meant to apply to beans defined in the original factory.
internalBeanFactory.getBeanPostProcessors().removeIf(beanPostProcessor ->
beanPostProcessor instanceof AopInfrastructureBean);
return internalBeanFactory;
}
這個其實就是正常的容器創(chuàng)建,倒也沒啥好說的,但是有幾個需要注意的點:
- 在調(diào)用 buildInternalBeanFactory 方法構(gòu)建容器的時候,會先調(diào)用 getConfigurableBeanFactory 方法獲取到當前容器作為父容器,如果當前容器不存在,那么就會拋出異常。這就意味著,當我們自己提供 TargetSourceCreator 實例的時候,一定要指定一個容器。
- 在創(chuàng)建了內(nèi)部容器之后,會從內(nèi)部容器中移除所有 AopInfrastructureBean 類型的 BeanPostProcessor,也就是內(nèi)部容器將來創(chuàng)建出來的 bean,不再走 AopInfrastructureBean 類型后置處理器,因為這種類型的后置處理器主要是用來處理 AOP 的,現(xiàn)在,AOP 代理當場就生成了,就不再需要這些后置處理器了。
好了,這就是大致的 AOP 提前生成原理,接下來松哥寫一個案例我們一起來看下。
2. 實踐
首先,我們先來自定義一個 TargetSource:
public class UserServiceTargetSource extends AbstractBeanFactoryBasedTargetSource {
@Override
public Object getTarget() throws Exception {
return getBeanFactory().getBean(getTargetBeanName());
}
@Override
public boolean isStatic() {
return true;
}
}
關(guān)于 TargetSource 本身,松哥在之前的 Spring 源碼視頻中已經(jīng)和大家介紹過很多了,這里我就不再啰嗦了。
接下來自定義 TargetSourceCreator:
public class CustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
@Override
protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
if (beanClass.isAssignableFrom(UserService.class)) {
return new UserServiceTargetSource();
}
}
return null;
}
}
如果要創(chuàng)建的 bean 是 UserService 的話,那么就給返回一個 UserServiceTargetSource 對象。
最后,也是最關(guān)鍵的一步,根據(jù)前面的分析,TargetSourceCreator 是存在于 AnnotationAwareAspectJAutoProxyCreator 這樣一個 InstantiationAwareBeanPostProcessor 類型的后置處理器中的,因此,我們要想辦法把自定義的 TargetSourceCreator 設(shè)置給 AnnotationAwareAspectJAutoProxyCreator,如下:
@Component
public class SetCustomTargetSourceCreator implements BeanPostProcessor, PriorityOrdered, BeanFactoryAware {
private BeanFactory beanFactory;
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator)bean;
CustomTargetSourceCreator customTargetSourceCreator = new CustomTargetSourceCreator();
customTargetSourceCreator.setBeanFactory(beanFactory);
annotationAwareAspectJAutoProxyCreator.setCustomTargetSourceCreators(customTargetSourceCreator);
}
return bean;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
AnnotationAwareAspectJAutoProxyCreator 本身就是一個 BeanPostProcessor,我們現(xiàn)在要做的就是修改這個 BeanPostProcessor,BeanPostProcessor 是在 Spring 容器啟動時候的 refresh 方法中去初始化的。
BeanPostProcessor 初始化的時候,先初始化實現(xiàn)了 PriorityOrdered 接口的,再初始化實現(xiàn)了 Ordered 接口的,最后再去初始化那些沒有實現(xiàn)任何排序接口的 BeanPostProcessor。
而我們這里 SetCustomTargetSourceCreator 一定要趕在 AnnotationAwareAspectJAutoProxyCreator 之前進行初始化,這樣,當 AnnotationAwareAspectJAutoProxyCreator 進行初始化的時候,就會用到 SetCustomTargetSourceCreator 這樣一個后置處理器,進而在該處理器中修改 AnnotationAwareAspectJAutoProxyCreator 的屬性。
AnnotationAwareAspectJAutoProxyCreator 類間接實現(xiàn)了 Ordered 接口,默認優(yōu)先級是最低,但是在 Spring 容器啟動時,在處理 BeanFactoryPostProcessor 時(具體是 ConfigurationClassPostProcessor),將其優(yōu)先級設(shè)置為最高。
所以,我們?nèi)绻胍屪远x的 SetCustomTargetSourceCreator 搶在 AnnotationAwareAspectJAutoProxyCreator 之前執(zhí)行,那么就只能讓 SetCustomTargetSourceCreator 去實現(xiàn) PriorityOrdered 接口了,實現(xiàn) PriorityOrdered 接口之后,重寫 getOrder 方法,這個方法返回值是什么無所謂,反正都會在實現(xiàn)了 Ordered 接口的 BeanPostProcessor 之前執(zhí)行。
最后,我們再在啟動類上開啟自動代理即可:
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class JavaConfig {
}
大功告成。
這樣,當 Spring 容器創(chuàng)建一個 Bean 的時候,就會提前被 BeanPostProcessor 攔截,然后給出一個 TargetSource,進而據(jù)此創(chuàng)建代理對象,這樣就不需要后續(xù)常規(guī)的 Bean 創(chuàng)建流程了。好啦,感興趣的小伙伴可以自己去試一試哦~