<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      談談Spring中的BeanPostProcessor接口(轉)

      原文:談談Spring中的BeanPostProcessor接口

      作者:特務依昂

       

      一. 前言

      ??這幾天正在復習Spring的相關內容,在了解bean的生命周期的時候,發現其中涉及到一個特殊的接口——BeanPostProcessor接口。由于網上沒有找到比較好的博客,所有最后花了好幾個小時,通過Spring的官方文檔對它做了一個大致的了解,下面就來簡單介紹一下這個接口。

      二. 正文

      2.1 BeanPostProcessor的功能

      ??有時候,我們希望Spring容器在創建bean的過程中,能夠使用我們自己定義的邏輯,對創建的bean做一些處理,或者執行一些業務。而實現方式有多種,比如自定義bean的初始化話方法等,而BeanPostProcessor接口也是用來實現類似的功能的。

      ??如果我們希望容器中創建的每一個bean,在創建的過程中可以執行一些自定義的邏輯,那么我們就可以編寫一個類,并讓他實現BeanPostProcessor接口,然后將這個類注冊到一個容器中。容器在創建bean的過程中,會優先創建實現了BeanPostProcessor接口的bean,然后,在創建其他bean的時候,會將創建的每一個bean作為參數,調用BeanPostProcessor的方法。而BeanPostProcessor接口的方法,即是由我們自己實現的。下面就來具體介紹一下BeanPostProcessor的使用。

      2.2 BeanPostProcessor的使用

      我們先看一看BeanPostProcessor接口的代碼:

      public interface BeanPostProcessor {
          // 注意這個方法名稱關鍵的是before這個單詞
          Object postProcessBeforeInitialization(Object bean, String beanName) 
              throws BeansException;
      
          // 注意這個方法名稱關鍵的是after這個單詞
          Object postProcessAfterInitialization(Object bean, String beanName) 
              throws BeansException;
      }

      ??可以看到,BeanPostProcessor接口只有兩個抽象方法,由實現這個接口的類去實現(后面簡稱這兩個方法為beforeafter),這兩個方法有著相同的參數:

      • bean:容器正在創建的那個bean的引用;
      • beanName:容器正在創建的那個bean的名稱;

      ??那這兩個方法何時執行呢?這就涉及到Spring中,bean的生命周期了。下面引用《Spring實戰》中的一張圖,這張圖表現了bean的生命周期,而Spring容器創建bean的具體過程,請參考這篇博客——簡單談談Spring的IoC

       

      ??上圖中標紅的兩個地方就是BeanPostProcessor中兩個方法的執行時機。Spring容器在創建bean時,如果容器中包含了BeanPostProcessor的實現類對象,那么就會執行這個類的這兩個方法,并將當前正在創建的bean的引用以及名稱作為參數傳遞進方法中。這也就是說,BeanPostProcessor的作用域是當前容器中的所有bean(不包括一些特殊的bean,這個后面說)。

      ??值得注意的是,我們可以在一個容器中注冊多個不同的BeanPostProcessor的實現類對象,而bean在創建的過程中,將會輪流執行這些對象實現的beforeafter方法。那執行順序如何確定呢?Spring提供了一個接口Ordered,我們可以讓BeanPostProcessor的實現類實現這個Ordered接口,并實現接口的getOrder方法。這個方法的返回值是一個int類型,Spring容器會通過這個方法的返回值,對容器中的多個BeanPostProcessor對象進行從小到大排序,然后在創建bean時依次執行它們的方法。也就是說,getOrder方法返回值越小的BeanPostProcessor對象,它的方法將越先被執行。

      2.3 一個簡單的demo

      下面就來寫一個簡單的demo,來看看BeanPostProcessor的效果。首先定義兩個普通的bean,就叫UserCar吧:

      public class User {
      
          private String name;
          private int age;
          
          // ... 省略getter和setter...
      }
      
      public class Car {
          private int speed;
          private double price;
      
          // ... 省略getter和setter...
      }

      在定義一個BeanPostProcessor的實現類,重寫接口的方法:

      public class PostBean implements BeanPostProcessor {
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) 
              throws BeansException {
              // 輸出信息,方便我們看效果
              System.out.println("before -- " + beanName);
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) 
              throws BeansException {
              // 輸出信息,方便我們看效果
              System.out.println("after -- " + beanName);
              return bean;
          }
      
      }

      我們直接使用一個Java類作為Spring的配置,就不使用xml配置文件了。配置如下,在這個配置類中,聲明了UserCar以及PostBean這三個bean的工廠方法,前兩個是普通bean,而PostBean是實現BeanPostProcessorbean

      @Configuration
      public class BeanConfig {
          // 在Spring中注冊User這個bean
          @Bean
          public User user() {
              return new User();
          }
          
          // 在Spring中注冊Car這個bean
          @Bean
          public Car car() {
              return new Car();
          }
      
          // 在Spring中注冊PostBean這個bean,這個bean實現了BeanPostProcessor接口
          @Bean
          public PostBean postBean() {
              return new PostBean();
          }
      
      }

      好,有了上面四個類,就可以開始測試了,下面是測試方法:

      @Test
      public void testConfig() {
          ApplicationContext context =
              new AnnotationConfigApplicationContext(BeanConfig.class);
      }

      上面這個方法啥也不干,就是創建一個Spring的上下文對象,也就是SpringIoC容器。這個容器將去加載BeanConfig這個類的配置,然后創建配置類中聲明的對象。在創建User和Car的過程中,就會執行BeanPostProcessor實現類的方法。我們看看執行結果:

      before -- org.springframework.context.event.internalEventListenerProcessor
      after -- org.springframework.context.event.internalEventListenerProcessor
      before -- org.springframework.context.event.internalEventListenerFactory
      after -- org.springframework.context.event.internalEventListenerFactory
      before -- car
      after -- car
      before -- user
      after -- user

      可以看到,BeanPostProcessorbefore方法和after方法都被調用了四次,最后兩次調用時,傳入的參數正是我們自己定義的Bean——UserCar。那為什么調用了四次呢,明明我們只定義了兩個普通bean。我們看上面的輸出發現,前兩次調用,傳入的beanSpring內部的組件。Spring在初始化容器的過程中,會創建一些自己定義的bean用來實現一些功能,而這些bean,也會執行我們注冊進容器中的BeanPostProcessor實現類的方法。

      2.4 使用BeanPostProcessor時容易踩的坑

      ??BeanPostProcessor這個接口,在使用的過程中,其實還有許多的限制和坑點,若不了解的話,可能會讓你對某些結果感到莫名其妙。下面我就來簡單地說一說:

      (一)BeanPostProcessor依賴的bean,不會執行BeanPostProcessor的方法

      ??當我們在BeanPostProcessor的實現類中,依賴了其他的bean,那么被依賴的bean被創建時,將不會執行它所在的BeanPostProcessor實現類實現的方法,比如我們修改PostBean的實現,如下所示:

      @Component
      public class PostBean implements BeanPostProcessor, Ordered {
          // 讓PostBean依賴User
          @Autowired
          private User user;
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) 
              throws BeansException {
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) 
              throws BeansException {
              return bean;
          }
      }

      此時,容器在創建User這個bean時,不會執行PostBean實現的兩個方法,因為由于PostBean依賴于user,所以user需要在PostBean之前創建完成,這也就意味著在user創建時,PostBean還未初始化完成,所以不會調用它的方法。

      (二)BeanPostProcessor以及依賴的bean無法使用AOP

      ??以下是Spring官方文檔中的一段話:

      Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor s nor the beans they reference directly are eligible for auto-proxying, and thus do not have aspects woven into them.

      ??上面這段話的意思大致是說,SpringAOP代理就是作為BeanPostProcessor實現的,所以我們無法對BeanPostProcessor的實現類使用AOP織入通知,也無法對BeanPostProcessor的實現類依賴的bean使用AOP織入通知SpringAOP實現我暫時還沒有研究過,所以上面的說AOP作為BeanPostProcessor實現的意思我不是特別明白,但是我們現在只需要關注BeanPostProcessor以及它依賴的bean都無法使用AOP這一點。為了驗證上面的說法,我稍微修改一下2.3中的例子,來測試一波。

      ??首先,我們修改2.3中用到的PostBeanUser這兩個類,讓PostBean依賴User這個類,同時為了輸出更加地簡單,我們將beforeafter方法中的println語句刪了:

      @Component
      public class PostBean implements BeanPostProcessor, Ordered {
          // 讓PostBean依賴User
          @Autowired
          private User user;
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) 
              throws BeansException {
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) 
              throws BeansException {
              return bean;
          }
      
          // 此方法用來測試AOP,作為切點
          public void testAOP() {
              System.out.println("Post Bean");
          }
      }
      
      @Component
      public class User {
      
          private String name;
          private int age;
          
          // ... 省略getter和setter...
          
          // 此方法用來測試AOP,用作切點
          public void testAOP() {
              System.out.println("user bean");
          }
      }

      ??然后,我們定義一個AOP的切面,在切面中將PostBeantestAOP方法作為切點,代碼如下:

      @Aspect
      public class BeanPostProcessorAspect {
          
          // 此方法織入PostBean的testAOP方法
          @Before("execution(* cn.tewuyiang.pojo.PostBean.testAOP(..))")
          public void before() {
              System.out.println("before1");
          }
      
          // 此方法織入User的testAOP方法
          @Before("execution(* cn.tewuyiang.pojo.User.testAOP(..))")
          public void before2() {
              System.out.println("before2");
          }
      }

      好,這就準備完畢,可以開始測試了。我們這次使用Spring注解掃描來配置bean以及為bean注入依賴,測試代碼如下:

      @Test
      public void testConfig() {
          ApplicationContext context =
              new AnnotationConfigApplicationContext(AutoConfig.class);
          // 獲取User這個bean,執行測試AOP的方法
          User user = context.getBean(User.class);
          user.testAOP();
          // 獲取PostBean這個bean,執行測試AOP的方法
          PostBean bean = context.getBean(PostBean.class);
          bean.testAOP();
      }
      
      輸出如下:
          user bean
          post Bean

      ??從輸出中可以看到,使用AOP織入的前置通知沒有執行,這也就驗證了上面所說的,BeanPostProcessor的實現類以及實現類依賴的bean,無法使用AOP為其織入通知。但是這個限制具體有到什么程度,我也不是很確定,因為我使用xml配置依賴,以及上面使用注解掃描兩種方式,AOP織入都沒法使用,但是我在使用@Bean這種配置方式時,被依賴的bean卻成功執行了通知。所以,關于此處提到的限制,還需要深入了解Spring容器的源碼實現才能下定論。

       

      (三)注冊BeanPostProcessor的方式以及限制

      ??我們如何將BeanPostProcessor注冊到Spring容器中?方式主要有兩種,第一種就是上面一直在用的,將其聲明在Spring的配置類或xml文件中,作為普通的bean,讓ApplicationContext對象去加載它,這樣它就被自動注冊到容器中了。而且Spring容器會對BeanPostProcessor的實現類做特殊處理,即會將它們挑選出來,在加載其他bean前,優先加載BeanPostProcessor的實現類。

      ??還有另外一種方式就是使用ConfigurableBeanFactory接口的addBeanPostProcessor方法手動添加,ApplicationContext對象中組合了一個ConfigurableBeanFactory的實現類對象。但是這種方式添加BeanPostProcessor有一些缺點。首先,我們一創建Spring容器,在配置文件中配置的單例bean就會被加載,此時addBeanPostProcessor方法還沒有執行,那我們手動添加的BeanPostProcessor也就無法作用于這些bean了,所以手動添加的BeanPostProcessor只能作用于那些延遲加載的bean,或者非單例bean

      ??還有一個就是,使用addBeanPostProcessor方式添加的BeanPostProcessor,Ordered接口的作用將失效,而是以注冊的順序執行。我們前面提過,Ordered接口用來指定多個BeanPostProcessor實現的方法的執行順序。這是Spring官方文檔中提到的:

      While the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described above), it is also possible to register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however that BeanPostProcessor s added programmatically do not respect the Ordered interface. Here it is the order of registration that dictates the order of execution. Note also that BeanPostProcessor s registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.

       

      (四)使用@Bean配置BeanPostProcessor的限制

      ??如果我們使用Java類的方式配置Spring,并使用@Bean聲明一個工廠方法返回bean實例,那么返回值的類型必須是BeanPostProcessor類型,或者等級低于BeanPostProcessor的類型。這里不好口頭描述,直接看代碼吧。以下是一個BeanPostProcessor的實現類,它實現了多個接口:

      /**
       * 此BeanPostProcessor的實現類,還實現了Ordered接口
       */
      public class PostBean implements BeanPostProcessor, Ordered {
      
          @Override
          public Object postProcessBeforeInitialization(Object bean, String beanName) 
              throws BeansException {
              System.out.println("before -- " + beanName);
              return bean;
          }
      
          @Override
          public Object postProcessAfterInitialization(Object bean, String beanName) 
              throws BeansException {
              System.out.println("after -- " + beanName);
              return bean;
          }
      
          @Override
          public int getOrder() {
              return 0;
          }
      }

      ??我們在配置類中,聲明PostBean可以有以下幾種方式:

       
      @Configuration
      public class BeanConfig {
      
          // 方式1:PostBean
          @Bean
          public PostBean postBean() {
              return new PostBean();
          }
          
          // 方式2:返回值為BeanPostProcessor
          @Bean
          public BeanPostProcessor postBean() {
              return new PostBean();
          }
          
          // 方式3:返回值為Ordered
          @Bean
          public Ordered postBean() {
              return new PostBean();
          }
      }

      以上三種方式都可以讓Spring容器創建PostBean實例對象,因為PostBean實現了BeanPostProcessorOrdered接口,所以它的對象也是這兩種類型的對象。但是需要注意,上面三種方式中,只有第一種和第二種方式,會讓Spring容器將PostBean當作BeanPostProcessor處理;而第三種方式,則會被當作一個普通Bean處理,實現BeanPostProcessor的兩個方法都不會被調用。因為在PostBean的繼承體系中,OrderedBeanPostProcessor是同級別的,Spring無法識別出這個Ordered對象,也是一個BeanPostProcessor對象;但是使用PostBean卻可以,因為PostBean類型就是BeanPostProcessor的子類型。所以,在使用@Bean聲明工廠方法返回BeanPostProcessor實現類對象時,返回值必須是BeanPostProcessor類型,或者更低級的類型Spring官方文檔中,這一部分的內容如下:

      Note that when declaring a BeanPostProcessor using an @Bean factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.

      三. 總結

      ??以上就對BeanPostProcessor的功能、使用以及需要注意的問題做了一個大致的介紹。需要注意的是,上面所提到的問題,可能根據不同的情況,會有不同的結果,因為文檔中的資料只是簡單地提了幾句,并不詳細,上面的內容大部分都是我基于官方文檔的描述,以及自己的測試得出,所以可能并不準確。還需要自己在實踐中去嘗試,或者閱讀源碼,才能徹底了解BeanPostProcessor的執行機制。

      ??以上描述若存在錯誤或不足,希望能夠提出來,因為這一部分內容,我也不太了解,所以希望有人幫忙指正

      posted @ 2024-09-06 22:43  奮斗終生  Views(197)  Comments(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲熟女片嫩草影院| 亚洲国产精品高清久久久| 美女无遮挡免费视频网站| 国产一区二区三区怡红院| 国产偷窥熟女高潮精品视频| 乱老年女人伦免费视频| 欧美亚洲熟妇一区二区三区| 蜜臀av久久国产午夜| 精品国产精品三级精品av网址| 吉川爱美一区二区三区视频| 久久精品国产亚洲av麻豆长发| 免费无码av片在线观看播放| 深夜在线观看免费av| 久久综合给合久久狠狠狠88| 午夜在线观看成人av| 池州市| 永久无码天堂网小说区| 毛片在线看免费| 最新国产精品中文字幕| 亚洲熟女乱综合一区二区| 在线观看成人永久免费网站| 新余市| japanese边做边乳喷| 日韩精品福利一区二区三区| 国产精品自在线拍国产手机版| 都匀市| 福利一区二区不卡国产| 丁香婷婷色综合激情五月| 国产在线精品一区二区三区直播| 91精品人妻中文字幕色| 成人国产精品一区二区不卡| 国产乱码精品一区二三区| 文化| 免费看视频的网站| 另类 亚洲 图片 激情 欧美| 武功县| 亚洲综合精品一区二区三区| 国产很色很黄很大爽的视频| 精品国产成人国产在线视| 国产美女遭强高潮免费| 韩国无码AV片午夜福利|