Spring?Bean名稱不會被代理的命名技巧
1. 代碼實踐
假設(shè)我有如下一個切面:
@Aspect @EnableAspectJAutoProxy @Component public class LogAspect { @Pointcut("execution(* org.javaboy.demo.service.*.*(..))") public void pc() { } @Before("pc()") public void before(JoinPoint jp) { String name = jp.getSignature().getName(); System.out.println(name + " 方法開始執(zhí)行了..."); } }
這個切面要攔截的方法是 org.javaboy.demo.service
包下的所有類的所有方法,現(xiàn)在,這個包下有一個 BookService 類,內(nèi)容如下:
@Service("org.javaboy.demo.service.BookService.ORIGINAL") public class BookService { public void hello() { System.out.println("hello bs"); } }
這個 BookService 的 beanName 我沒有使用默認(rèn)的 beanName,而是自己配置了一個 beanName,這個 beanName 的配置方式是 類名的完整路徑+.ORIGINAL
。
當(dāng)我們按照這樣的規(guī)則給 bean 取名之后,那么即使當(dāng)前 bean 已經(jīng)包含在切點所定義的范圍內(nèi),這個 bean 也不會被代理了。
這是 Spring5.1 開始的新玩法。
這種寫法的原理是什么呢?
2. 原理分析
在 Spring 創(chuàng)建 Bean 的時候,小伙伴們都知道,bean 創(chuàng)建完成之后會去各種后置處理器(BeanPostProcessor)中走一圈,所以一般我們認(rèn)為 BeanPostProcessor 是在 bean 實例創(chuàng)建完成之后執(zhí)行。但是,BeanPostProcessor 中有一個特例 InstantiationAwareBeanPostProcessor,這個接口繼承自 BeanPostProcessor,但是在 BeanPostProcessor 的基礎(chǔ)之上,增加了額外的能力:
- 在 bean 實例化之前先做一些預(yù)處理,例如直接創(chuàng)建代理對象,代替后續(xù)的 bean 生成。
- 在 bean 實例化之后但是屬性填充之前,可以自定義一些屬性注入策略。
大致上就是這兩方面的能力。
具體到代碼上,就是在創(chuàng)建 bean 的 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; } } 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 一個返回代理對象的機(jī)會,在這個方法中,最終就會觸發(fā)到 InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation 方法,我們來看看這里涉及到的跟 AOP 相關(guān)的 AbstractAutoProxyCreator#postProcessBeforeInstantiation 方法:
@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; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in a custom fashion. 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; }
方法作用
這個方法實際上干了兩件事:
- 檢查當(dāng)前 bean 是否需要代理,如果不需要代理,那么就存入到一個 map 集合 advisedBeans 中,key 是 bean 的名字,value 如果為 true 則表示這個 bean 是需要代理的,value 為 false,則表示這個 bean 是不需要代理的。
- 如果有我們有自定義的 TargetSource,則根據(jù)自定義的 TargetSource 去創(chuàng)建代理對象。
這里我要和大家說的是第一點。
在判斷一個 bean 是否需要代理的時候,主要依據(jù)兩個方法:
- isInfrastructureClass:這個方法主要是檢查當(dāng)前 bean 是否是 Advice/Advisor/Pointcut 等類型,或者這個類上是否有 @Aspect 注解,這個松哥在之前的文章中其實和大家介紹過了:聽說 Spring Bean 的創(chuàng)建還有一條捷徑?。
- shouldSkip:如果 isInfrastructureClass 方法返回 false,那么就要執(zhí)行 shouldSkip 了,我們來仔細(xì)看下 shouldSkip 方法。
@Override protected boolean shouldSkip(Class<?> beanClass, String beanName) { // TODO: Consider optimization by caching the list of the aspect names List<Advisor> candidateAdvisors = findCandidateAdvisors(); for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor pointcutAdvisor && pointcutAdvisor.getAspectName().equals(beanName)) { return true; } } return super.shouldSkip(beanClass, beanName); }
這里首先找到系統(tǒng)中所有的切面,找到之后挨個遍歷,遍歷的時候判斷如果當(dāng)前要創(chuàng)建的 bean 剛好就是切面,那切面肯定是不需要代理的,直接返回 true。否則就會去調(diào)用父類的 shouldSkip 方法,我們再來瞅一眼父類的 shouldSkip 方法:
protected boolean shouldSkip(Class<?> beanClass, String beanName) { return AutoProxyUtils.isOriginalInstance(beanName, beanClass); } static boolean isOriginalInstance(String beanName, Class<?> beanClass) { if (!StringUtils.hasLength(beanName) || beanName.length() != beanClass.getName().length() + AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX.length()) { return false; } return (beanName.startsWith(beanClass.getName()) && beanName.endsWith(AutowireCapableBeanFactory.ORIGINAL_INSTANCE_SUFFIX)); }
父類 shouldSkip 方法主要是調(diào)用了一個靜態(tài)工具方法 isOriginalInstance 來判斷當(dāng)前 bean 是否是一個不需要代理的 bean,這個具體的判斷邏輯就是檢查這個 beanName 是否按照 類名的完整路徑+.ORIGINAL
的方式命名的,如果是則返回 true。
當(dāng) shouldSkip 方法返回 true 的時候,就會進(jìn)入到 postProcessBeforeInstantiation 方法的 if 分支中,該分支將當(dāng)前 beanName 存入到 advisedBeans 集合中,存儲的 key 就是 beanName,value 則是 false,然后將方法 return。
當(dāng) bean 創(chuàng)建完成之后,再進(jìn)入到 AbstractAutoProxyCreator#postProcessAfterInitialization 方法中處理的時候,就會發(fā)現(xiàn)這個 bean 已經(jīng)存入到 advisedBeans 集合中,并且 value 是 false,這就意味著這個 bean 不需要代理,那么就針對該 bean 就不會進(jìn)行 AOP 處理了,直接 return 即可。
以上就是Spring Bean名稱不會被代理的命名技巧的詳細(xì)內(nèi)容,更多關(guān)于Spring Bean命名的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringCloud?Eureka應(yīng)用全面介紹
Eureka是Netflix開發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的2022-09-09Java中使用BeanMap將對象轉(zhuǎn)為Map詳解
這篇文章主要介紹了Java中使用BeanMap將對象轉(zhuǎn)為Map詳解,BeanMap?是?Apache?Commons?BeanUtils?庫中的一個類,BeanMap?可以將?Java?對象的屬性作為鍵,屬性值作為對應(yīng)的值,存儲在一個?Map?中,它提供了一種將?Java?對象轉(zhuǎn)換為?Map?的方式,需要的朋友可以參考下2024-01-01用Java代碼實現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)的基本方法歸納
這篇文章主要介紹了用Java代碼實現(xiàn)棧數(shù)據(jù)結(jié)構(gòu)的基本方法歸納,各種算法的實現(xiàn)也是ACM上經(jīng)常出現(xiàn)的題目,是計算機(jī)學(xué)習(xí)的基本功,需要的朋友可以參考下2015-08-08