在上文Spring Aop之Target Source詳解中,咱們講解了Spring是如何經過封裝Target Source
來達到對最終獲取的目標bean進行封裝的目的。其中咱們講解到,Spring Aop對目標bean進行代理是經過AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()
進行的,Spring Aop的代理主要分爲三個步驟:獲取全部的Advisor,過濾可應用到當前bean的Adivsor和使用Advisor爲當前bean生成代理對象。本文主要對這三步中的第一步獲取全部的Advisor進行講解。java
首先咱們看看postProcessAfterInitialization()
方法的實現:express
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException { if (bean != null) { // 獲取當前bean的key:若是beanName不爲空,則以beanName爲key,若是爲FactoryBean類型, // 前面還會添加&符號,若是beanName爲空,則以當前bean對應的class爲key Object cacheKey = getCacheKey(bean.getClass(), beanName); // 判斷當前bean是否正在被代理,若是正在被代理則不進行封裝 if (!this.earlyProxyReferences.contains(cacheKey)) { // 對當前bean進行封裝 return wrapIfNecessary(bean, beanName, cacheKey); } } return bean; }
從上述代碼能夠看出,對目標bean的封裝是主要是經過wrapIfNecessary()
方法進行的,該方法就是Spring對目標bean進行代理的骨架方法。以下是該方法的實現:數組
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { // 判斷當前bean是否在TargetSource緩存中存在,若是存在,則直接返回當前bean。這裏進行如此判斷的 // 緣由是在上文中,咱們講解了如何經過本身聲明的TargetSource進行目標bean的封裝,在封裝以後其實 // 就已經對封裝以後的bean進行了代理,而且添加到了targetSourcedBeans緩存中。於是這裏判斷獲得 // 當前緩存中已經存在當前bean,則說明該bean已經被代理過,這樣就能夠直接返回當前bean。 if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } // 這裏advisedBeans緩存了已經進行了代理的bean,若是緩存中存在,則能夠直接返回 if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } // 這裏isInfrastructureClass()用於判斷當前bean是否爲Spring系統自帶的bean,自帶的bean是 // 不用進行代理的;shouldSkip()則用於判斷當前bean是否應該被略過 if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { // 對當前bean進行緩存 this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; } // 獲取當前bean的Advices和Advisors Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null); if (specificInterceptors != DO_NOT_PROXY) { // 對當前bean的代理狀態進行緩存 this.advisedBeans.put(cacheKey, Boolean.TRUE); // 根據獲取到的Advices和Advisors爲當前bean生成代理對象 Object proxy = createProxy( bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean)); // 緩存生成的代理bean的類型,而且返回生成的代理bean this.proxyTypes.put(cacheKey, proxy.getClass()); return proxy; } this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
在上述骨架方法中,Spring主要進行了三件事:緩存
關於上述骨架方法,這裏須要說明兩個點:post
shouldSkip()
方法中對當前bean判斷是否應該略過期,其主要作了兩件事:a. 爲當前bean生成須要代理的Advisors;b. 判斷生成的Advisor是否爲AspectJPointcutAdvisor
類型。於是實際上判斷略過的過程就是判斷是否爲AspectJPointcutAdvisor
,判斷這個類的緣由在於Spring Aop的切面和切點的生成也能夠經過在xml文件中使用<aop:config/>
標籤進行。這個標籤最終解析獲得的Adivsor類型就是``AspectJPointcutAdvisor類型的,由於其在解析
aop:config/的時候就已經生成了Advisor,於是這裏須要對這種類型的Advisor進行略過。這裏
aop:config/`也是一種自定義標籤,關於其解析過程,讀者能夠參照本人前面的博文自行閱讀器源碼;getAdvicesAndAdvisorsForBean()
方法就其名稱而言是獲取Advisors和Advices,但實際上其返回值是一個Advisor的數組。Spring Aop在爲目標bean獲取須要進行代理的切面邏輯的時候最終獲得的是Advisor,這裏Advice表示的是每一個切面邏輯中使用@Before
、@After
和@Around
等須要織入的代理方法。由於每一個代理方法都表示一個Advice,而且每一個代理方法最終都會生成一個Advisor,於是Advice和Advisor就本質而言其實沒有太大的區別。Advice表示須要織入的切面邏輯,而Advisor則表示將切面邏輯進行封裝以後的織入者。 雖然在shouldSkip()
方法中會爲當前bean生成Advisor,可是在getAdvicesAndAdvisorsForBean()
中也仍是會獲取一次,只不過在第一次生成的時候會將獲得的Advisor都進行緩存,於是第二次獲取時能夠直接從緩存中獲取。咱們這裏仍是以getAdvicesAndAdvisorsForBean()
方法爲準來進行講解。以下是該方法的源碼:優化
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) { // 爲目標bean生成Advisor List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName); if (advisors.isEmpty()) { return DO_NOT_PROXY; } return advisors.toArray(); }
咱們繼續看findEligibleAdvisors()
方法:ui
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) { // 將當前系統中全部的切面類的切面邏輯進行封裝,從而獲得目標Advisor List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 對獲取到的全部Advisor進行判斷,看其切面定義是否能夠應用到當前bean,從而獲得最終須要應用的Advisor List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName); // 提供的hook方法,用於對目標Advisor進行擴展 extendAdvisors(eligibleAdvisors); if (!eligibleAdvisors.isEmpty()) { // 對須要代理的Advisor按照必定的規則進行排序 eligibleAdvisors = sortAdvisors(eligibleAdvisors); } return eligibleAdvisors; }
在上述方法中,Spring Aop首先獲取到了系統中全部的切面邏輯,並將其封裝爲了Advisor對象,而後經過遍歷Advisor判斷哪些Advisor是能夠應用到當前bean的,最後將須要織入的Advisor返回。這裏咱們看看findCandidateAdvisors()
的源碼:this
protected List<Advisor> findCandidateAdvisors() { // 找到系統中實現了Advisor接口的bean List<Advisor> advisors = super.findCandidateAdvisors(); if (this.aspectJAdvisorsBuilder != null) { // 找到系統中使用@Aspect標註的bean,而且找到該bean中使用@Before,@After等標註的方法, // 將這些方法封裝爲一個個Advisor advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); } return advisors; }
能夠看到,findCandidateAdvisors()
主要是經過兩種方式獲取切面邏輯,一種是在系統中找到實現了Advisor接口的全部類,另外一種是在找到系統中使用@Aspect
標註的類,並將其切面邏輯封裝爲Advisor,這兩種Advisor都有多是咱們須要進行織入的切面邏輯。這裏super.findCandidateAdvisors()
方法最終調用的是BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()
方法,咱們首先看看該方法的實現:.net
public List<Advisor> findAdvisorBeans() { String[] advisorNames = null; synchronized (this) { advisorNames = this.cachedAdvisorBeanNames; if (advisorNames == null) { // 獲取當前BeanFactory中全部實現了Advisor接口的bean的名稱 advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Advisor.class, true, false); this.cachedAdvisorBeanNames = advisorNames; } } if (advisorNames.length == 0) { return new LinkedList<>(); } // 對獲取到的實現Advisor接口的bean的名稱進行遍歷 List<Advisor> advisors = new LinkedList<>(); for (String name : advisorNames) { // isEligibleBean()是提供的一個hook方法,用於子類對Advisor進行過濾,這裏默認返回值都是true if (isEligibleBean(name)) { // 若是當前bean還在建立過程當中,則略過,其建立完成以後會爲其判斷是否須要織入切面邏輯 if (this.beanFactory.isCurrentlyInCreation(name)) { if (logger.isDebugEnabled()) { logger.debug("Skipping currently created advisor '" + name + "'"); } } else { try { // 將當前bean添加到結果中 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)) { if (logger.isDebugEnabled()) { logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + ex.getMessage()); } continue; } } throw ex; } } } } return advisors; }
這裏findAdvisorBeans()
方法邏輯其實很是簡單,其主要是在BeanFactory中找打實現了Advisor接口的類,而後經過hook方法判斷子類是否須要對Advisor進行過濾,最後將過濾以後的Advisor返回。debug
接下來咱們看看BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()
的實現:
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<>(); aspectNames = new LinkedList<>(); // 獲取當前BeanFactory中全部的bean String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this.beanFactory, Object.class, true, false); // 對獲取到的全部bean進行循環遍歷 for (String beanName : beanNames) { // 判斷當前bean是否爲子類定製的須要過濾的bean if (!isEligibleBean(beanName)) { continue; } // 獲取當前遍歷的bean的Class類型 Class<?> beanType = this.beanFactory.getType(beanName); if (beanType == null) { continue; } // 判斷當前bean是否使用了@Aspect註解進行標註 if (this.advisorFactory.isAspect(beanType)) { aspectNames.add(beanName); // 對於使用了@Aspect註解標註的bean,將其封裝爲一個AspectMetadata類型。 // 這裏在封裝的過程當中會解析@Aspect註解上的參數指定的切面類型,如perthis // 和pertarget等。這些被解析的註解都會被封裝到其perClausePointcut屬性中 AspectMetadata amd = new AspectMetadata(beanType, beanName); // 判斷@Aspect註解中標註的是否爲singleton類型,默認的切面類都是singleton // 類型 if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) { // 將BeanFactory和當前bean封裝爲MetadataAwareAspect- // InstanceFactory對象,這裏會再次將@Aspect註解中的參數都封裝 // 爲一個AspectMetadata,而且保存在該factory中 MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName); // 經過封裝的bean獲取其Advice,如@Before,@After等等,而且將這些 // Advice都解析而且封裝爲一個個的Advisor List<Advisor> classAdvisors this.advisorFactory.getAdvisors(factory); // 若是切面類是singleton類型,則將解析獲得的Advisor進行緩存, // 不然將當前的factory進行緩存,以便再次獲取時能夠經過factory直接獲取 if (this.beanFactory.isSingleton(beanName)) { this.advisorsCache.put(beanName, classAdvisors); } else { this.aspectFactoryCache.put(beanName, factory); } advisors.addAll(classAdvisors); } else { // 若是@Aspect註解標註的是perthis和pertarget類型,說明當前切面 // 不多是單例的,於是這裏判斷其若是是單例的則拋出異常 if (this.beanFactory.isSingleton(beanName)) { throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect " + "instantiation model is not singleton"); } // 將當前BeanFactory和切面bean封裝爲一個多例類型的Factory MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName); // 對當前bean和factory進行緩存 this.aspectFactoryCache.put(beanName, factory); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } } this.aspectBeanNames = aspectNames; return advisors; } } } if (aspectNames.isEmpty()) { return Collections.emptyList(); } // 經過全部的aspectNames在緩存中獲取切面對應的Advisor,這裏若是是單例的,則直接從advisorsCache // 獲取,若是是多例類型的,則經過MetadataAwareAspectInstanceFactory當即生成一個 List<Advisor> advisors = new LinkedList<>(); for (String aspectName : aspectNames) { List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName); // 若是是單例的Advisor bean,則直接添加到返回值列表中 if (cachedAdvisors != null) { advisors.addAll(cachedAdvisors); } else { // 若是是多例的Advisor bean,則經過MetadataAwareAspectInstanceFactory生成 MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName); advisors.addAll(this.advisorFactory.getAdvisors(factory)); } } return advisors; }
對於經過@Aspect註解獲取切面邏輯的方法,這裏的邏輯也比較簡單,Spring首先會過濾獲得BeanFactory中全部標註有@Aspect的類,而後對該註解參數進行解析,判斷其環繞的目標bean是單例的仍是多例的。若是是單例的,則直接緩存到advisorsCache中;若是是多例的,則將生成Advisor的factory進行緩存,以便每次獲取時都經過factory獲取一個新的Advisor。上述方法中主要是對@Aspect註解進行了解析,咱們前面講過,Spring Aop的Advisor對應的是Advice,而每一個Advice都是對應的一個@Before或者@After等標註方法的切面邏輯,這裏對這些切面邏輯的解析過程就在上述的advisorFactory.getAdvisors(factory)
方法調用中。這裏咱們看看該方法的實現:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) { // 獲取當前切面類的Class類型 Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 獲取當前切面bean的名稱 String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName(); // 對當前切面bean進行校驗,主要是判斷其切點是否爲perflow或者是percflowbelow,Spring暫時不支持 // 這兩種類型的切點 validate(aspectClass); // 將當前aspectInstanceFactory進行封裝,這裏LazySingletonAspectInstanceFactoryDecorator // 使用裝飾器模式,主要是對獲取到的切面實例進行了緩存,保證每次獲取到的都是同一個切面實例 MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory); List<Advisor> advisors = new LinkedList<>(); // 這裏getAdvisorMethods()會獲取全部的沒有使用@Pointcut註解標註的方法,而後對其進行遍歷 for (Method method : getAdvisorMethods(aspectClass)) { // 判斷當前方法是否標註有@Before,@After或@Around等註解,若是標註了,則將其封裝爲一個Advisor Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName); if (advisor != null) { advisors.add(advisor); } } // 這裏的isLazilyInstantiated()方法判斷的是當前bean是否應該被延遲初始化,其主要是判斷當前 // 切面類是否爲perthis,pertarget或pertypewithiin等聲明的切面。由於這些類型所環繞的目標bean // 都是多例的,於是須要在運行時動態判斷目標bean是否須要環繞當前的切面邏輯 if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) { // 若是Advisor不爲空,而且是須要延遲初始化的bean,則在第0位位置添加一個同步加強器, // 該同步加強器實際上就是一個BeforeAspect的Advisor Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory); advisors.add(0, instantiationAdvisor); } // 判斷屬性上是否包含有@DeclareParents註解標註的須要新添加的屬性,若是有,則將其封裝爲一個Advisor for (Field field : aspectClass.getDeclaredFields()) { Advisor advisor = getDeclareParentsAdvisor(field); if (advisor != null) { advisors.add(advisor); } } return advisors; }
在上述getAdvisors()
方法中,Spring會遍歷當前切面類全部的方法,包括父類和父接口的方法,找到其中沒有使用@Pointcut
註解標註的方法,而後對找到的方法進行遍歷,將其封裝爲一個Advisor。這裏咱們繼續看封裝爲Advisor的方法:
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrderInAspect, String aspectName) { // 校驗當前切面類是否使用了perflow或者percflowbelow標識的切點,Spring暫不支持這兩種切點 validate(aspectInstanceFactory.getAspectMetadata().getAspectClass()); // 獲取當前方法中@Before,@After或者@Around等標註的註解,而且獲取該註解的值,將其 // 封裝爲一個AspectJExpressionPointcut對象 AspectJExpressionPointcut expressionPointcut = getPointcut( candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass()); if (expressionPointcut == null) { return null; } // 將獲取到的切點,切點方法等信息封裝爲一個Advisor對象,也就是說當前Advisor包含有全部 // 當前切面進行環繞所須要的信息 return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName); }
到這裏Spring纔將@Before,@After或@Around標註的方法封裝爲了一個Advisor對象。須要說明的是,這裏封裝成的Advisor對象只是一個半成品。所謂的半成品指的是此時其並無對切點表達式進行解析,其還只是使用一個字符串保存在AspectJExpressionPointcut對象中,只有在真正使用當前Advice邏輯進行目標bean的環繞的時候纔會對其進行解析。
本文主要講解了Spring是如何獲取全部的Advisor的,即首先獲取BeanFactory中全部實現了Advisor接口的bean,而後獲取BeanFactory中全部標註了@Aspect註解的bean,解析該bean中的全部的切面邏輯,而且封裝爲一個個Advisor,這兩種方式獲得的Advisor都有多是最終會應用到目標bean上的切面邏輯。須要注意的是,這裏獲取到的Advisor並無對切點表達式進行解析,實際的解析過程是在判斷當前bean是否能夠應用到目標bean時進行的。這也是一個小小的優化,由於解析切點表達式的過程是一個比較複雜的過程。