beanFactory 設計模式 Bean 生命週期的胡言亂語,哈哈

寫在前面的話

適用讀者:有必定經驗的,本文不適合初學者,由於可能不能理解我在說什麼java

文章思路:不會一開始就像別的博客文章那樣,Bean 的生命週期,源碼解讀(給你貼一大堆的源碼)。我的以爲應該由問題驅動,爲何爲出現 BeanFactory ,爲何會有生命週期。git

正文

一開始咱們使用 bean 都是簡單bean,如 vo ,po,entity,dto,咱們是這麼玩的程序員

XXEntity xxEntity = new XXEntity();
xxEntity.setPropA("字符串");

後面可能出現了某個比較複雜的 bean ,它有一個對象作爲屬性,須要在構造時或構造後設置值(示例而已,不要較真),如redis

// 構建序列化實例,這裏 Serializable 是接口,使用接口的好處是在使用別的序列化時,不須要修改 jedis 類
Serializable fastJsonSerizlizable = new FastJsonSerizlizable();

// 構建目標 jedis 實例 ,須要先構建序列化對象 
Jedis jedis = new Jedis();
jedis.setSerializable(fastJsonSerizlizable);

這時來了 serviceA 類和 serviceB 類,它們都須要使用 redis,我不可能在每一個類裏面都去把 jedis 實例化的過程寫一遍,這時有經驗的同窗會寫一個工具類來建立 jedis ,像這樣數據庫

public BeanUtil {
    // 能夠把建立序列化單拿出來,由於除了 redis 須要序列化以外,kafka 也須要序列化
    public static Serializable createSerializable(){
        return new FastJsonSerizlizable();
    }
    
    public static Jedis createJedis(){
        Jedis jedis = new Jedis();
        jedis.setSerializable(createSerializable());
        return jedis;
    }
}

// 這裏我 serviceA,serviceB 均可以使用 createJedis 來直接獲取 jedis 實例 ,而不須要關心建立細節,使用哪一個序列化等問題

上面代碼有幾個問題緩存

  • 每次使用時都會建立 jedis 對象 ,而每個 jedis 對象又會單獨對一個 Serializable 對象 ,可是 fastJson 的序列化和 jedis 都只是工具類型的東西,一個實例足已。
  • 沒法對 Jedis 進行配置
  • 不能讓使用者去建立 BeanUtil 實例 ,改進的代碼 以下
public BeanUtil {
    // 禁用 BeanUtil 構建 
    private BeanUtil(){}
    
    // 這裏咱們可使用 bean 的全路徑 => bean 實例來緩存 bean 
    static Map<String,Object> beansCache = new ConcurrentHashMap<String,Object>();
    
    static{
        // 初始化時,在內容緩存這些 bean 的實例,由於 jedis 依賴於 serializable ,須要須要先建立 serializable
        Serializable serializable = createSerializable();
        beansCache.put(Serializable.class.getSimpleName(),serializable);
        Jedis jedis = createJedis();
        beansCache.put(jedis.class.getSimpleName(),jedis);
    }
    
    static Serializable createSerializable(String type){
        Serializable serializable =  beansCache.get("serializable");
        if(serializable != null)return serializable;
        
        switch(type){
            case "kryo":    // kryo 不能用單例,請忽略本問題,示例而已
                return new KryoSerializable();
            case "protostuff":
                return new protostuffSerializable();
            default:
                return new FastJsonSerizlizable();
        }
    }
    
    static Jedis createJedis(String serializableType){
        Jedis jedis = new Jedis();
        Serializable serializable = beansCache.get("serializable");
        jedis.setSerializable(serializable);
        return jedis;
    }

    //而後對外提供獲取 Bean 的方法 
    public static Object getBean(String beanName){
        return beansCache.get(beanName);
    }
    
    public static T getBean(Class<T> type){
        return beansCache.get(type.getSimpleName());
    }

}

但若是寫這個類的是小明,通過一段時間後這個類裏會出現大量的 createXx 和 XX 的初始化操做,並且依賴程度也很是複雜,這時小明想,是時候優化一波了,因而小明想了一種解決方案,定義了一種 xml 語法maven

使用 bean 標籤來定義一個 bean,每一個 bean 都有惟一的一個 id 信息 ,使用 property 來定義它的屬性 ,若是是複雜屬性使用 ref ,解析這個xml 獲得一個完整的 bean 依賴圖函數

<beans>
    <bean id="serializable" class="com.xx.FastJsonSerizlizable" />
    
    <bean id="jedis" class="com.xx.Jedis">
        <property name="host" value="localhost" />
        <property name="serializable" ref="serializable"/>
    </bean>
</beans>

這時會有一個依賴問題,我建立 jedis 要先建立 serializable ,可是 serializable 的 xml bean 定義是寫在文件前面 的,小明想了一個辦法,先把 ref 使用字符串先存着,所有放到一個 bean 定義中,像這樣工具

Map<String,BeanDefinition> beanDefinitions = new HashMap();

而後把其解析成一顆依賴樹,這樣就能夠先構造樹葉,而後逐層構造對象 ,但也有一種棘手的狀況 ,那就是循環依賴post

root

  |-jedis

    |- serializable

什麼是循環依賴呢,最簡單的 A 依賴於 B,B 依賴於 A ,或者中間有更多的依賴最後造成了一個圈,A-B-C-A

