Spring高級進階:BeanFactoryPostProcessor


BeanFactoryPostProcessor是實現spring容器功能擴展的重要接口,例如修改bean屬性值,實現bean動態代理等。很多框架都是通過此接口實現對spring容器的擴展,例如mybatis與spring集成時,只定義了mapper接口,無實現類,但spring卻可以完成自動注入,是不是很神奇? 本文將通過簡單的例子,展現BeanFactoryPostProcessor的擴展能力。

 一、bean生命周期簡述

Spring Bean生命周期比較復雜,在此簡化一下,如下圖。

步驟1、 豆子工廠(BeanFactory)從xml文件、java配置或注解配置中讀取“各種豆子的生產方法說明(BeanDefinition)”。

步驟2、 這些豆子分為“特殊豆子(實現spring指定的某些接口)”和“普通豆子”,  豆子工廠先生產出這些特殊豆子。

步驟3和4、 特殊豆子調用特定接口(例如BeanFactoryPostProcessor接口),可以對豆子工廠(BeanFactory)進行修改,或添加一些新豆子生產方法(即注冊新的BeanDefinition到BeanFactory中)。

步驟5、豆子工廠(BeanFactory)執行getBean方法生產其他的普通裸豆子。(調用類的構造方法,或FactoryBean的getObject方法,以及@Bean注解的方法)

步驟6、設置豆子的依賴關系以及屬性值。

步驟7、調用豆子的@PostConstruct指定的方法

步驟8、調用豆子的InitializingBean接口方法

步驟9、調用豆子的initMethod指定的方法。

總結上述過程, 我們可以得到以下執行順序 :  BeanFactoryPostProcessor ---> 普通Bean構造方法 ---> 設置依賴或屬性 ---> @PostConstruct ---> InitializingBean ---> initMethod

二、BeanFactoryPostProcessor  代碼例子

BenzCar類(奔馳汽車類)有成員屬性Engine(發動機), Engine是接口,無具體的實現類。本代碼例子,通過BeanFactoryPostProcessor ,FactoryBean,動態代理三項技術實現給BenzCar裝配上Engine。

首先是 SpringBoot的 App類,如下:

 1 @SpringBootApplication
 2 public class App {
 3 
 4     public static void main(String[] args) {
 5         SpringApplication.run(App.class, args);
 6         try {
 7             System.in.read();
 8         } catch (IOException e) {
 9             e.printStackTrace();
10         }
11     }
12     
13     @Bean(initMethod="start")
14     BenzCar benzCar(Engine engine){
15         BenzCar car = new BenzCar();
16         car.engine = engine;
17         return car ;
18     }
19 }

從上面第14行代碼可以知道 benzCar 依賴 Engine對象,

以下是BenzCar 代碼:

public class BenzCar implements InitializingBean {
    
    Engine engine;
    
    public BenzCar(){
        System.out.println("BenzCar Constructor");
        if(engine==null){
            System.out.println("BenzCar's engine not setting");
        }else{
            System.out.println("BenzCar's engine installed");
        }
    }
    
    void start(){
        System.out.println("BenzCar start");
        engine.fire();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("BenzCar initializingBean after propertieSet");
        if(engine==null){
            System.out.println("BenzCar's engine not setting, in initializingBean ");
        }else{
            System.out.println("BenzCar's engine installed, in initializingBean");
            engine.fire();
        }
    }
    
    @PostConstruct
    public void postConstruct(){
        System.out.println("BenzCar postConstruct");
        if(engine==null){
            System.out.println("BenzCar's engine not setting, in postConstruct");
        }else{
            System.out.println("BenzCar's engine installed, in postConstruct");
        }
    }

}

BenzCar類中有一個Engine對象成員, 在start方法中調用Engine的fire方法。

Engine接口代碼如下:

public interface Engine {
    void fire();
}

