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)