最原始的解決方式是這樣的,咱們能夠先使用構造函數把它們都建立出來,不能是有帶它們的構造函數,而後經過 set 把對象經過屬性設置值。因此除了構造注入外,經過屬性方式是能夠解決循環依賴的。

這時咱們的 BeanUtil 變成了這樣,想一想不能叫工具類了,改成實體類 Factory

public BeanFactory {
    
    Map<String,BeanDefinition> beanDefinitions = new HashMap();
    
    // 這裏咱們可使用 bean 的全路徑 => bean 實例來緩存 bean 
    Map<String,Object> beansCache = new ConcurrentHashMap<String,Object>();
 
    {
        // 加載 xml bean 配置文件
        beanDefinitions = loadXml(contextConfigurations:String []);
        
        //實例化全部 bean 
        beansCache = instanceBeans(beanDefinitions);
    }
    
    //而後對外提供獲取 Bean 的方法 
    public  Object getBean(String beanName){
        return beansCache.get(beanName);
    }
    
    public  T getBean(Class<T> type){
        return beansCache.get(type.getSimpleName());
    }
}

這看起來已經足夠完美了,但這時程序員A提問了,我須要對個人某個類的初始化時,我要獲取一些好比鏈接資源,文件資源,而後在類銷燬時想要回收資源,但根據上面沒任何辦法能夠作到。

小明說,這好辦,我提供幾個接口給你,你實現一下,我會在實例化 Bean 的時候 ,若是發現你有實現接口,在相應的過程裏我就幫你調用一下,因而小明就添加了兩個接口

public interface InitializingBean{
    void afterPropertiesSet() throws Exception;
}

public  interface DisposableBean{
    void destroy() throws Exception;
}

程序員A 的問題解決了,這時程序員B說,有沒有一種辦法,能夠對全部 Bean 的初始化過程進行攔截,而不是我當前這個類,我想把每個 service 改爲代理類,我想要給 service 中的方法添加事務。

小明說,那好吧,我把 bean 的屬性都注入完了,而後給這個 bean 交給你,你裝飾一下這個 bean 而後再還給我,因而小明提供出了這樣一個接口 ,在 bean 初始化前和初始化後,你均可以來修改 bean ,不要要注意,這個是針對全局的,不是你我的的 bean ,要作好過濾操做

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException ;
    
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
}

程序員C 這時又發問了,我建立了一個 BeanA 但我怎麼樣能夠拿到 BeanC 啊,我想看看 c 的一些屬性。

小說說,真煩,我乾脆把 map 都給你好,不,我把 BeanFactory 都給你好了,因而有了這個接口

public interface BeanFactoryAware{
    void setBeanFactory(BeanFactory beanUtil);
}

這時程序D 又問了,我在 setBeanFactory 的時候 ,我建立的全局 processor 執行了嗎,仍是在以後執行,頭大。

小明說,我整理下執行順序,取個名吧,叫 bean 的生命週期,順便再提供幾個實用的接口,bean 的名字我還沒告訴你呢,因而整理的生命週期以下

反射建立 Bean 
填充對象屬性
BeanNameAware.setBeanName();
BeanFactoryAware.setBeanFactory ();
BeanPostProcessor.postProcessBeforeInitialization(); 多個
InitializingBean.afterPropertiesSet()
BeanPostProcessor.postProcessAfterInitialization(); 多個
DisposableBean.destory()

程序員E 又說了,xml 配置太麻煩了,jdk1.5 不是有註解嗎,我在類上加個標識,你掃描個人類,幫我建立實例唄

而後我須要用的時候,我在屬性上加個標識,你一樣能夠根據類型找到依賴的類,而後把對應的實例建立好,幫我把值放進去就行了,若是這個類的建立過程比較複雜,我本身來建立,而後我把它返回給你,我定義一個方法,加個 Bean 的標識,你來讀進容器。

因而小明又加了 @Component 來表示組件,@Bean 來表示自定義實例建立,@Autowired 來注入對象 @PostConstruct 來執行類的初始化工做 @PreDestroy 來作類的銷燬工做,類的生命週期變成這樣

反射建立 Bean 
填充對象屬性
BeanNameAware.setBeanName();
BeanFactoryAware.setBeanFactory ();
BeanPostProcessor.postProcessBeforeInitialization(); 多個
PostConstruct
InitializingBean.afterPropertiesSet()
BeanPostProcessor.postProcessAfterInitialization(); 多個
PreDestroy
DisposableBean.destory()

可是爲了兼容之前的 xml 形式,小明這時把 BeanFactory 抽象成接口,提供 getBean 方法,根據職責單一原則,BeanFactory 不該該再作解析 Bean 的工做;

再建立一個接口用於加載 Bean 定義,有兩個實現 XmlBeanRegistry ,AnnotationBeanRegistry ,加載 Bean 定義後再合併,考慮到之後還有可能添加別的註冊 bean 的方式 ,一次性提供一個對外的接口

public interface BeanFactoryPostProcessor{
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

你能夠把你規則寫成的 bean 定義,實例化爲我要求的 BeanDefinition 而後發給我就能夠自定義實現把你自定義的 bean 添加到容器中了

一點小推廣

創做不易,但願能夠支持下個人開源軟件,及個人小工具,歡迎來 gitee 點星,fork ,提 bug 。

Excel 通用導入導出,支持 Excel 公式
博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi

使用模板代碼 ,從數據庫生成代碼 ,及一些項目中常常能夠用到的小工具
博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven

相關文章
相關標籤/搜索