javascript
Spring AOP 源码分析 - 筛选合适的通知器
1.簡(jiǎn)介
從本篇文章開(kāi)始,我將會(huì)對(duì) Spring AOP 部分的源碼進(jìn)行分析。本文是 Spring AOP 源碼分析系列文章的第二篇,本文主要分析 Spring AOP 是如何為目標(biāo) bean 篩選出合適的通知器(Advisor)。在上一篇AOP 源碼分析導(dǎo)讀一文中,我簡(jiǎn)單介紹了 AOP 中的一些術(shù)語(yǔ)及其對(duì)應(yīng)的源碼,部分術(shù)語(yǔ)和源碼將會(huì)在本篇文章中出現(xiàn)。如果大家不熟悉這些術(shù)語(yǔ)和源碼,不妨去看看。
關(guān)于 Spring AOP,我個(gè)人在日常開(kāi)發(fā)中用過(guò)一些,也參照過(guò)?tiny-spring?過(guò)寫過(guò)一個(gè)玩具版的 AOP 框架,并寫成了文章。正因?yàn)榍懊孀隽艘恍?zhǔn)備工作,最近再看 Spring AOP 源碼時(shí),覺(jué)得也沒(méi)那么難了。所以如果大家打算看 AOP 源碼的話,這里建議大家多做一些準(zhǔn)備工作。比如熟悉 AOP 的中的術(shù)語(yǔ),亦或是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IOC 和 AOP,并將兩者整合在一起。經(jīng)過(guò)如此準(zhǔn)備,相信大家會(huì)對(duì) AOP 會(huì)有更多的認(rèn)識(shí)。
好了,其他的就不多說(shuō)了,下面進(jìn)入源碼分析階段。
?2.源碼分析
?2.1 AOP 入口分析
在導(dǎo)讀一文中,我已經(jīng)說(shuō)過(guò) Spring AOP 是在何處向目標(biāo) bean 中織入通知(Advice)的。也說(shuō)過(guò) Spring 是如何將 AOP 和 IOC 模塊整合到一起的,即通過(guò)拓展點(diǎn) BeanPostProcessor 接口。Spring AOP 抽象代理創(chuàng)建器實(shí)現(xiàn)了 BeanPostProcessor 接口,并在 bean 初始化后置處理過(guò)程中向 bean 中織入通知。下面我們就來(lái)看看相關(guān)源碼,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupportimplements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {@Override/** bean 初始化后置處理方法 */public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {// 如果需要,為 bean 生成代理對(duì)象return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (beanName != null && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}/** 如果是基礎(chǔ)設(shè)施類(Pointcut、Advice、Advisor 等接口的實(shí)現(xiàn)類),或是應(yīng)該跳過(guò)的類,* 則不應(yīng)該生成代理,此時(shí)直接返回 bean*/ if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {// 將 <cacheKey, FALSE> 鍵值對(duì)放入緩存中,供上面的 if 分支使用this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 為目標(biāo) bean 查找合適的通知器Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);/** 若 specificInterceptors != null,即 specificInterceptors != DO_NOT_PROXY,* 則為 bean 生成代理對(duì)象,否則直接返回 bean*/ if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 創(chuàng)建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());/** 返回代理對(duì)象,此時(shí) IOC 容器輸入 bean,得到 proxy。此時(shí),* beanName 對(duì)應(yīng)的 bean 是代理對(duì)象,而非原始的 bean*/ return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);// specificInterceptors = null,直接返回 beanreturn bean;} } |
以上就是 Spring AOP 創(chuàng)建代理對(duì)象的入口方法分析,過(guò)程比較簡(jiǎn)單,這里簡(jiǎn)單總結(jié)一下:
上面的流程看起來(lái)并不復(fù)雜,不過(guò)不要被表象所迷糊,以上流程不過(guò)是冰山一角。
圖片來(lái)源:無(wú)版權(quán)圖片網(wǎng)站?pixabay.com
在本文,以及后續(xù)的文章中,我將會(huì)對(duì)步驟2和步驟3對(duì)應(yīng)的源碼進(jìn)行分析。在本篇文章先來(lái)分析步驟2對(duì)應(yīng)的源碼。
?2.2 篩選合適的通知器
在向目標(biāo) bean 中織入通知之前,我們先要為 bean 篩選出合適的通知器(通知器持有通知)。如何篩選呢?方式由很多,比如我們可以通過(guò)正則表達(dá)式匹配方法名,當(dāng)然更多的時(shí)候用的是 AspectJ 表達(dá)式進(jìn)行匹配。那下面我們就來(lái)看一下使用 AspectJ 表達(dá)式篩選通知器的過(guò)程,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {// 查找合適的通知器List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray(); }protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 查找所有的通知器List<Advisor> candidateAdvisors = findCandidateAdvisors();/** 篩選可應(yīng)用在 beanClass 上的 Advisor,通過(guò) ClassFilter 和 MethodMatcher* 對(duì)目標(biāo)類和方法進(jìn)行匹配*/List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);// 拓展操作extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors; } |
如上,Spring 先查詢出所有的通知器,然后再調(diào)用 findAdvisorsThatCanApply 對(duì)通知器進(jìn)行篩選。在下面幾節(jié)中,我將分別對(duì) findCandidateAdvisors 和 findAdvisorsThatCanApply 兩個(gè)方法進(jìn)行分析,繼續(xù)往下看吧。
?2.2.1 查找通知器
Spring 提供了兩種配置 AOP 的方式,一種是通過(guò) XML 進(jìn)行配置,另一種是注解。對(duì)于兩種配置方式,Spring 的處理邏輯是不同的。對(duì)于 XML 類型的配置,比如下面的配置:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <!-- 目標(biāo) bean --> <bean id="hello" class="xyz.coolblog.aop.Hello"/><aop:aspectj-autoproxy/><!-- 普通 bean,包含 AOP 切面邏輯 --> <bean id="aopCode" class="xyz.coolblog.aop.AopCode"/> <!-- 由 @Aspect 注解修飾的切面類 --> <bean id="annotationAopCode" class="xyz.coolblog.aop.AnnotationAopCode"/><aop:config><aop:aspect ref="aopCode"><aop:pointcut id="helloPointcut" expression="execution(* xyz.coolblog.aop.*.hello*(..))" /><aop:before method="before" pointcut-ref="helloPointcut"/><aop:after method="after" pointcut-ref="helloPointcut"/></aop:aspect> </aop:config> |
Spring 會(huì)將上的配置解析為下面的結(jié)果:
如上圖所示,紅框中對(duì)應(yīng)的是普通的 bean 定義,比如?<bean id="hello" .../>、<bean id="annotationAopCode" .../>、<bean id="appCode" .../>?等配置。黃色框中的則是切點(diǎn)的定義,類型為 AspectJExpressionPointcut,對(duì)應(yīng)?<aop:pointcut id="helloPointcut" .../>?配置。那綠色框中的結(jié)果對(duì)應(yīng)的是什么配置呢?目前僅剩下兩個(gè)配置沒(méi)說(shuō),所以對(duì)應(yīng)?<aop:before .../>?和?<aop:after .../>?配置,類型為 AspectJPointcutAdvisor。這里請(qǐng)大家注意,由 @Aspect 注解修飾的 AnnotationAopCode 也是普通類型的 bean,該 bean 會(huì)在查找通知器的過(guò)程中被解析,并被構(gòu)建為一個(gè)或多個(gè) Advisor。
上面講解了 Spring AOP 兩種配置的處理方式,算是為下面的源碼分析做鋪墊。現(xiàn)在鋪墊完畢,我們就來(lái)分析一下源碼吧。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {//...@Overrideprotected List<Advisor> findCandidateAdvisors() {// 調(diào)用父類方法從容器中查找所有的通知器List<Advisor> advisors = super.findCandidateAdvisors();// 解析 @Aspect 注解,并構(gòu)建通知器advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());return advisors;}//... } |
AnnotationAwareAspectJAutoProxyCreator 覆寫了父類的方法 findCandidateAdvisors,并增加了一步操作,即解析 @Aspect 注解,并構(gòu)建成通知器。下面我先來(lái)分析一下父類中的 findCandidateAdvisors 方法的邏輯,然后再來(lái)分析 buildAspectJAdvisors 方法邏的輯。
?2.2.1.1 findCandidateAdvisors 方法分析
我們先來(lái)看一下 AbstractAdvisorAutoProxyCreator 中 findCandidateAdvisors 方法的定義,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 | public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;//...protected List<Advisor> findCandidateAdvisors() {return this.advisorRetrievalHelper.findAdvisorBeans();}//... } |
從上面的源碼中可以看出,AbstractAdvisorAutoProxyCreator 中的 findCandidateAdvisors 是個(gè)空殼方法,所有邏輯封裝在了一個(gè) BeanFactoryAdvisorRetrievalHelper 的 findAdvisorBeans 方法中。這里大家可以仔細(xì)看一下類名 BeanFactoryAdvisorRetrievalHelper 和方法 findAdvisorBeans,兩個(gè)名字其實(shí)已經(jīng)描述出他們的職責(zé)了。BeanFactoryAdvisorRetrievalHelper 可以理解為從 bean 容器中獲取 Advisor 的幫助類,findAdvisorBeans 則可理解為查找 Advisor 類型的 bean。所以即使不看 findAdvisorBeans 方法的源碼,我們也可從方法名上推斷出它要做什么,即從 bean 容器中將 Advisor 類型的 bean 查找出來(lái)。下面我來(lái)分析一下這個(gè)方法的源碼,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | public List<Advisor> findAdvisorBeans() {String[] advisorNames = null;synchronized (this) {// cachedAdvisorBeanNames 是 advisor 名稱的緩存advisorNames = this.cachedAdvisorBeanNames;/** 如果 cachedAdvisorBeanNames 為空,這里到容器中查找,* 并設(shè)置緩存,后續(xù)直接使用緩存即可*/ if (advisorNames == null) {// 從容器中查找 Advisor 類型 bean 的名稱advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);// 設(shè)置緩存this.cachedAdvisorBeanNames = advisorNames;}}if (advisorNames.length == 0) {return new LinkedList<Advisor>();}List<Advisor> advisors = new LinkedList<Advisor>();// 遍歷 advisorNamesfor (String name : advisorNames) {if (isEligibleBean(name)) {// 忽略正在創(chuàng)建中的 advisor beanif (this.beanFactory.isCurrentlyInCreation(name)) {if (logger.isDebugEnabled()) {logger.debug("Skipping currently created advisor '" + name + "'");}}else {try {/** 調(diào)用 getBean 方法從容器中獲取名稱為 name 的 bean,* 并將 bean 添加到 advisors 中*/ advisors.add(this.beanFactory.getBean(name, Advisor.class));}catch (BeanCreationException ex) {Throwable rootCause = ex.getMostSpecificCause();if (rootCause instanceof BeanCurrentlyInCreationException) {BeanCreationException bce = (BeanCreationException) rootCause;if (this.beanFactory.isCurrentlyInCreation(bce.getBeanName())) {if (logger.isDebugEnabled()) {logger.debug("Skipping advisor '" + name +"' with dependency on currently created bean: " + ex.getMessage());}continue;}}throw ex;}}}}return advisors; } |
以上就是從容器中查找 Advisor 類型的 bean 所有的邏輯,代碼雖然有點(diǎn)長(zhǎng),但并不復(fù)雜。主要做了兩件事情:
看完上面的分析,我們繼續(xù)來(lái)分析一下 @Aspect 注解的解析過(guò)程。
?2.2.1.2 buildAspectJAdvisors 方法分析
與上一節(jié)的內(nèi)容相比,解析 @Aspect 注解的過(guò)程還是比較復(fù)雜的,需要一些耐心去看。下面我們開(kāi)始分析 buildAspectJAdvisors 方法的源碼,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | public List<Advisor> buildAspectJAdvisors() {List<String> aspectNames = this.aspectBeanNames;if (aspectNames == null) {synchronized (this) {aspectNames = this.aspectBeanNames;if (aspectNames == null) {List<Advisor> advisors = new LinkedList<Advisor>();aspectNames = new LinkedList<String>();// 從容器中獲取所有 bean 的名稱String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);// 遍歷 beanNamesfor (String beanName : beanNames) {if (!isEligibleBean(beanName)) {continue;}// 根據(jù) beanName 獲取 bean 的類型Class<?> beanType = this.beanFactory.getType(beanName);if (beanType == null) {continue;}// 檢測(cè) beanType 是否包含 Aspect 注解if (this.advisorFactory.isAspect(beanType)) {aspectNames.add(beanName);AspectMetadata amd = new AspectMetadata(beanType, beanName);if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);// 獲取通知器List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);if (this.beanFactory.isSingleton(beanName)) {this.advisorsCache.put(beanName, classAdvisors);}else {this.aspectFactoryCache.put(beanName, factory);}advisors.addAll(classAdvisors);}else {if (this.beanFactory.isSingleton(beanName)) {throw new IllegalArgumentException("Bean with name '" + beanName +"' is a singleton, but aspect instantiation model is not singleton");}MetadataAwareAspectInstanceFactory factory =new PrototypeAspectInstanceFactory(this.beanFactory, beanName);this.aspectFactoryCache.put(beanName, factory);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}this.aspectBeanNames = aspectNames;return advisors;}}}if (aspectNames.isEmpty()) {return Collections.emptyList();}List<Advisor> advisors = new LinkedList<Advisor>();for (String aspectName : aspectNames) {List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);if (cachedAdvisors != null) {advisors.addAll(cachedAdvisors);}else {MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);advisors.addAll(this.advisorFactory.getAdvisors(factory));}}return advisors; } |
上面就是 buildAspectJAdvisors 的代碼,看起來(lái)比較長(zhǎng)。代碼比較多,我們關(guān)注重點(diǎn)的方法調(diào)用即可。在進(jìn)行后續(xù)的分析前,這里先對(duì) buildAspectJAdvisors 方法的執(zhí)行流程做個(gè)總結(jié)。如下:
下面我們來(lái)重點(diǎn)分析advisorFactory.getAdvisors(factory)這個(gè)調(diào)用,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {// 獲取 aspectClass 和 aspectNameClass<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();validate(aspectClass);MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);List<Advisor> advisors = new LinkedList<Advisor>();// getAdvisorMethods 用于返回不包含 @Pointcut 注解的方法for (Method method : getAdvisorMethods(aspectClass)) {// 為每個(gè)方法分別調(diào)用 getAdvisor 方法Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}// If it's a per target aspect, emit the dummy instantiating aspect.if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}// Find introduction fields.for (Field field : aspectClass.getDeclaredFields()) {Advisor advisor = getDeclareParentsAdvisor(field);if (advisor != null) {advisors.add(advisor);}}return advisors; }public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());// 獲取切點(diǎn)實(shí)現(xiàn)類AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}// 創(chuàng)建 Advisor 實(shí)現(xiàn)類return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName); } |
如上,getAdvisor 方法包含兩個(gè)主要步驟,一個(gè)是獲取 AspectJ 表達(dá)式切點(diǎn),另一個(gè)是創(chuàng)建 Advisor 實(shí)現(xiàn)類。在第二個(gè)步驟中,包含一個(gè)隱藏步驟 – 創(chuàng)建 Advice。下面我將按順序依次分析這兩個(gè)步驟,先看獲取 AspectJ 表達(dá)式切點(diǎn)的過(guò)程,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {// 獲取方法上的 AspectJ 相關(guān)注解,包括 @Before,@After 等AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}// 創(chuàng)建一個(gè) AspectJExpressionPointcut 對(duì)象AspectJExpressionPointcut ajexp =new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);// 設(shè)置切點(diǎn)表達(dá)式ajexp.setExpression(aspectJAnnotation.getPointcutExpression());ajexp.setBeanFactory(this.beanFactory);return ajexp; }protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {// classesToLookFor 中的元素是大家熟悉的Class<?>[] classesToLookFor = new Class<?>[] {Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};for (Class<?> c : classesToLookFor) {// 查找注解AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) c);if (foundAnnotation != null) {return foundAnnotation;}}return null; } |
獲取切點(diǎn)的過(guò)程并不復(fù)雜,不過(guò)需要注意的是,目前獲取到的切點(diǎn)可能還只是個(gè)半成品,需要再次處理一下才行。比如下面的代碼:
| 1 2 3 4 5 6 7 8 9 10 11 | @Aspect public class AnnotationAopCode {@Pointcut("execution(* xyz.coolblog.aop.*.world*(..))")public void pointcut() {}@Before("pointcut()")public void before() {System.out.println("AnnotationAopCode`s before");} } |
@Before 注解中的表達(dá)式是pointcut(),也就是說(shuō) ajexp 設(shè)置的表達(dá)式只是一個(gè)中間值,不是最終值,即execution(* xyz.coolblog.aop.*.world*(..))。所以后續(xù)還需要將 ajexp 中的表達(dá)式進(jìn)行轉(zhuǎn)換,關(guān)于這個(gè)轉(zhuǎn)換的過(guò)程,我就不說(shuō)了。有點(diǎn)復(fù)雜,我暫時(shí)沒(méi)怎么看懂。
說(shuō)完切點(diǎn)的獲取過(guò)程,下面再來(lái)看看 Advisor 實(shí)現(xiàn)類的創(chuàng)建過(guò)程。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {this.declaredPointcut = declaredPointcut;this.declaringClass = aspectJAdviceMethod.getDeclaringClass();this.methodName = aspectJAdviceMethod.getName();this.parameterTypes = aspectJAdviceMethod.getParameterTypes();this.aspectJAdviceMethod = aspectJAdviceMethod;this.aspectJAdvisorFactory = aspectJAdvisorFactory;this.aspectInstanceFactory = aspectInstanceFactory;this.declarationOrder = declarationOrder;this.aspectName = aspectName;if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);this.lazy = true;}else {this.pointcut = this.declaredPointcut;this.lazy = false;// 按照注解解析 Advicethis.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);} } |
上面是 InstantiationModelAwarePointcutAdvisorImpl 的構(gòu)造方法,不過(guò)我們無(wú)需太關(guān)心這個(gè)方法中的一些初始化邏輯。我們把目光移到構(gòu)造方法的最后一行代碼中,即 instantiateAdvice(this.declaredPointcut),這個(gè)方法用于創(chuàng)建通知 Advice。在上一篇文章中我已經(jīng)說(shuō)過(guò),通知器 Advisor 是通知 Advice 的持有者,所以在 Advisor 實(shí)現(xiàn)類的構(gòu)造方法中創(chuàng)建通知也是合適的。那下面我們就來(lái)看看構(gòu)建通知的過(guò)程是怎樣的,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {return this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pcut,this.aspectInstanceFactory, this.declarationOrder, this.aspectName); }public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();validate(candidateAspectClass);// 獲取 Advice 注解AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}if (!isAspect(candidateAspectClass)) {throw new AopConfigException("Advice must be declared inside an aspect type: " +"Offending method '" + candidateAdviceMethod + "' in class [" +candidateAspectClass.getName() + "]");}if (logger.isDebugEnabled()) {logger.debug("Found AspectJ method: " + candidateAdviceMethod);}AbstractAspectJAdvice springAdvice;// 按照注解類型生成相應(yīng)的 Advice 實(shí)現(xiàn)類switch (aspectJAnnotation.getAnnotationType()) {case AtBefore: // @Before -> AspectJMethodBeforeAdvicespringAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfter: // @After -> AspectJAfterAdvicespringAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;case AtAfterReturning: // @AfterReturning -> AspectJAfterAdvicespringAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;case AtAfterThrowing: // @AfterThrowing -> AspectJAfterThrowingAdvicespringAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;case AtAround: // @Around -> AspectJAroundAdvicespringAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;/** 什么都不做,直接返回 null。從整個(gè)方法的調(diào)用棧來(lái)看,* 并不會(huì)出現(xiàn)注解類型為 AtPointcut 的情況*/ case AtPointcut: if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}return null;default:throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);}springAdvice.setAspectName(aspectName);springAdvice.setDeclarationOrder(declarationOrder);/** 獲取方法的參數(shù)列表名稱,比如方法 int sum(int numX, int numY), * getParameterNames(sum) 得到 argNames = [numX, numY]*/String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);if (argNames != null) {// 設(shè)置參數(shù)名springAdvice.setArgumentNamesFromStringArray(argNames);}springAdvice.calculateArgumentBindings();return springAdvice; } |
上面的代碼邏輯不是很復(fù)雜,主要的邏輯就是根據(jù)注解類型生成與之對(duì)應(yīng)的通知對(duì)象。下面來(lái)總結(jié)一下獲取通知器(getAdvisors)整個(gè)過(guò)程的邏輯,如下:
如上所示,上面的步驟做了一定的簡(jiǎn)化。總的來(lái)說(shuō),獲取通知器的過(guò)程還是比較復(fù)雜的,并不是很容易看懂。大家在閱讀的過(guò)程中,還要寫一些測(cè)試代碼進(jìn)行調(diào)試才行。調(diào)試的過(guò)程中,一些不關(guān)心的調(diào)用就別跟進(jìn)去了,不然會(huì)陷入很深的調(diào)用棧中,影響對(duì)源碼主流程的理解。
現(xiàn)在,大家知道了通知是怎么創(chuàng)建的。那我們難道不要去看看這些通知的實(shí)現(xiàn)源碼嗎?顯然,我們應(yīng)該看一下。那接下里,我們一起來(lái)分析一下 AspectJMethodBeforeAdvice,也就是 @Before 注解對(duì)應(yīng)的通知實(shí)現(xiàn)類。看看它的邏輯是什么樣的。
?2.2.1.3 AspectJMethodBeforeAdvice 分析
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice {public AspectJMethodBeforeAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {super(aspectJBeforeAdviceMethod, pointcut, aif);}@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {// 調(diào)用通知方法invokeAdviceMethod(getJoinPointMatch(), null, null);}@Overridepublic boolean isBeforeAdvice() {return true;}@Overridepublic boolean isAfterAdvice() {return false;}}protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {// 調(diào)用通知方法,并向其傳遞參數(shù)return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex)); }protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {Object[] actualArgs = args;if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {actualArgs = null;}try {ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);// 通過(guò)反射調(diào)用通知方法return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);}catch (IllegalArgumentException ex) {throw new AopInvocationException("Mismatch on arguments to advice method [" +this.aspectJAdviceMethod + "]; pointcut expression [" +this.pointcut.getPointcutExpression() + "]", ex);}catch (InvocationTargetException ex) {throw ex.getTargetException();} } |
如上,AspectJMethodBeforeAdvice 的源碼比較簡(jiǎn)單,這里我們僅關(guān)注 before 方法。這個(gè)方法調(diào)用了父類中的 invokeAdviceMethod,然后 invokeAdviceMethod 在調(diào)用 invokeAdviceMethodWithGivenArgs,最后在 invokeAdviceMethodWithGivenArgs 通過(guò)反射執(zhí)行通知方法。是不是很簡(jiǎn)單?
關(guān)于 AspectJMethodBeforeAdvice 就簡(jiǎn)單介紹到這里吧,至于剩下的幾種實(shí)現(xiàn),大家可以自己去看看。好了,關(guān)于 AspectJMethodBeforeAdvice 的源碼分析,就分析到這里了。我們繼續(xù)往下看吧。
?2.2.2 篩選合適的通知器
查找出所有的通知器,整個(gè)流程還沒(méi)算完,接下來(lái)我們還要對(duì)這些通知器進(jìn)行篩選。適合應(yīng)用在當(dāng)前 bean 上的通知器留下,不適合的就讓它自生自滅吧。那下面我們來(lái)分析一下通知器篩選的過(guò)程,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {ProxyCreationContext.setCurrentProxiedBeanName(beanName);try {// 調(diào)用重載方法return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);}finally {ProxyCreationContext.setCurrentProxiedBeanName(null);} }public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();for (Advisor candidate : candidateAdvisors) {// 篩選 IntroductionAdvisor 類型的通知器if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}boolean hasIntroductions = !eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {if (candidate instanceof IntroductionAdvisor) {continue;}// 篩選普通類型的通知器if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors; }public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {/** 從通知器中獲取類型過(guò)濾器 ClassFilter,并調(diào)用 matchers 方法進(jìn)行匹配。* ClassFilter 接口的實(shí)現(xiàn)類 AspectJExpressionPointcut 為例,該類的* 匹配工作由 AspectJ 表達(dá)式解析器負(fù)責(zé),具體匹配細(xì)節(jié)這個(gè)就沒(méi)法分析了,我* AspectJ 表達(dá)式的工作流程不是很熟*/return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;// 對(duì)于普通類型的通知器,這里繼續(xù)調(diào)用重載方法進(jìn)行篩選return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {return true;} }public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");// 使用 ClassFilter 匹配 classif (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}/** 查找當(dāng)前類及其父類(以及父類的父類等等)所實(shí)現(xiàn)的接口,由于接口中的方法是 public,* 所以當(dāng)前類可以繼承其父類,和父類的父類中所有的接口方法*/ Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);for (Class<?> clazz : classes) {// 獲取當(dāng)前類的方法列表,包括從父類中繼承的方法Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {// 使用 methodMatcher 匹配方法,匹配成功即可立即返回if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false; } |
以上是通知器篩選的過(guò)程,篩選的工作主要由 ClassFilter 和 MethodMatcher 完成。關(guān)于 ClassFilter 和 MethodMatcher 我在導(dǎo)讀一文中已經(jīng)說(shuō)過(guò)了,這里再說(shuō)一遍吧。在 AOP 中,切點(diǎn) Pointcut 是用來(lái)匹配連接點(diǎn)的,以 AspectJExpressionPointcut 類型的切點(diǎn)為例。該類型切點(diǎn)實(shí)現(xiàn)了ClassFilter 和 MethodMatcher 接口,匹配的工作則是由 AspectJ 表達(dá)式解析器復(fù)雜。除了使用 AspectJ 表達(dá)式進(jìn)行匹配,Spring 還提供了基于正則表達(dá)式的切點(diǎn)類,以及更簡(jiǎn)單的根據(jù)方法名進(jìn)行匹配的切點(diǎn)類。大家有興趣的話,可以自己去了解一下,這里就不多說(shuō)了。
在完成通知器的查找和篩選過(guò)程后,還需要進(jìn)行最后一步處理 – 對(duì)通知器列表進(jìn)行拓展。怎么拓展呢?我們一起到下一節(jié)中一探究竟吧。
?2.2.3 拓展篩選出通知器列表
拓展方法 extendAdvisors 做的事情并不多,邏輯也比較簡(jiǎn)單。我們一起來(lái)看一下,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | protected void extendAdvisors(List<Advisor> candidateAdvisors) {AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors); }public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {// 如果通知器列表是一個(gè)空列表,則啥都不做if (!advisors.isEmpty()) {boolean foundAspectJAdvice = false;/** 下面的 for 循環(huán)用于檢測(cè) advisors 列表中是否存在 * AspectJ 類型的 Advisor 或 Advice*/for (Advisor advisor : advisors) {if (isAspectJAdvice(advisor)) {foundAspectJAdvice = true;}}/** 向 advisors 列表的首部添加 DefaultPointcutAdvisor,* 至于為什么這樣做,我會(huì)在后續(xù)的文章中進(jìn)行說(shuō)明*/if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {advisors.add(0, ExposeInvocationInterceptor.ADVISOR);return true;}}return false; }private static boolean isAspectJAdvice(Advisor advisor) {return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||advisor.getAdvice() instanceof AbstractAspectJAdvice ||(advisor instanceof PointcutAdvisor &&((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut)); } |
如上,上面的代碼比較少,也不復(fù)雜。由源碼可以看出 extendAdvisors 是一個(gè)空殼方法,除了調(diào)用makeAdvisorChainAspectJCapableIfNecessary,該方法沒(méi)有其他更多的邏輯了。至于 makeAdvisorChainAspectJCapableIfNecessary 這個(gè)方法,該方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor 類型的通知器,也就是 ExposeInvocationInterceptor.ADVISOR。至于添加此種類型通知器的意圖,我會(huì)在后面文章里說(shuō)明,這里不便展開(kāi)。關(guān)于 extendAdvisors 這個(gè)方法,這里就先說(shuō)到這了。
?3.總結(jié)
到這里,本篇文章就接近尾聲了。這篇文章有點(diǎn)長(zhǎng),大家看下來(lái)應(yīng)該蠻累的吧。由于個(gè)人能力問(wèn)題,暫時(shí)未能做到對(duì)本篇文章中所貼的源碼進(jìn)行更為細(xì)致的分析,有點(diǎn)遺憾。不過(guò)好在目前把主邏輯分析弄清楚了,總的來(lái)說(shuō)還算合格吧,給個(gè)及格分。大家在閱讀的過(guò)程中,如果發(fā)現(xiàn)文章中出現(xiàn)錯(cuò)誤或不妥之處,這里還請(qǐng)指明,也請(qǐng)多多指教。大家共同學(xué)習(xí),一起進(jìn)步。
好了,本篇文章就到這里了。謝謝大家的閱讀。
?參考
- 《Spring 源碼深度解析》- 郝佳
?附錄:Spring 源碼分析文章列表
?Ⅰ. IOC
| 2018-05-30 | Spring IOC 容器源碼分析系列文章導(dǎo)讀 |
| 2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
| 2018-06-04 | Spring IOC 容器源碼分析 - 創(chuàng)建單例 bean 的過(guò)程 |
| 2018-06-06 | Spring IOC 容器源碼分析 - 創(chuàng)建原始 bean 對(duì)象 |
| 2018-06-08 | Spring IOC 容器源碼分析 - 循環(huán)依賴的解決辦法 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對(duì)象 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
?Ⅱ. AOP
| 2018-06-17 | Spring AOP 源碼分析系列文章導(dǎo)讀 |
| 2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
| 2018-06-20 | Spring AOP 源碼分析 - 創(chuàng)建代理對(duì)象 |
| 2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執(zhí)行過(guò)程 |
?Ⅲ. MVC
| 2018-06-29 | Spring MVC 原理探秘 - 一個(gè)請(qǐng)求的旅行過(guò)程 |
| 2018-06-30 | Spring MVC 原理探秘 - 容器的創(chuàng)建過(guò)程 |
- 本文鏈接:?https://www.tianxiaobo.com/2018/06/20/Spring-AOP-源碼分析-篩選合適的通知器/
from:?http://www.tianxiaobo.com/2018/06/20/Spring-AOP-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E7%AD%9B%E9%80%89%E5%90%88%E9%80%82%E7%9A%84%E9%80%9A%E7%9F%A5%E5%99%A8/?
總結(jié)
以上是生活随笔為你收集整理的Spring AOP 源码分析 - 筛选合适的通知器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spring IOC 容器源码分析系列文
- 下一篇: Spring AOP 源码分析 - 创建