Spring?Bean創(chuàng)建的另一條捷徑
Spring Bean創(chuàng)建方法
有如下一段代碼:
AbstractAutowireCapableBeanFactory#createBean:
@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; } } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } //... }
我們平時(shí)說(shuō)的 Bean 的創(chuàng)建邏輯都是指 doCreateBean 方法中的邏輯,在松哥前面幾篇文章中,凡是涉及到 Bean 的創(chuàng)建流程的,我說(shuō)的也都是 doCreateBean 方法的流程。
但是小伙伴們注意,在 doCreateBean 方法執(zhí)行之前,其實(shí)還有一個(gè) resolveBeforeInstantiation 方法會(huì)先執(zhí)行,而這個(gè)方法可能就直接產(chǎn)生一個(gè) Bean 了!如果這個(gè)方法直接產(chǎn)生一個(gè) Bean 了,那么 doCreateBean 方法中的邏輯就不會(huì)生效了。
那么 resolveBeforeInstantiation 方法存在的意義是什么呢?其實(shí)大家從該方法的注釋上大概也能看出一些端倪出來(lái)了:
給 BeanPostProcessor 一個(gè)機(jī)會(huì)去創(chuàng)建一個(gè)代理對(duì)象,用這個(gè)代理對(duì)象來(lái)代替目標(biāo) Bean。
1. resolveBeforeInstantiation
看下面的源碼小伙伴們一定要先搞清楚兩個(gè)比較相似的單詞,否則看到后面就亂了:
- instantiation:實(shí)例化,從 Class 到 Bean 就是實(shí)例化。
- initialization:初始化,給 Bean 做各種配置就是初始化。
搞明白這兩個(gè)單詞,我們來(lái)看源碼。
首先我先來(lái)和小伙伴們稍微梳理一下 resolveBeforeInstantiation 方法。
AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation:
@Nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
小伙伴們看一下,這里有一個(gè)判斷條件,mbd.isSynthetic 是判斷是否是一個(gè)合成 Bean,這種一般都是 Spring 定義的,我們自定義的 Bean 一般都不屬于這一類,然后后面的 hasInstantiationAwareBeanPostProcessors 方法則是判斷當(dāng)前是否存在 InstantiationAwareBeanPostProcessor 類型的后置處理器,如果存在,則進(jìn)入到 if 分支中。
如果我們想要在 resolveBeforeInstantiation 方法中就完成 Bean 的處理,那么就需要自己提供一個(gè) InstantiationAwareBeanPostProcessor 類型的后置處理器。
接下來(lái)會(huì)調(diào)用兩個(gè)方法:
- applyBeanPostProcessorsBeforeInstantiation:從名字可以看出來(lái),這個(gè)是在實(shí)例化之前觸發(fā)的方法,所以這個(gè)方法的參數(shù)還是 Class,因?yàn)檫€未實(shí)例化。
- applyBeanPostProcessorsAfterInitialization:從名字可以看出來(lái),這個(gè)是在初始化之后出發(fā)的方法,所以這個(gè)方法的參數(shù)是 Bean,因?yàn)榇藭r(shí)已經(jīng)完成了初始化了。
1.1 applyBeanPostProcessorsBeforeInstantiation
@Nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) { Object result = bp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } return null; }
這個(gè)地方就很簡(jiǎn)單了,就是執(zhí)行 InstantiationAwareBeanPostProcessor 類型的后置處理器的 postProcessBeforeInstantiation 方法。
1.2 applyBeanPostProcessorsAfterInitialization
@Override public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }
這個(gè)是執(zhí)行 BeanPostProcessor 的 postProcessAfterInitialization 方法。
所以這塊的源碼其實(shí)并不難,道理很簡(jiǎn)單。
1.3 案例
松哥寫(xiě)一個(gè)簡(jiǎn)單的案例小伙伴們來(lái)看下。
假設(shè)我有一個(gè) BookService,如下:
public class BookService { public void hello() { System.out.println("hello javaboy"); } }
然后我再創(chuàng)建一個(gè) InstantiationAwareBeanPostProcessor 類型的后置處理器,并且重寫(xiě)前面提到的 postProcessBeforeInstantiation 和 postProcessAfterInitialization 方法:
public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if (beanClass == BookService.class) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanClass); enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> { String name = method.getName(); System.out.println(name + " 方法開(kāi)始執(zhí)行了..."); Object invoke = proxy.invokeSuper(obj, args); System.out.println(name + " 方法執(zhí)行結(jié)束了..."); return invoke; }); BookService bookService = (BookService) enhancer.create(); return bookService; } return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("bean.getClass() ========= " + bean.getClass()); return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName); } }
在 postProcessBeforeInstantiation 方法中,如果要?jiǎng)?chuàng)建的 Bean 是 BookService,則這里通過(guò) Enhancer 來(lái)創(chuàng)建一個(gè) CGLIB 的代理對(duì)象,如果是其他的 Bean 的創(chuàng)建,則調(diào)用父類方法即可。這樣重寫(xiě)之后,就會(huì)導(dǎo)致在 1.1 小節(jié)中,獲取到的 result 就是一個(gè)代理的 BookService 對(duì)象。
在 postProcessAfterInitialization 方法中,我未做任何額外處理,就是把拿到的 Bean 打印了一下,此時(shí)我們拿到手的 Bean 其實(shí)就是前面 postProcessBeforeInstantiation 方法生成的代理對(duì)象,然后這里調(diào)用父類方法去返回,實(shí)際上就是把參數(shù) Bean 原封不動(dòng)返回。
最后將這兩個(gè) Bean 注冊(cè)到 Spring 容器中:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="org.javaboy.bean.aop2.BookService" id="bookService"/> <bean class="org.javaboy.bean.aop2.MyInstantiationAwareBeanPostProcessor" id="myInstantiationAwareBeanPostProcessor"/> </beans>
然后初始化 Spring 容器:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("aop2.xml"); BookService bs = ctx.getBean(BookService.class); System.out.println("bs.getClass() = " + bs.getClass()); bs.hello();
最終執(zhí)行結(jié)果如下:
可以看到,最終拿到的 BookService 就是一個(gè)代理對(duì)象,從源碼層面來(lái)講,這個(gè)代理對(duì)象在 resolveBeforeInstantiation 方法中就生成了,后續(xù)的 doCreateBean 方法實(shí)際上并未執(zhí)行。
這就是 resolveBeforeInstantiation 方法的作用,實(shí)際上就是給 BeanPostProcessor 一個(gè)機(jī)會(huì)去創(chuàng)建一個(gè)代理對(duì)象,用這個(gè)代理對(duì)象來(lái)代替目標(biāo) Bean。
2. 源碼實(shí)踐
松哥為什么會(huì)關(guān)注到這個(gè)方法呢?
如果有小伙伴研究過(guò) Spring AOP 源碼,就會(huì)發(fā)現(xiàn)這個(gè)方法在處理 Spring AOP 的時(shí)候,有一個(gè)用武之地。
當(dāng)我們?cè)?Spring AOP 中,往往通過(guò)如下代碼來(lái)定義切面:
@Component @Aspect @EnableAspectJAutoProxy public class LogAspect { //... }
這個(gè)類上面有一個(gè) @Aspect 注解,那么問(wèn)題來(lái)了,Spring 是如何識(shí)別出這是一個(gè)切面而非普通的 Bean 的?
答案就是在 1.1 小節(jié)中的 applyBeanPostProcessorsBeforeInstantiation 方法中,這個(gè)方法會(huì)遍歷所有 InstantiationAwareBeanPostProcessor 類型的后置處理器,InstantiationAwareBeanPostProcessor 有一個(gè)子類是 AnnotationAwareAspectJAutoProxyCreator,在這個(gè)處理器中,識(shí)別出來(lái)了 LogAspect 是一個(gè)切面。
具體識(shí)別方法如下:
首先調(diào)用 AnnotationAwareAspectJAutoProxyCreator 的 postProcessBeforeInstantiation 方法(實(shí)際上是 AnnotationAwareAspectJAutoProxyCreator 的父類 AbstractAutoProxyCreator 中的方法):
@Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { Object cacheKey = getCacheKey(beanClass, beanName); if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } //... }
從這個(gè)地方開(kāi)始,分成了兩條線:
- 如果是一個(gè)切面 Bean 的話,則執(zhí)行第一個(gè)方法 isInfrastructureClass 就可以返回 true 了。
- 如果是一個(gè)普通 Bean 的話,則第一個(gè)方法會(huì)返回 false,此時(shí)就會(huì)執(zhí)行第二個(gè)方法 shouldSkip(雖然該方法也會(huì)返回 false),但是該方法有一些其他的價(jià)值在里邊。
2.1 切面 Bean
我們先來(lái)看 isInfrastructureClass 方法,先來(lái)看切面 Bean 是怎么處理的。
這個(gè)方法我摘了一部分出來(lái),我們重點(diǎn)關(guān)注 isInfrastructureClass 方法,這個(gè)方法用來(lái)判斷當(dāng)前類是否是一個(gè) Aspect:
@Override protected boolean isInfrastructureClass(Class<?> beanClass) { return (super.isInfrastructureClass(beanClass) || (this.aspectJAdvisorFactory != null && this.aspectJAdvisorFactory.isAspect(beanClass))); }
這里的判斷主要是兩方面:
- 調(diào)用父類的方法去判斷當(dāng)前類是否和 AOP 相關(guān):
protected boolean isInfrastructureClass(Class<?> beanClass) { boolean retVal = Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass) || AopInfrastructureBean.class.isAssignableFrom(beanClass); return retVal; }
這個(gè)就不用我解釋了,這些都是我們?cè)?AOP 中的老熟人了。
- 調(diào)用
aspectJAdvisorFactory.isAspect
方法去判斷當(dāng)前類是否包含 @Aspect 注解:
AbstractAspectJAdvisorFactory#isAspect
@Override public boolean isAspect(Class<?> clazz) { return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz)); } private boolean hasAspectAnnotation(Class<?> clazz) { return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); }
這代碼可太好理解了,就是檢查當(dāng)前類是否有 @Aspect
注解。后面那個(gè) compiledByAjc 方法是檢查是否需要 ajc 編譯,這個(gè)我們一般都不需要,所以,只要 hasAspectAnnotation 方法返回 true,整體上就會(huì)返回 true。
如果我們的類上包含 @Aspect
注解,那么最終就會(huì)在將當(dāng)前類名加入到 advisedBeans Map 中,在 advisedBeans 這個(gè) Map 中,key 是當(dāng)前 Bean 的名稱,value 則是 false 是一個(gè)標(biāo)記,表示當(dāng)前類不需要生成代理類。
這就是 isInfrastructureClass 方法執(zhí)行的大致邏輯。
2.2 普通 Bean
如果是普通 Bean 的話,很明顯 isInfrastructureClass 方法會(huì)返回 false,這就會(huì)導(dǎo)致 shouldSkip 方法去執(zhí)行,這個(gè)方法名雖然叫 shouldSkip,但是卻干了不少實(shí)事。
這個(gè)方法我會(huì)在下篇文章中和小伙伴們分享 AOP 的創(chuàng)建過(guò)程中再和大家詳解,這里先說(shuō)一句,這個(gè)方法會(huì)把各種 Aspect Bean 都收集整理起來(lái),將來(lái)根據(jù)這些 Bean 去生成 Advisor。
好啦,這就是 resolveBeforeInstantiation 方法的作用,感興趣的小伙伴可以自己 DEBUG 看一些哦~
以上就是Spring Bean創(chuàng)建的另一條捷徑的詳細(xì)內(nèi)容,更多關(guān)于Spring Bean創(chuàng)建的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot與springmvc基礎(chǔ)入門(mén)講解
本篇文章主要介紹了詳解快速搭建Spring Boot+Spring MVC,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2021-07-07Mybatis(ParameterType)傳遞多個(gè)不同類型的參數(shù)方式
這篇文章主要介紹了Mybatis(ParameterType)傳遞多個(gè)不同類型的參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04JAVA實(shí)現(xiàn)經(jīng)典游戲坦克大戰(zhàn)的示例代碼
小時(shí)候大家都玩過(guò)坦克大戰(zhàn)吧,熟悉的旋律和豐富的關(guān)卡陪伴了我們一整個(gè)寒暑假。本文將通過(guò)Java+Swing實(shí)現(xiàn)這一經(jīng)典游戲,感興趣的可以學(xué)習(xí)一下2022-01-01IntelliJ IDEA 如何徹底刪除項(xiàng)目的步驟
本篇文章主要介紹了IntelliJ IDEA 如何徹底刪除項(xiàng)目的步驟,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11關(guān)于SpringMVC請(qǐng)求域?qū)ο蟮臄?shù)據(jù)共享問(wèn)題
這篇文章主要介紹了SpringMVC請(qǐng)求域?qū)ο蟮臄?shù)據(jù)共享問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02