摘要:版本如何掃描接上一回,講到了方法,該方法的目的是獲取并生成。其中英文為源碼注釋。那么,以上便是通過掃描配置并生成的過程了。一些總結讀到這兒,如何掃描配置,生成類,并匹配對應的整個流程已經很清楚了。
版本
spring 5.0.8.BUILD-SNAPSHOT
aspectjweaver 1.8.13
如何掃描Advice接上一回,講到了getAdvicesAndAdvisorsForBean方法,該方法的目的是獲取并生成Advisor Bean。其中包含了掃描通過@Aspect注解配置且與Bean方法的匹配的Advice,也是本章主要講的內容
getAdvicesAndAdvisorsForBean/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java @Override @Nullable protected Object[] getAdvicesAndAdvisorsForBean( Class> beanClass, String beanName, @Nullable TargetSource targetSource) { Listadvisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); } /** * Find all eligible Advisors for auto-proxying this class. * @param beanClass the clazz to find advisors for * @param beanName the name of the currently proxied bean * @return the empty List, not {@code null}, * if there are no pointcuts or interceptors * @see #findCandidateAdvisors * @see #sortAdvisors * @see #extendAdvisors */ protected List findEligibleAdvisors(Class> beanClass, String beanName) { List candidateAdvisors = findCandidateAdvisors(); List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; } ....
關注findEligibleAdvisors方法可以清楚地知道這里主要分四步:
findCandidateAdvisors (獲取候選Advisor)
findAdvisorsThatCanApply (獲取適用于bean的Advisor: 例如Pointcut匹配)
extendAdvisors (特殊處理,這里不贅述)
sortAdvisors (排序,不贅述)
什么是Advisor? 首先,Advice是增強方法,即@Around, @Before等注解修飾的方法。而Advisor則是在Advice之上再包了一層。例如PointcutAdvisor則包有Advice和Pointcut
下面接著看findCandidateAdvisors和findAdvisorsThatCanApply
findCandidateAdvisors/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java .... /** * Find all candidate Advisors to use in auto-proxying. * @return the List of candidate Advisors */ protected ListfindCandidateAdvisors() { Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available"); return this.advisorRetrievalHelper.findAdvisorBeans(); } .... /org/springframework/aop/aspectj/annotation/AnnotationAwareAspectJAutoProxyCreator.java .... @Override protected List findCandidateAdvisors() { // Add all the Spring advisors found according to superclass rules. List advisors = super.findCandidateAdvisors(); // Build Advisors for all AspectJ aspects in the bean factory. if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; } ....
這里findCandidateAdvisors在AbstractAdvisorAutoProxyCreator中有實現,同時被AnnotationAwareAspectJAutoProxyCreator重寫了。不過可以看到重寫的方法中先調用了super.findCandidateAdvisor,因此兩個方法的代碼都被執行了。
this.advisorRetrievalHelper.findAdvisorBeans (該方法主要從BeanFactory中獲取Advisor Bean)
this.aspectJAdvisorsBuilder.buildAspectJAdvisors (從所有Bean中獲取@Aspect配置的Bean并創建Advisor,也是我們關注的內容,下面細講)
/org/springframework/aop/aspectj/annotation/BeanFactoryAspectJAdvisorsBuilder.java .... /** * Look for AspectJ-annotated aspect beans in the current bean factory, * and return to a list of Spring AOP Advisors representing them. *Creates a Spring Advisor for each AspectJ advice method. * @return the list of {@link org.springframework.aop.Advisor} beans * @see #isEligibleBean */ public List
buildAspectJAdvisors() { // aspectBeanNames,緩存 // tips: aspectBeanNames由volatile修飾 // volatile: 保證變量可見性,指令不可重排 List aspectNames = this.aspectBeanNames; // 如緩存存在,則跳過初始化步驟 if (aspectNames == null) { // synchronized, 同步鎖 // 鎖住當前實例,里面的內容同一時間只會被一個線程執行 synchronized (this) { // 拿到鎖后再次獲取緩存,避免重復初始化 aspectNames = this.aspectBeanNames; if (aspectNames == null) { List advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); // 獲取原始類為Object的bean,即所有bean String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { // 是否是合適的bean if (!isEligibleBean(beanName)) { continue; } // We must be careful not to instantiate beans eagerly as in this case they // would be cached by the Spring container but would not have been weaved. Class> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // isAspect 是否為@Aspect注解修飾的Bean // 是不是很親切方法,終于讀到它了。這是我們在第二章一開始就提到的方法。 if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); // 由beanType,beanName組合Metadata,包含了創建Advisor需要的內容 AspectMetadata amd = new AspectMetadata(beanType, beanName); // PerClauseKind.SINGLETON 單例模式 // 由@Aspect中的value參數配置,這個參數Bean的scope 有點類似,用來配置生命 周期,默認都為單例。可配為"每..."的模式 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 生成實例工廠類 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 生成Advisors實例 List classAdvisors = this.advisorFactory.getAdvisors(factory); // 如果Aspect Bean是單例,則緩存到advisorsCache if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } // 如果不是,則將工廠緩存到aspectFactoryCache else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // Per target or per this. // bean為單例,@Aspect也要配置為單例 if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name "" + beanName + "" is a singleton, but aspect instantiation model is not singleton"); } // 跟前一個分支一樣,生成Advisors實例,然后將工廠緩存到aspectFactoryCache 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 advisors = new ArrayList<>(); for (String aspectName : aspectNames) { // 通過aspectName(beanName)獲取advisors緩存 List cachedAdvisors = this.advisorsCache.get(aspectName); // 如已存在,則加載advisorsCache if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } // 如不存在,則加載factoryCache,再從工廠生成advisors,與上面初始時候的兩個分支對應 // ps:這里并沒有做factory的空判斷... else { MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; } ....
由于這段代碼比較長,我將過程注釋在代碼中。其中英文為源碼注釋。
那么,以上便是通過beanName掃描@Aspect配置并生成Advisor的過程了。其中this.advisorFactory.getAdvisors(factory)是生成Advisor類的具體內容。深挖的話還能再寫一篇文章,這里就不細說了。有興趣的可以自行閱讀。
findAdvisorsThatCanApply現在我們獲得了所有的候選Advisor,那么找出和當前Bean匹配的Advisor呢?
/org/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.java .... /** * Search the given candidate Advisors to find all Advisors that * can apply to the specified bean. * @param candidateAdvisors the candidate Advisors * @param beanClass the target"s bean class * @param beanName the target"s bean name * @return the List of applicable Advisors * @see ProxyCreationContext#getCurrentProxiedBeanName() */ protected ListfindAdvisorsThatCanApply( List candidateAdvisors, Class> beanClass, String beanName) { ProxyCreationContext.setCurrentProxiedBeanName(beanName); try { return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); } finally { ProxyCreationContext.setCurrentProxiedBeanName(null); } } ....
一步一步往下探
/org/springframework/aop/support/AopUtils.java .... /** * Determine the sublist of the {@code candidateAdvisors} list * that is applicable to the given class. * @param candidateAdvisors the Advisors to evaluate * @param clazz the target class * @return sublist of Advisors that can apply to an object of the given class * (may be the incoming List as-is) */ public static ListfindAdvisorsThatCanApply(List candidateAdvisors, Class> clazz) { if (candidateAdvisors.isEmpty()) { return candidateAdvisors; } List eligibleAdvisors = new ArrayList<>(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) { eligibleAdvisors.add(candidate); } } boolean hasIntroductions = !eligibleAdvisors.isEmpty(); for (Advisor candidate : candidateAdvisors) { if (candidate instanceof IntroductionAdvisor) { // already processed continue; } if (canApply(candidate, clazz, hasIntroductions)) { eligibleAdvisors.add(candidate); } } return eligibleAdvisors; } .... /** * Can the given advisor apply at all on the given class? * This is an important test as it can be used to optimize * out a advisor for a class. * @param advisor the advisor to check * @param targetClass class we"re testing * @return whether the pointcut can apply on any method */ public static boolean canApply(Advisor advisor, Class> targetClass) { return canApply(advisor, targetClass, false); } /** * Can the given advisor apply at all on the given class? * This is an important test as it can be used to optimize out a advisor for a class. * This version also takes into account introductions (for IntroductionAwareMethodMatchers). * @param advisor the advisor to check * @param targetClass class we"re testing * @param hasIntroductions whether or not the advisor chain for this bean includes * any introductions * @return whether the pointcut can apply on any method */ public static boolean canApply(Advisor advisor, Class> targetClass, boolean hasIntroductions) { if (advisor instanceof IntroductionAdvisor) { return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass); } else if (advisor instanceof PointcutAdvisor) { PointcutAdvisor pca = (PointcutAdvisor) advisor; return canApply(pca.getPointcut(), targetClass, hasIntroductions); } else { // It doesn"t have a pointcut so we assume it applies. return true; } } ....
最后定位到canApply(Pointcut pc, Class> targetClass, boolean hasIntroductions)方法
/org/springframework/aop/support/AopUtils.java /** * Can the given pointcut apply at all on the given class? *This is an important test as it can be used to optimize * out a pointcut for a class. * @param pc the static or dynamic pointcut to check * @param targetClass the class to test * @param hasIntroductions whether or not the advisor chain * for this bean includes any introductions * @return whether the pointcut can apply on any method */ public static boolean canApply(Pointcut pc, Class> targetClass, boolean hasIntroductions) { Assert.notNull(pc, "Pointcut must not be null"); if (!pc.getClassFilter().matches(targetClass)) { return false; } MethodMatcher methodMatcher = pc.getMethodMatcher(); if (methodMatcher == MethodMatcher.TRUE) { // No need to iterate the methods if we"re matching any method anyway... return true; } IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null; if (methodMatcher instanceof IntroductionAwareMethodMatcher) { introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher; } Set
> classes = new LinkedHashSet<>(); if (!Proxy.isProxyClass(targetClass)) { classes.add(ClassUtils.getUserClass(targetClass)); } classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass)); for (Class> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method method : methods) { if (introductionAwareMethodMatcher != null ? introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) : methodMatcher.matches(method, targetClass)) { return true; } } } return false; }
可以看出判斷是否是該bean合適的advisor,是通過advisor.getPointcut().getClassFilter().matches(targetClass)方法來判斷的。匹配完class以后下面還有MethodMatcher來匹配method。回想我們在配置pointcut的時候不僅僅有class的規則,也有method的規則。
當然,再深入matches方法進去的話就是pointcut的匹配語法實現了。有興趣的可以自行閱讀。
一些總結讀到這兒,Spring AOP如何掃描@Aspect配置,生成Advisor類,并匹配對應的Bean整個流程已經很清楚了。這里再總結一下:
獲取已在BeanFactory的Advisor Bean
獲取所有Object Bean,過濾出@Aspect注解修飾的Bean,并生成Advisor
遍歷上述獲取的所有Advisor,由Advisor的Pointcut ClassFilter匹配合適的Bean
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/76783.html
摘要:版本從注解開始由于在本人實際應用中使用的是注解配置,也更傾向于了解的整個實現,而不僅僅是關鍵實現。于是本篇源碼解析,將會從注解開始。那么,便從的源碼引用開始吧。的引用先從源碼中找有引用到,用來判斷是否有該注解的代碼。 版本 spring 5.0.8.BUILD-SNAPSHOT aspectjweaver 1.8.13 從注解開始 由于在本人實際應用中使用的是注解配置AOP,也更傾...
摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達式相關,就是切面如何切點。例子中,注解就是切點表達式,匹配對應的連接點,通知,指在切面的某個特定的連接點上執行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。 之前《零基礎帶你看Spring源碼——IOC控制反轉》詳細講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態代理嗎?看這篇就夠了》介紹了下...
摘要:而面向切面編程理所當然關注于切面,那么什么是切面可以理解為程序執行時的某個節點,或更具體一點,在某個方法執行之前,執行之后,返回之后等其它節點。術語一個切面,可以理解為一個切面模塊,將相關的增強內容寫進同一個切面。例如一個負責日志的切面。 AOP是什么 AOP全稱 Aspect-Oriented Programming 即面向切面編程。怎么樣,是不是感覺很熟悉?對,類似的還有面向過程編...
摘要:入門篇學習總結時間年月日星期三說明本文部分內容均來自慕課網。主要的功能是日志記錄,性能統計,安全控制,事務處理,異常處理等等。 《Spring入門篇》學習總結 時間:2017年1月18日星期三說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:https://github.com/zccodere/s...個人學習源碼:https://git...
摘要:是通過判斷當前是否匹配,只有匹配的才會創建代理。實現分析類結構從上圖類結構,我們知道其實現與類似都是通過實現接口在完成實例化后進行自動代理處理。 概述 在上一篇 重拾-Spring AOP 中我們會發現 Spring AOP 是通過類 ProxyFactoryBean 創建代理對象,其有個缺陷就是只能代理一個目標對象 bean, 當代理目標類過多時,配置文件臃腫不方便管理維護,因此 S...
閱讀 735·2023-04-25 19:28
閱讀 1396·2021-09-10 10:51
閱讀 2392·2019-08-30 15:55
閱讀 3413·2019-08-26 13:55
閱讀 3001·2019-08-26 13:24
閱讀 3330·2019-08-26 11:46
閱讀 2756·2019-08-23 17:10
閱讀 1419·2019-08-23 16:57