歡迎光臨
每天分享高質量文章

Spring 核心 :FactoryBean

(點選上方公眾號,可快速關註)


來源:隨風溜達的嚮日葵@  ,

www.chkui.com/article/spring/spring_core_ioc_extension_points

Spring核心 : IOC 處理器擴充套件 》介紹了非侵入式的框架的概念以及IOC的功能擴充套件點之一——BeanPostProcessor,我們接下來的內容繼續說明IoC更多的擴充套件方法。

BeanFactoryPostProcessor

BeanFactoryPostProcessor是針對整個容器的後置處理器。他的使用也非常簡單,只要向容器中新增一個繼承BeanFactoryPostProcessor的Bean即可。

如何使用

繼承了BeanFactoryPostProcessor介面的類PostProcessors:

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessors implements BeanFactoryPostProcessor{

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

         //DO

    }

}

然後再向容器中新增這個Bean就增加了一個BeanFactoryPostProcessor。

   

BeanFactoryPostProcessor主要用於處理容器相關的內容,他被觸發時機是在IoC容器載入完各種配置後,還沒執行Bean的初始化之前。這個時候除了PostProcessors這個Bean,其他任何Bean都沒有被建立。 所以在BeanFactoryPostProcessor處理Bean是不合適的,Bean應該要到BeanPostProcessor中去處理,2者的區別就是前者面向容器,後者面向Bean。接下來將透過一個詳細例子來說明BeanFactoryPostProcessor和BeanPostProcessor的區別以及使用方式。期間還會介紹BeanDefinitio相關的內容。

BeanFactoryPostProcessor與BeanPostProcessor使用

文中僅僅是示例程式碼,無法執行,原始碼在https://gitee.com/chkui-com/spring-core-sample,如需下載請自行clone

建造者樣式

下麵將會透過一個例子介紹2者的使用方式和使用場景。例子使用建造者樣式模擬組裝一臺個人電腦,分為一下3步:

  1.  容器啟動之後,會將電腦的所有“配件”(Cpu、Graphics、Ram)都新增到容器中。

  2.  在PostProcessorS實現BeanFactoryPostProcessor介面,它的功能是向容器新增一個Pc物件。

  3.  在PostProcessor實現BeanPostProcessor介面。他的工作是組裝電腦——每一個Bean都會檢查域上的@Autowired註解,並註入對應的部件,部件也會標記自己所屬的電腦。

下麵是XML配置檔案,它負責將Cpu、顯示卡、記憶體等電腦常用品牌的部件放置到容器中等待組裝。此外它還添加了PostProcessorS和PostProcessor兩個後置處理器用於裝載電腦。

   

     

   

    

   

     

   

    

   

     

   

    

   

    

   

下麵是一個Cpu物件的結構,他標記了品牌和所屬電腦。Graphics和Ram的結構和它一模一樣。

package chkui.springcore.example.xml.beanfactorypostprocessor.bean;

public class Cpu {

private String brand;

@Autowired

private Pc belong;

}

註意這裡的@Autowired註解,我們的配置檔案並沒有開啟Spring的自動裝配功能,我們將在PostProcessor實現自動裝配。

PostProcessorS的作用是向容器動態新增一個之前未定義的Bean——Pc。

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessors implements BeanFactoryPostProcessor{

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

//獲取容器的註冊介面

BeanDefinitionRegistry registry = (BeanDefinitionRegistry)beanFactory;

//新建一個BeanDefinition用於動態裝配Bean

GenericBeanDefinition defininition = new GenericBeanDefinition();

//設定要新增的類

defininition.setBeanClass(Pc.class);

//註冊BeanDefinition

registry.registerBeanDefinition(“postBean”, defininition);

}

}

如果看過 Ioc結構介紹的這篇文章,你就會知道BeanFactory經過層層派生,實際上大部分介面都在一個類實現——DefaultListableBeanFactory,它除了實現ConfigurableListableBeanFactory介面,還實現了BeanDefinitionRegistry介面。BeanDefinitionRegistry提供了BeanDefinition的管理功能。

https://www.chkui.com/article/spring/spring_core_context_and_ioc

BeanDefinition與配接器樣式

