目錄java
1. findCandidateAdvisorsspring
2. buildAspectJAdvisorsexpress
2.1. 建立一個MetadataAwareAspectInstanceFactory對象數組
bean建立過程當中,在createBean步驟的resolveBeforeInstantiation中會調用beanPostProcessor處理器執行各自的postProcessBeforeInstantiation方法。在Spring在引入了AOP的狀況下,後置處理器會多出一個AnnotationAwareAspectJAutoProxyCreator。在建立bean進行到該步驟的時候,該beanPostProcessor會在postProcessBeforeInstantiation方法中進行代理類Pointcut,JointPoint,Advice,Advisor的解析和緩存。爲後續初始化bean中實現bean的代理作好準備。post
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result != null) { return result; } } } return null; }
直接來看調用ibp.postProcessBeforeInstantiation(beanClass, beanName)的實現方法, ui
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) { // 若是是普通bean,則返回beanName,若是是FactoryBean,則返回加上前綴&的&beanName Object cacheKey = getCacheKey(beanClass, beanName); // 第一次進入時,targetSourcedBeans還未緩存有對象,故會進入。 if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) { // 若是this.advisedBeans有緩存值,說明該bean已經被解析過了,直接返回null就跳過了後續的解析 if (this.advisedBeans.containsKey(cacheKey)) { return null; } // 基礎類不該該被代理 if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return null; } } // 若是用戶自定義了TargetSource,那麼直接返回用戶自定義的代理類。不然返回null,使得Spring框架能繼續執行下去。 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; }
首先getCacheKey是獲取beanName,若是是FactoryBean,那麼beanName是要加上前綴"&"。第一次進入時,if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName))確定是會進入的。若是advisedBeans有緩存值,說明該bean已經被解析過了,直接返回null就跳過了後續的解析,不然進入第二個if分支。第一個條件isInfrastructureClass()是判斷當前bean類型是不是基礎類型,若是是則不會被代理。基礎類型有Advice.class,Pointcut.class, Advisor.class, AopInfrastructureBean.class這4種。關鍵的核心代碼是在shouldSkip(beanClass, beanName)中,返回的結果用於判斷是否要跳過解析。可是執行的過程卻遠遠不是一句話能說明白的,咱們一塊兒深刻剖析shouldSkip方法。(後續的getCustomTargetSource方法是返回用戶自定義的代理類。若是沒有定義就返回null,使得Spring框架能繼續執行下去。這裏後續再也不分析~)this
protected boolean shouldSkip(Class<?> beanClass, String beanName) { // findCandidateAdvisors最終會由BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans執行 List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 找到的candidate中有beanName與AspectJPointcutAdvisor相同,也就是PointcutAdvisor的實現類,則不會進行做爲代理的嘗試。 for (Advisor advisor : candidateAdvisors) { if (advisor instanceof AspectJPointcutAdvisor && ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)) { return true; } } // 根據ORIGINAL_INSTANCE_SUFFIX判斷給定bean名稱是「原始實例」類型,若是是就跳過代理的嘗試。 return super.shouldSkip(beanClass, beanName); }
核心的步驟是findCandidateAdvisors,用於尋找和解析advisor。而後再找到的advisor們中判斷是否能夠跳過。有兩種狀況能夠被跳過:1.AspectName恰好就是這個bean的name;2.這個bean是原始實例,原始bean的後綴有「.ORIGINAL」標誌。
接下來咱們進入核心的findCandidateAdvisors方法分析:
protected List<Advisor> findCandidateAdvisors() { List<Advisor> advisors = super.findCandidateAdvisors(); // 合併全局的advisors後返回 if (this.aspectJAdvisorsBuilder != null) { advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
super.findCandidateAdvisors()會藉助BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans執行,咱們直接來看findAdvisorBeans。
public List<Advisor> findAdvisorBeans() { String[] advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 從bean工廠中尋找Advisor.class,而且是單例類型的beanName advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } if (advisorNames.length == 0) { return new ArrayList<>(); } List<Advisor> advisors = new ArrayList<>(); for (String name : advisorNames) { if (isEligibleBean(name)) { if (this.beanFactory.isCurrentlyInCreation(name)) { // 若是正在建立就跳過 } else { try { advisors.add(this.beanFactory.getBean(name, Advisor.class)); } catch (BeanCreationException ex) { Throwable rootCause = ex.getMostSpecificCause(); if (rootCause instanceof BeanCurrentlyInCreationException) { BeanCreationException bce = (BeanCreationException) rootCause; String bceBeanName = bce.getBeanName(); if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) { continue; } } throw ex; } } } } return advisors; }
首先嚐試從拿到緩存的advisors,若是沒有緩存,那麼就從bean工廠中嘗試找出從bean工廠中尋找繼承了Advisor.class接口的類,並過濾出單例類型的beanName返回。·若是項目中沒有繼承接口Advisor的bean,那麼直接返回new一個了ArrayList返回。若是有繼承Advisor接口的bean,那麼就從bean工廠中實例化出來添加到advisors list中返回上層執行後續的代碼。
下面咱們來看this.aspectJAdvisorsBuilder.buildAspectJAdvisors()的執行流程。
public List<Advisor> buildAspectJAdvisors() { // 仍是先從緩存中獲取,第一次進來確定是爲null List<String> aspectNames = this.aspectBeanNames; if (aspectNames == null) { synchronized (this) { aspectNames = this.aspectBeanNames; if (aspectNames == null) { List<Advisor> advisors = new ArrayList<>(); aspectNames = new ArrayList<>(); // 從bean工廠中拿出全部beanName String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); for (String beanName : beanNames) { // 判斷這個bean是否能夠作自動代理的可能 // 具體的判斷依據是根據org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#includePatterns中 // 添加的規則進行過濾。默認是沒有添加任何規則,也就是任何bean都有被代理的可能。 if (!isEligibleBean(beanName)) { continue; } Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // 看看這個bean的類中是否有Aspect的註解,若是 // 好比OperationAop類中就含有這樣的註解。第一次解析出註解後,會將其添加到AnnotationUtils的findAnnotationCache map中緩存 if (this.advisorFactory.isAspect(beanType)) { // 先添加到臨時list中,後邊會追加到this.aspectBeanNames中。 aspectNames.add(beanName); // 這裏其實就是看這個bean是不是單例的。若是是單例的,那麼它的代理bean也會是單例的,不然就看成是原型的。 AspectMetadata amd = new AspectMetadata(beanType, beanName); // aspect (perthis/target/cflow/cflowbelow)有這幾種使用模式,我一個都沒用過>.<!。 默認是SINGLETON if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 建立一個針對當前類型的一個單例的代理工廠 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 解析和建立對應代理工廠的advisor List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // 放入BeanFactoryAspectJAdvisorsBuilder的緩存map advisorsCache中 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { 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 ArrayList<>(); 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; }
進來首先仍是先從緩存中獲取,第一次進來確定是爲null。DCL檢查this.aspectBeanNames是否已經有緩存。檢查都經過之後,首先從bean工廠中拿出全部到beanNames中,而後遍歷這些beanNames進行aop的相關解析。isEligibleBean(beanName)是判斷這個bean是否能夠作自動代理的可能,具體的判斷依據是根據AnnotationAwareAspectJAutoProxyCreator#includePatterns中添加的規則進行過濾。默認是沒有添加任何規則,也就是任何bean都有被代理的可能。advisorFactory.isAspect(beanType)是判斷這個bean的類中是否有Aspect的註解,好比OperationAop.class類中就含有這樣的註解。第一次解析出註解後,會將其添加到AnnotationUtils的findAnnotationCache map中緩存。隨後根據PerClauseKind.SINGLETON分開處理單例與原型的advisorFactory。aspect (perthis/target/cflow/cflowbelow)有這幾種使用模式,默認是SINGLETON。咱們只分析單例的狀況,原型的大同小異留給讀者自行分析吧。重點來看這三行代碼:
//建立一個針對當前類型的一個單例的代理工廠 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 解析和建立對應代理工廠的advisor List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory); // 放入BeanFactoryAspectJAdvisorsBuilder的緩存map advisorsCache中 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors);
以OperationAop.class爲例,對象中包含的信息以下:
beanFactory = DefaultListableBeanFactory name = "operationAop" aspectMetadata = {AspectMetadata@3331} 其中aspectMetadata: aspectName = "operationAop" aspectClass = "class com.Hodey.analyseaop.aop.OperationAop" ajType = "com.Hodey.analyseaop.aop.OperationAop" perClausePointcut = "Pointcut.TRUE"
this.advisorFactory.getAdvisors(factory) 解析和建立對一個代理工廠的advisor
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); validate(aspectClass); // 包裝MetadataAwareAspectInstanceFactory一下,使得它只能被實例化1次 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new ArrayList<>(); for (Method method : getAdvisorMethods(aspectClass)) { // 嘗試解析每一個方法,找到方法對應的切點和通知 Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // 代理默認不是懶加載的,這裏的分支通常不會走進 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 若是屬性也有須要代理的,那麼還須要尋找到屬性的advisor for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
建立LazySingletonAspectInstanceFactoryDecorator的目的僅僅是包裝傳入的MetadataAwareAspectInstanceFactory,使得它是單例對象。getAdvisorMethods(Class<?> aspectClass)是獲取傳入class及其繼承類的全部方法,以OperationAop.class爲例獲取的方法集以下:
0 = {Method@3112} "public int com.Hodey.analyseaop.aop.OperationAop.doAround(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable" 1 = {Method@3512} "public void com.Hodey.analyseaop.aop.OperationAop.doBefore()" 2 = {Method@3513} "public void com.Hodey.analyseaop.aop.OperationAop.doAfter()" 3 = {Method@3514} "protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException" 4 = {Method@3515} "public boolean java.lang.Object.equals(java.lang.Object)" 5 = {Method@3516} "protected void java.lang.Object.finalize() throws java.lang.Throwable" 6 = {Method@3517} "public final native java.lang.Class java.lang.Object.getClass()" 7 = {Method@3518} "public native int java.lang.Object.hashCode()" 8 = {Method@3519} "public final native void java.lang.Object.notify()" 9 = {Method@3520} "public final native void java.lang.Object.notifyAll()" 10 = {Method@3521} "private static native void java.lang.Object.registerNatives()" 11 = {Method@3522} "public java.lang.String java.lang.Object.toString()" 12 = {Method@3523} "public final void java.lang.Object.wait() throws java.lang.InterruptedException" 13 = {Method@3524} "public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException" 14 = {Method@3525} "public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException"
而後對這些方法嘗試獲取它的advisor。
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 得到切點 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 找到了切點,就將切點、鏈接點、通知等aop參數封裝成InstantiationModelAwarePointcutAdvisorImpl對象向上返回。 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
首先要去找到切點。可是進入getPointCut後從方法名上看爲何第一步竟然是去找對應方法上的註解呢?彆着急,咱們跟着流程一步步分析:
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) { AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]); // 由於aspectJAnnotation已經封裝了切點pointcut的信息,因此直接拿出來設置到ajexp對象上便可。 ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); if (this.beanFactory != null) { ajexp.setBeanFactory(this.beanFactory); } return ajexp; } protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) { for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) { AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz); if (foundAnnotation != null) { return foundAnnotation; } } return null; }
首先來看findAspectJAnnotationOnMethod方法。ASPECTJ_ANNOTATION_CLASSES中存放的是[Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class]數組。在for循環中會依次對方法上的註解和ASPECTJ_ANNOTATION_CLASSES中元素進行比對,找出對應註解的AspectJAnnotation。以OperationAop中的beforeAdd方法爲例。該方法上有註解@Around("addCut()"),那麼就會經過findAnnotation找到該註解, 在返回的結果中已經封裝了切點位置,通知類型,切點的表達式和參數名等信息:
annotation = "@org.aspectj.lang.annotation.Around(argNames=, value=doCut())" annotationType = "AtAround" pointcutExpression = "doCut()" argumentNames = ""
而後建立AspectJExpressionPointcut ajexp對象,將findAnnotation找到的結果中封裝的相關切點信息設置到ajexp對象上,隨後將這個帶有切點信息的對象返回上層getAdvisor方法,隨後在該方法中將切點、鏈接點、通知等aop參數封裝成InstantiationModelAwarePointcutAdvisorImpl對象向上返回到getAdvisors方法,隨後將返回值添加到List<Advisor> advisors中。等到遍歷完全部方法,將advisors一路返回,最終返回到buildAspectJAdvisors中在advisorsCache緩存好advisors對象再一路返回到shouldSkip,本步驟執行完成。
值得一提的是再InstantiationModelAwarePointcutAdvisorImpl對象建立過程當中會調用instantiateAdvice實例化通知(advice),這個過程當中會調用getAdvice方法建立通知。
public Advice getAdvice(Method candidateAdviceMethod是, AspectJExpressionPointcut expressionPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) { Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass); AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation == null) { return null; } AbstractAspectJAdvice springAdvice; //根據不一樣的註解,就會走不一樣的分支建立各自的通知對象 switch (aspectJAnnotation.getAnnotationType()) { case AtPointcut: return null; case AtAround: springAdvice = new AspectJAroundAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtBefore: springAdvice = new AspectJMethodBeforeAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfter: springAdvice = new AspectJAfterAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); break; case AtAfterReturning: springAdvice = new AspectJAfterReturningAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterReturningAnnotation.returning())) { springAdvice.setReturningName(afterReturningAnnotation.returning()); } break; case AtAfterThrowing: springAdvice = new AspectJAfterThrowingAdvice( candidateAdviceMethod, expressionPointcut, aspectInstanceFactory); AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation(); if (StringUtils.hasText(afterThrowingAnnotation.throwing())) { springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); } break; default: throw new UnsupportedOperationException( "Unsupported advice type on method: " + candidateAdviceMethod); } // 最後再手動設置一些屬性。 springAdvice.setAspectName(aspectName); springAdvice.setDeclarationOrder(declarationOrder); String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod); if (argNames != null) { springAdvice.setArgumentNamesFromStringArray(argNames); } // 綁定參數 // 若是第一個參數類型是JoinPoint或procedure edingjoinpoint,那麼將在那個位置傳遞一個JoinPoint (procedure edingjoinpoint for around advice)。 // 若是第一個參數是JoinPoint.StaticPart類型的,那麼將會在對應位置綁定上一個JoinPoint.StaticPart參數。 // 剩下的參數必須由給定鏈接點上的切入點求值綁定,以此獲得一個從參數名到值的映射。咱們須要計算須要將哪一個通知參數綁定到哪一個參數名。肯定這種綁定有多種策略,它們以責任鏈的形式排列。 springAdvice.calculateArgumentBindings(); return springAdvice; }
以建立建立OperationAop doBefore()方法的通知爲例,傳入的candidateAdviceMethod是切點方法。findAspectJAnnotationOnMethod(candidateAdviceMethod)獲得的aspectJAnnotation對象結果以下:
annotation = "@org.aspectj.lang.annotation.Before(argNames=, value=doCut())" annotationType = "AtBefore" pointcutExpression = "doCut()" argumentNames = ""
而後根據下面不一樣的分支建立不一樣的Advice。springAdvice.calculateArgumentBindings();是進行參數綁定。(過程複雜,暫時沒看)
到此,Spring AOP的元數據解析過程基本上算是分析完畢了。下一篇文章將着重介紹AOP類的建立過程,敬請期待!
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HodayDouble { }
@Aspect @Component public class OperationAop { @Pointcut("@annotation(com.Hodey.analyseaop.anno.HodayDouble)") public void doCut(){} @Around("doCut()") public int doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object[] args = proceedingJoinPoint.getArgs(); int i = (int) args[0]; int j = (int) args[1]; System.out.println("入參:i:" + i); System.out.println("入參:j:" + j); System.out.println("before around"); int result = (int) proceedingJoinPoint.proceed(); System.out.println("after around"); System.out.println("原始結果:" + result); result = result * 2; System.out.println("代理結果:" + result); return result; } @Before("doCut()") public void doBefore(){ System.out.println("@Before print"); } @After("doCut()") public void doAfter(){ System.out.println("@After print"); } }
@Service("service") public class OperationService { @Autowired private OperationUtil ops; public int add(int i , int j){ return ops.add(i,j); } }
@Component public class OperationUtil { @HodayDouble public int add(int i, int j){ int result = i + j; return result; } }
@ComponentScan("com.Hodey") @EnableAutoConfiguration public class AnalyseAopApplication { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnalyseAopApplication.class); OperationService opsService = (OperationService) ctx.getBean("service"); int result = opsService.add(2,3); System.out.println("最終結果:" + result); } }
執行結果:
入參:i:2 入參:j:3 before around @Before print after around 原始結果:5 代理結果:10 @After print 最終結果:10