三年Java開發經驗,連如何動態註冊bean都不知道

乾貨點:

看完該篇文章我們可以瞭解到爲何要動態註冊bean、動態註冊bean的方式、註冊和實例化的區別、spring是什麼時候實例化bean的。

描述:

前陣子在給項目寫組件的時候研究了下spring組件拓展流程,其中遇見了動態註冊bean的操作,對於長期使用spring容器管理bean的同學來說動態註冊bean應該是活久見系列,爲此打算以組件中使用到的方式講講動態註冊方面的相關解說。

應用場景

在自定義組件的時候,需要自定義命名空間註冊器,其中需要提供一個配置解析器ConfigDefinitionParser,配置解析器需要繼承AbstractBeanDefinitionParser, 其中需要重寫函數parseInternal,返回的是AbstractBeanDefinition類型的數據。爲了返回AbstractBeanDefinition,我借鑑了其他spring內部組件的寫法,這邊就用到了動態註冊bean了。

在進入動態註冊bean的相關解說前之前有必要大致描述下spring容器註冊bean的相關知識!

瞭解下DefaultListableBeanFactory

DefaultListableBeanFactory是spring容器註冊bean的核心,是spring註冊及加載bean的默認實現。

一句話來描述其應用那就是:在spring項目啓動的時候通過DefaultListableBeanFactory註冊所有的BeanDefinition後放入definition表,後面在我們使用的時候就可以直接從表中加載了。

瞭解下BeanDefinition

在說到動態註冊bean之前,要先提下BeanDefinition

 

BeanDefinition.png

 

 

這是從官方文檔截出來的,對此我的理解是,BeanDefinition是bean在實例化之前存在spring容器中的一種狀態。

分不清註冊和實例化的同學們在這裏內心應該是 ━━( ̄ー ̄*|||━━ wocao,實例化和註冊,什麼跟什麼 。

別急,在文章最後會點一下實例化和註冊的哈!

大致瞭解了這兩個之後便可以真正講解如何動態註冊bean了

使用 BeanDefinitionBuilder註冊bean

舉個可以運行的栗子

package com.example.testdemo.beanDefinitionBuilderTest.main;


public class TestBean {

    private String str;

    private TestBean2 testBean2;

    public void setStr(String str) {
        this.str = str;
    }

    public void setTestBean2(TestBean2 testBean2) {
        this.testBean2 = testBean2;
    }

    @Override
    public String toString() {
        return "TestBean{" +
            "str='" + str + '\'' +
            ", testBean2=" + testBean2 +
            '}';
    }
}

複製代碼
package com.example.testdemo.beanDefinitionBuilderTest.main;

public class TestBean2 {

    private int a;
    
    public void setA(int a) {
        this.a = a;
    }

    @Override
    public String toString() {
        return "TestBean2{" +
            "a=" + a +
            '}';
    }
}
複製代碼

主要main類,可分爲三個步驟:定義、註冊、再取出

package com.example.testdemo.beanDefinitionBuilderTest.main;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class BeanDefinitionBuilderExample {

    public static void main(String[] args) {
        init();
    }

    private static void init() {

        // 構建DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 註冊testBean2
        registerTestBean2(beanFactory);
        // 註冊testBean
        registerTestBean1(beanFactory);

        // 從DefaultListableBeanFactory中讀取bean
        TestBean bean = beanFactory.getBean(TestBean.class);
        System.out.println(bean);
    }

    private static void registerTestBean1(DefaultListableBeanFactory beanFactory) {

        // 構建TestBean
        BeanDefinitionBuilder b1 = BeanDefinitionBuilder.rootBeanDefinition(TestBean.class);
        // 添加屬性
        b1.addPropertyValue("str", "myStringValue");
        // 添加testBean2引用
        b1.addPropertyReference("testBean2", "testBean2");

        // 註冊TestBean
        beanFactory.registerBeanDefinition("testBean", b1.getBeanDefinition());
    }

    private static void registerTestBean2(DefaultListableBeanFactory beanFactory) {

        // 構建TestBean2
        BeanDefinitionBuilder b2 = BeanDefinitionBuilder.rootBeanDefinition(TestBean2.class);
        // 添加屬性
        b2.addPropertyValue("a", 1);

        // 註冊TestBean2
        beanFactory.registerBeanDefinition("testBean2", b2.getBeanDefinition());
    }
}

複製代碼

運行後輸出

TestBean{str='myStringValue', testBean2=TestBean2{a=1}}

關於如何給BeanDefinitionBuilder添加各種成員屬性和構造傳參可以直接查看api地址:

docs.spring.io/spring/docs…

使用 BeanFactoryPostProcessor註冊bean

BeanFactoryPostProcessor允許自定義BeanDefinition,使用的GenericBeanDefinition,然後再註冊入DefaultListableBeanFactory。

舉個可以運行的栗子

這裏只給出main類,和上面的例子的區別在於註冊函數的不同

package com.example.testdemo.beanDefinitionBuilderTest.main;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

public class BeanDefinitionBuilderExample {

    public static void main(String[] args) {
        init();
    }

    private static void init() {

        // 構建DefaultListableBeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

        // 註冊testBean2
        registerTestBean2(beanFactory);
        // 註冊testBean
        registerTestBean1(beanFactory);

        // 從DefaultListableBeanFactory中讀取bean
        TestBean bean = beanFactory.getBean(TestBean.class);
        System.out.println(bean);
    }

    rivate static void registerTestBean1(DefaultListableBeanFactory beanFactory) {

        // 構建TestBean
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(TestBean.class);
        // 添加屬性
        bd.getPropertyValues().add("str", "myStringValue");
        // 添加testBean2引用
        bd.getPropertyValues().add("testBean2", beanFactory.getBean("testBean2"));

        // 註冊TestBean
        beanFactory.registerBeanDefinition("testBean", bd);
    }

    private static void registerTestBean2(DefaultListableBeanFactory beanFactory) {

        // 構建TestBean2
        GenericBeanDefinition bd = new GenericBeanDefinition();
        bd.setBeanClass(TestBean2.class);
        // 添加屬性
        bd.getPropertyValues().add("a", 1);

        // 註冊TestBean2
        beanFactory.registerBeanDefinition("testBean2", bd);
    }
}

複製代碼

運行後輸出

TestBean{str='myStringValue', testBean2=TestBean2{a=1}}

可以看到註冊bean的時候對GenericBeanDefinition的處理是可以自定義了,最終運行效果也是一樣的。

關於註冊和實例化

註冊和實例化其實就是spring容器管理bean的一個過程,先有註冊bean纔有實例化bean。 那麼spring什麼時候實例化bean呢?這裏可以分爲2種情況

  • 如果我們使用BeanFactory作爲bean的工廠類,如我上面那樣的,則所有的bean都是在第一次使用該Bean的時候實例化,也就是從容器get出來的時候。

  • 如果我們使用ApplicationContext作爲bean的工廠類,則又分爲以下幾種情況:

    • 如果bean的scope是singleton的,也就是單例,並且lazy-init爲false(默認是false,所以可以不用設置),意思就是懶初始化,則ApplicationContext啓動的時候就實例Bean,並且將實例化後的Bean放在緩存中,下次再使用該Bean的時候,直接從這個緩存中。
    • 如果bean的scope是singleton的,而lazy-init爲true,則該Bean的實例化是在第一次使用該Bean的時候進行實例化。
    • 如果bean的scope是prototype的,則該Bean的實例化是在第一次使用該Bean的時候進行實例化。

ApplicationContext由BeanFactory類派生而來,所以提供了更多面向實際應用的功能。 以上可以看出spring容器其實是很懶的!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章