在上面的程式碼中出現了BeanDefinition介面,這裡就順道說一說這個有趣的小玩意。關於他如何使用Spring的官網並沒有太詳細的介紹(至少我沒找到),網上倒是有各路大神的部落格在解讀他的原始碼,不過程式碼只是表象,要理解他的整套設計思路才能提升。

關於BeanDefinition的使用樣式,官網將其稱呼為configuration metadata,直譯過來叫“配置元資料”。 他的作用有點類似於Context分層應用的效果(見Spring核心——背景關係與IoC 關於 ApplicationContext的說明),目的就是將Bean的配置和初始化工作分成2個互不幹擾的部分。

我們知道 Spring現在支援各種各樣的方式來新增Bean,比如在XML配置檔案中使用標簽、使用@Component以及他的派生類註解、可以在@Configuration類中生成、甚至還可以透過RMI實現遠端配置等等。如果所有的這些配置來源直接和IoC容器產生關係生成Bean,那麼耦合度、程式碼複雜度會越來越高,而且以後指不定什麼時候又會加入什麼新的配置方式。

為瞭解決這個問題Spring的大神們引入了配接器樣式——IoC容器只接受BeanDefinition介面,IoC如何初始化一個Bean是僅僅是看BeanDefinition裡的資訊。而各種配置方式都有自己的配接器,所有的配接器都會根據他所需要處理的內容來生成一個BeanDefinition的實現類。這樣,如果新增一個新的配置方式,增加一個配接器就可以搞定。

所以,我們也可以利用BeanDefinitionRegistry介面向容器新增一個BeanDefinition,進而在隨後的執行過程中IoC容器會根據 這個BeanDefinition建立一個對應的Bean。

BeanPostProcessor

前面已經提到,BeanFactoryPostProcessor用於處理容器級別的問題,而BeanPostProcessor用來處理每一個Bean。我們前面已經用BeanFactoryPostProcessor向容器添加了一個Pc物件的Bean,接下來我們在BeanPostProcessor中處理每一個Bean的自動註入註解。

package chkui.springcore.example.xml.beanfactorypostprocessor;

public class PostProcessor implements BeanPostProcessor, BeanFactoryAware {

private ConfigurableListableBeanFactory beanFactory;

public Object postProcessBeforeInitialization(Object bean, String beanName) {

        return autowiredImplement(bean);

    }

public Object postProcessAfterInitialization(Object bean, String beanName) {

        return bean;

    }

//自定義實現autowired功能

private Object autowiredImplement(final Object bean) {

for(Field field : bean.getClass().getDeclaredFields()) {

Autowired value = field.getAnnotation(Autowired.class);

if(null != value) {

Object obj = beanFactory.getBean(field.getType());

field.setAccessible(true);

field.set(bean, obj);

}

}

return bean;

}

@Override

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {

this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;

}

}

這裡的PostProcessor實現BeanFactoryAware介面來獲取 BeanFactory。自動註入的處理邏輯都在autowiredImplement方法中,它會掃描Bean的每一個域檢查是否有@Autowired註解,如果有則根據這個域的Class型別到BeanFactory去獲取對應的Bean,然後反射註入。

最後我們建立一個ApplicationContext來執行他們:

public class SampleApp {

    public static void main(String[] args) {

    ApplicationContext context = new ClassPathXmlApplicationContext(“xml/beanfactorypostprocessor/config.xml”);

    Pc pc = context.getBean(Pc.class);

        /**

        Pc Info: Graphics=Nvdia, Cpu=Amd, Ram=Kingston]

        */

        System.out.println(pc);

    }

}

本文介紹了BeanFactoryPostProcessor和BeanPostProcessor的使用方式,以及IoC容易是如何透過BeanDefinition裝載各種配置的。後續還會持續介紹Spring IoC容器的各種功能擴充套件點。

【關於投稿】


如果大家有原創好文投稿,請直接給公號傳送留言。


① 留言格式:
【投稿】+《 文章標題》+ 文章連結

② 示例:
【投稿】《不要自稱是程式員,我十多年的 IT 職場總結》:http://blog.jobbole.com/94148/

③ 最後請附上您的個人簡介哈~


看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