Engine是一個接口,一般情況下,需要在App類中配置一個Engine的實現類bean才行,否則因為缺少Engine實例,spring啟動時會報錯。通過FactoryBean和動態代理,可以生成Engine接口的代理對象;結合BeanFactoryPostProcessor 接口,將FactoryBean動態添加到BeanFactory中,即可以給BenzCar配置上Engine接口代理對象。

為此新增一個 SpecialBeanForEngine類, 代碼如下:

 1 public class SpecialBeanForEngine implements BeanFactoryPostProcessor, BeanNameAware{
 2     
 3     String name;
 4 
 5     @Override
 6     public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
 7         
 8         BeanDefinitionRegistry bdr = (BeanDefinitionRegistry)beanFactory;
 9         GenericBeanDefinition gbd = new GenericBeanDefinition();
10         gbd.setBeanClass(EngineFactory.class);
11         gbd.setScope(BeanDefinition.SCOPE_SINGLETON);
12         gbd.setAutowireCandidate(true);
13         bdr.registerBeanDefinition("engine01-gbd", gbd);
14     }
15     
16     public static class EngineFactory implements FactoryBean<Engine>, BeanNameAware, InvocationHandler{
17         
18         String name;
19         
20         @Override
21         public Engine getObject() throws Exception {
22             System.out.println("EngineFactory  to build Engine01 , EngineFactory :"+ name);
23             Engine prox = (Engine) Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{Engine.class}, this);
24             return prox;
25         }
26 
27         @Override
28         public Class<?> getObjectType() {
29             return Engine.class;
30         }
31 
32         @Override
33         public boolean isSingleton() {
34             return true;
35         }
36 
37         @Override
38         public void setBeanName(String name) {
39             this.name = name;
40         }
41 
42         @Override
43         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
44             System.out.println("here is invoke  engine:"+method.getName());
45             return null;
46         }
47     }
48 
49     @Override
50     public void setBeanName(String name) {
51         this.name =name;
52     }
53 }

上面代碼 8 ~ 13行,在postProcessBeanFactory方法中添加了 EngineFactory.class類的Bean。 EngineFactory 是一個FactoryBean,代碼21-24行在getObject()方法中,使用動態代理生產Engine接口的代理對象。

在App類中增加SpecialBeanForEngine  Bean, 如下

    @Bean
    SpecialBeanForEngine specialBeanForEngine(){
        return new SpecialBeanForEngine();
    }

程序運行結果如下:

 1 SpecialBeanForEngine bean name :specialBeanForEngine
 2 EngineFactory  to build Engine01 , EngineFactory :engine01-gbd
 3 BenzCar Constructor
 4 BenzCar's engine not setting
 5 BenzCar postConstruct
 6 BenzCar's engine installed, in postConstruct
 7 BenzCar initializingBean after propertieSet
 8 BenzCar's engine installed, in initializingBean
 9 here is invoke  engine:fire
10 BenzCar start
11 here is invoke  engine:fire

第1行: specialBeanForEngine  bean 先生成

第2行: EngineFactory 調用 getObject()方法生產 Engine代理對象

第3行、4行: BenzCar調用構造方法,此時 engine屬性還未被設置。

第5行、6: BenzCar調用@PostConstruct注解的方法,此時engine屬性已經設置。

第7行: BenzCar調用 InitializingBean接口方法。

第10行: BenzCar調用 initMethod指定的方法,

第11行: BenzCar調用了代理對象的方法,SpecialBeanForEngine 類中第44行代碼。

運行結果與前面描述的bean生命周期一致。至此,我們完成了只有Engine接口的情況下,在BenzCar中注入了Engine對象。

總結,postProcessBeanFactory接口、FactoryBean、動態代理,三者結合,可以在運行時動態的給BeanFactory中增加Bean,非常靈活的對spring容器進行擴展。很多開源項目在與spring整合時采用了類似方法。如果我們想自己寫一些結合spring的框架程序,也可以采用類似方案。

 

參考:bean生命周期

代碼已上傳:https://github.com/shuangzh/springinsight.git

 來源:周雙的博客, 歡迎轉載。(微信/QQ: 48689547, email:shuangzh@qq.com)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM