日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Aop之Advisor解析

發布時間:2023/11/29 javascript 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Aop之Advisor解析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

2019獨角獸企業重金招聘Python工程師標準>>>

???????在上文Spring Aop之Target Source詳解中,我們講解了Spring是如何通過封裝Target Source來達到對最終獲取的目標bean進行封裝的目的。其中我們講解到,Spring Aop對目標bean進行代理是通過AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization()進行的,Spring Aop的代理主要分為三個步驟:獲取所有的Advisor,過濾可應用到當前bean的Adivsor和使用Advisor為當前bean生成代理對象。本文主要對這三步中的第一步獲取所有的Advisor進行講解。

1. 骨架方法

???????首先我們看看postProcessAfterInitialization()方法的實現:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {if (bean != null) {// 獲取當前bean的key:如果beanName不為空,則以beanName為key,如果為FactoryBean類型,// 前面還會添加&符號,如果beanName為空,則以當前bean對應的class為keyObject 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和AdvisorsObject[] 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的類型,并且返回生成的代理beanthis.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean; }

???????在上述骨架方法中,Spring主要進行了三件事:

  • 判斷當前bean是否已經生成過代理對象,或者是否是應該被略過的對象,是則直接返回,否則進行下一步;
  • 獲取當前bean的Advisors和Advices,如果當前bean不需要代理,則返回DO_NOT_PROXY;
  • 通過生成的Advisors和Advices為目標bean生成代理對象。

???????關于上述骨架方法,這里需要說明兩個點:

  • 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則表示將切面邏輯進行封裝之后的織入者。

2. 切面的生成

???????雖然在shouldSkip()方法中會為當前bean生成Advisor,但是在getAdvicesAndAdvisorsForBean()中也還是會獲取一次,只不過在第一次生成的時候會將得到的Advisor都進行緩存,因而第二次獲取時可以直接從緩存中獲取。我們這里還是以getAdvicesAndAdvisorsForBean()方法為準來進行講解。如下是該方法的源碼:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {// 為目標bean生成AdvisorList<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray(); }

???????我們繼續看findEligibleAdvisors()方法:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {// 將當前系統中所有的切面類的切面邏輯進行封裝,從而得到目標AdvisorList<Advisor> candidateAdvisors = findCandidateAdvisors();// 對獲取到的所有Advisor進行判斷,看其切面定義是否可以應用到當前bean,從而得到最終需要應用的AdvisorList<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()的源碼:

protected List<Advisor> findCandidateAdvisors() {// 找到系統中實現了Advisor接口的beanList<Advisor> advisors = super.findCandidateAdvisors();if (this.aspectJAdvisorsBuilder != null) {// 找到系統中使用@Aspect標注的bean,并且找到該bean中使用@Before,@After等標注的方法,// 將這些方法封裝為一個個Advisoradvisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());}return advisors; }

???????可以看到,findCandidateAdvisors()主要是通過兩種方式獲取切面邏輯,一種是在系統中找到實現了Advisor接口的所有類,另一種是在找到系統中使用@Aspect標注的類,并將其切面邏輯封裝為Advisor,這兩種Advisor都有可能是我們需要進行織入的切面邏輯。這里super.findCandidateAdvisors()方法最終調用的是BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans()方法,我們首先看看該方法的實現:

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進行過濾,這里默認返回值都是trueif (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返回。

???????接下來我們看看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中所有的beanString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);// 對獲取到的所有bean進行循環遍歷for (String beanName : beanNames) {// 判斷當前bean是否為子類定制的需要過濾的beanif (!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都解析并且封裝為一個個的AdvisorList<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封裝為一個多例類型的FactoryMetadataAwareAspectInstanceFactory 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等注解,如果標注了,則將其封裝為一個AdvisorAdvisor 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的AdvisorAdvisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);advisors.add(0, instantiationAdvisor);}// 判斷屬性上是否包含有@DeclareParents注解標注的需要新添加的屬性,如果有,則將其封裝為一個Advisorfor (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的環繞的時候才會對其進行解析。

3. 小結

???????本文主要講解了Spring是如何獲取所有的Advisor的,即首先獲取BeanFactory中所有實現了Advisor接口的bean,然后獲取BeanFactory中所有標注了@Aspect注解的bean,解析該bean中的所有的切面邏輯,并且封裝為一個個Advisor,這兩種方式得到的Advisor都有可能是最終會應用到目標bean上的切面邏輯。需要注意的是,這里獲取到的Advisor并沒有對切點表達式進行解析,實際的解析過程是在判斷當前bean是否可以應用到目標bean時進行的。這也是一個小小的優化,因為解析切點表達式的過程是一個比較復雜的過程。

轉載于:https://my.oschina.net/zhangxufeng/blog/1929863

總結

以上是生活随笔為你收集整理的Spring Aop之Advisor解析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 天堂一区二区三区四区 | 亚洲三级a | 国产精品www | 欧美激情一级精品国产 | 午夜精品久久久久久久久久久久久 | 啄木乌欧美一区二区三区 | 欧美精品一区二区蜜桃 | 青青草十七色 | 久久无码视频网站 | 丁香花高清在线 | 欧美日韩免费做爰视频 | 日本人和亚洲人zjzjhd | 综合久久一区 | 少妇高潮一区二区三区99 | av漫画在线观看 | 精品国产鲁一鲁一区二区三区 | 国产精品国产自产拍高清av | 一区二区国产视频 | 调教撅屁股啪调教打臀缝av | 欧美色图久久 | 国产精品无码一区二区三 | 欧美视频第一页 | 美女热逼 | 欧美大片一区 | 国产精品30p| 欧美hdse| 亚洲欧洲一区二区在线观看 | 天堂在线资源库 | 男女操操视频 | 欧美一区二区三区四区在线观看 | 国产成人精品一二三区 | 黄色福利社 | 视色网| 尤物视频在线看 | 写真福利片hd在线播放 | 欧美日韩 一区二区三区 | 欧美第一页在线 | 在线观看国产91 | 亚洲精品资源 | 日韩色吧 | 亚洲乱码日产精品bd在线观看 | 黄色不卡av| 66av欧美 | 国产网站入口 | 亚洲美女黄色片 | 欧美视频亚洲视频 | 欧美精品大片 | 嫩草研究院在线观看 | 波多野结衣一区二区三区 | 日韩免费黄色片 | 国产又粗又猛又爽69xx | 毛片av网站 | 国产精品免费观看视频 | 色婷婷av一区二区 | 伊人99在线 | 天天想你免费观看完整版高清电影 | 在线视频中文字幕 | 黄色免费在线网址 | 日韩一区在线免费观看 | 亚洲专区在线视频 | 毛片大全免费 | 一级黄色大片网站 | 白嫩日本少妇做爰 | 国产av无码专区亚洲av | 日本午夜一区二区三区 | 97超碰人人看 | 国产区在线观看 | 成人免费高清在线播放 | 狠狠操夜夜爽 | xxxx黄色| 国产精品9999 | 一级片免费网站 | 涩涩在线看 | 亚洲美女免费视频 | 91av爱爱 | 国产伦精品一区二区三区高清 | 波多野结衣精品在线 | 国产婷婷一区二区三区久久 | 日本一区二区不卡视频 | 黄a视频 | 手机成人免费视频 | 国产做爰免费视频观看 | 成人动漫av | 亚洲 小说区 图片区 | 无码专区久久综合久中文字幕 | 国产a级淫片 | 国产熟妇与子伦hd | 日本不卡一区二区三区视频 | 国产综合无码一区二区色蜜蜜 | 久久成年人 | 人妻少妇偷人精品无码 | 一区二区三区福利视频 | 美国色视频| 男女免费视频 | jvid乐乐| 精品欧美一区二区三区在线观看 | 成人爽爽爽 | 玩偶姐姐在线看 | 国产综合久久久 |