Spring系列第五講 建立bean例項這些方式你們都知道?

qwer1030274531發表於2020-10-29

透過反射呼叫構造方法建立bean物件

呼叫類的構造方法獲取對應的bean例項,是使用最多的方式,這種方式只需要在xml bean元素中指定class屬性,spring容器內部會自動呼叫該型別的構造方法來建立bean物件,將其放在容器中以供使用。

語法

<bean id="bean名稱" name="bean名稱或者別名" class="bean的完整型別名稱">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名稱" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名稱" /></bean>1234567

constructor-arg用於指定構造方法引數的值

index:構造方法中引數的位置,從0開始,依次遞增

value:指定引數的值

ref:當插入的值為容器內其他bean的時候,這個值為容器中對應bean的名稱

案例

UserModel類

@Getter@Setter@ToStringpublic class UserModel {
    private String name;
    private int age;
    public UserModel() {
        this.name = "我是透過UserModel的無參構造方法建立的!";
    }
    public UserModel(String name, int age) {
        this.name = name;
        this.age = age;
    }}1234567891011121314151617

beans.xml配置

<!-- 透過UserModel的預設構造方法建立UserModel物件 --><bean id="createBeanByConstructor1" class="com.javacode2018.lesson001.demo3.UserModel"/><!-- 透過UserModel有參構造方法建立UserModel物件 --><bean id="createBeanByConstructor2" class="com.javacode2018.lesson001.demo3.UserModel">
    <constructor-arg index="0" value="我是透過UserModel的有參方法構造的物件!"/>
    <constructor-arg index="1" value="30"/></bean>12345678

上面這2種寫法,spring容器建立這兩個UserModel的時候,都會透過反射的方式去呼叫UserModel類中對應的建構函式來建立UserModel物件。

測試用例

package com.javacode2018.lesson001.demo3;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;public class Client {
    public static void main(String[] args) {
        //1.bean配置檔案位置
        String beanXml = "classpath:/com/javacode2018/lesson001/demo3/beans.xml";
        //2.建立ClassPathXmlApplicationContext容器,給容器指定需要載入的bean配置檔案
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
        System.out.println("spring容器中所有bean如下:");
        //getBeanDefinitionNames用於獲取容器中所有bean的名稱
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(beanName + ":" + context.getBean(beanName));
        }
    }}1234567891011121314151617181920212223242526

程式碼中會輸出spring容器中所有bean的名稱和其對應的bean物件。

執行輸出

spring容器中所有bean如下:
createBeanByConstructor1:UserModel(name=我是透過UserModel的無參構造方法建立的!, age=0)createBeanByConstructor2:UserModel(name=我是透過UserModel的有參方法構造的物件!, age=30)123

透過靜態工廠方法建立bean物件

我們可以建立靜態工廠,內部提供一些靜態方法來生成所需要的物件,將這些靜態方法建立的物件交給spring以供使用。

語法

<bean id="bean名稱" name="" class="靜態工廠完整類名" factory-method="靜態工廠的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名稱" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名稱" /></bean>1234567

class:指定靜態工廠完整的類名

factory-method:靜態工廠中的靜態方法,返回需要的物件。

constructor-arg用於指定靜態方法引數的值,用法和上面介紹的構造方法一樣。

spring容器會自動呼叫靜態工廠的靜態方法獲取指定的物件,將其放在容器中以供使用。

案例

定義靜態工廠

建立一個靜態工廠類,用於生成UserModel物件。

package com.javacode2018.lesson001.demo3;public class UserStaticFactory {
    /**
     * 靜態無參方法建立UserModel
     *
     * @return
     */
    public static UserModel buildUser1() {
        System.out.println(UserStaticFactory.class + ".buildUser1()");
        UserModel userModel = new UserModel();
        userModel.setName("我是無參靜態構造方法建立的!");
        return userModel;
    }
    /**
     * 靜態有參方法建立UserModel
     *
     * @param name 名稱
     * @param age  年齡
     * @return
     */
    public static UserModel buildUser2(String name, int age) {
        System.out.println(UserStaticFactory.class + ".buildUser2()");
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(age);
        return userModel;
    }}1234567891011121314151617181920212223242526272829303132333435

beans.xml配置

<!-- 透過工廠靜態無參方法建立bean物件 --><bean id="createBeanByStaticFactoryMethod1" class="com.javacode2018.lesson001.demo3.UserStaticFactory"
      factory-method="buildUser1"/><!-- 透過工廠靜態有參方法建立bean物件 --><bean id="createBeanByStaticFactoryMethod2" class="com.javacode2018.lesson001.demo3.UserStaticFactory"
      factory-method="buildUser2">
    <constructor-arg index="0" value="透過工廠靜態有參方法建立UerModel例項物件"/>
    <constructor-arg index="1" value="30"/></bean>12345678910

上面配置中,spring容器啟動的時候會自動呼叫UserStaticFactory中的buildUser1靜態方法獲取UserModel物件,將其作為createBeanByStaticFactoryMethod1名稱對應的bean物件放在spring容器中。

會呼叫UserStaticFactory的buildUser2方法,並且會傳入2個指定的引數,得到返回的UserModel物件,將其作為createBeanByStaticFactoryMethod2名稱對應的bean物件放在spring容器中。

執行Client

class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser1()class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser2()spring容器中所有bean如下:
createBeanByStaticFactoryMethod1:UserModel(name=我是無參靜態構造方法建立的!, age=0)createBeanByStaticFactoryMethod2:UserModel(name=透過工廠靜態有參方法建立UerModel例項物件, age=30)12345

從輸出中可以看出,兩個靜態方法都被呼叫了,createBeanByStaticFactoryMethod1對應的bean物件是透過buildUser1方法建立的;createBeanByStaticFactoryMethod2對應的bean物件是透過buildUser2方法建立的。

透過例項工廠方法建立bean物件

讓spring容器去呼叫某些物件的某些例項方法來生成bean物件放在容器中以供使用。

語法 question/

<bean id="bean名稱" factory-bean="需要呼叫的例項物件bean名稱" factory-method="bean物件中的方法">
    <constructor-arg index="0" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="1" value="bean的值" ref="引用的bean名稱" />
    <constructor-arg index="2" value="bean的值" ref="引用的bean名稱" />
    ....
    <constructor-arg index="n" value="bean的值" ref="引用的bean名稱" /></bean>1234567

spring容器以factory-bean的值為bean名稱查詢對應的bean物件,然後呼叫該物件中factory-method屬性值指定的方法,將這個方法返回的物件作為當前bean物件放在容器中供使用。

案例

定義一個例項工廠

內部寫2個方法用來建立UserModel物件。

package com.javacode2018.lesson001.demo3;public class UserFactory {
    public UserModel buildUser1() {
        System.out.println("----------------------1");
        UserModel userModel = new UserModel();
        userModel.setName("bean例項方法建立的物件!");
        return userModel;
    }
    public UserModel buildUser2(String name, int age) {
        System.out.println("----------------------2");
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setAge(age);
        return userModel;
    }}123456789101112131415161718

beans.xml

<!-- 定義一個工廠例項 --><bean id="userFactory" class="com.javacode2018.lesson001.demo3.UserFactory"/><!-- 透過userFactory例項的無參user方法建立UserModel物件 --><bean id="createBeanByBeanMethod1" factory-bean="userFactory" factory-method="buildUser1"/><!-- 透過userFactory例項的有參user方法建立UserModel物件 --><bean id="createBeanByBeanMethod2" factory-bean="userFactory" factory-method="buildUser2">
    <constructor-arg index="0" value="透過bean例項有參方法建立UserModel例項物件"/>
    <constructor-arg index="1" value="30"/></bean>12345678910

createBeanByBeanMethod1對應的bean是透過userFactory的buildUser1方法生成的。

createBeanByBeanMethod2對應的bean是透過userFactory的buildUser2方法生成的。

執行Client

spring容器中所有bean如下:
createBeanByBeanMethod1:UserModel(name=bean例項方法建立的物件!, age=0)createBeanByBeanMethod2:UserModel(name=透過bean例項有參方法建立UserModel例項物件, age=30)123

透過FactoryBean來建立bean物件

前面我們學過了BeanFactory介面,BeanFactory是spring容器的頂層介面,而這裡要說的是FactoryBean,也是一個介面,這兩個介面很容易搞混淆,FactoryBean可以讓spring容器透過這個介面的實現來建立我們需要的bean物件。

FactoryBean介面原始碼: hunan/

public interface FactoryBean<T> {
    /**
     * 返回建立好的物件
     */
    @Nullable
    T getObject() throws Exception;
    /**
     * 返回需要建立的物件的型別
     */
    @Nullable
    Class<?> getObjectType();
    /**
    * bean是否是單例的
    **/
    default boolean isSingleton() {
        return true;
    }}12345678910111213141516171819202122

介面中有3個方法,前面2個方法需要我們去實現,getObject方法內部由開發者自己去實現物件的建立,然後將建立好的物件返回給Spring容器,getObjectType需要指定我們建立的bean的型別;最後一個方法isSingleton表示透過這個介面建立的物件是否是單例的,如果返回false,那麼每次從容器中獲取物件的時候都會呼叫這個介面的getObject() 去生成bean物件。

語法

<bean id="bean名稱" class="FactoryBean介面實現類" />1

案例

建立一個FactoryBean實現類

package com.javacode2018.lesson001.demo3;import org.springframework.beans.factory.FactoryBean;import org.springframework.lang.Nullable;public class UserFactoryBean implements FactoryBean<UserModel> {
    int count = 1;
    @Nullable
    @Override
    public UserModel getObject() throws Exception { //@1
        UserModel userModel = new UserModel();
        userModel.setName("我是透過FactoryBean建立的第"+count+++ "物件");//@4
        return userModel;
    }
    @Nullable
    @Override
    public Class<?> getObjectType() {
        return UserModel.class; //@2
    }
    @Override
    public boolean isSingleton() { 
        return true; //@3
    }}1234567891011121314151617181920212223242526

@1:返回了一個建立好的UserModel物件

@2:返回物件的Class物件

@3:返回true,表示建立的物件是單例的,那麼我們每次從容器中獲取這個物件的時候都是同一個物件

@4:此處用到了一個count,透過這個一會可以看出isSingleton不同返回值的時候從容器獲取的bean是否是同一個

bean xml配置

<!-- 透過FactoryBean 建立UserModel物件 --><bean id="createByFactoryBean" class="com.javacode2018.lesson001.demo3.UserFactoryBean"/>12

Client程式碼

package com.javacode2018.lesson001.demo3;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.net.URL;import java.net.URLClassLoader;import java.util.Arrays;public class Client {
    public static void main(String[] args) {
        //1.bean配置檔案位置
        String beanXml = "classpath:/com/javacode2018/lesson001/demo3/beans.xml";
        //2.建立ClassPathXmlApplicationContext容器,給容器指定需要載入的bean配置檔案
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);
        System.out.println("spring容器中所有bean如下:");
        //getBeanDefinitionNames用於獲取容器中所有bean的名稱
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(beanName + ":" + context.getBean(beanName));
        }
        System.out.println("--------------------------");
        //多次獲取createByFactoryBean看看是否是同一個物件
        System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
        System.out.println("createByFactoryBean:" + context.getBean("createByFactoryBean"));
    }}123456789101112131415161718192021222324252627282930

執行輸出

class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser1()class com.javacode2018.lesson001.demo3.UserStaticFactory.buildUser2()----------------------1----------------------2spring容器中所有bean如下:
createBeanByConstructor1:UserModel(name=我是透過UserModel的無參構造方法建立的!, age=0)createBeanByConstructor2:UserModel(name=我是透過UserModel的有參方法構造的物件!, age=30)createBeanByStaticFactoryMethod1:UserModel(name=我是無參靜態構造方法建立的!, age=0)createBeanByStaticFactoryMethod2:UserModel(name=透過工廠靜態有參方法建立UerModel例項物件, age=30)userFactory:com.javacode2018.lesson001.demo3.UserFactory@610694f1createBeanByBeanMethod1:UserModel(name=bean例項方法建立的物件!, age=0)createBeanByBeanMethod2:UserModel(name=透過bean例項有參方法建立UserModel例項物件, age=30)createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第1物件, age=0)--------------------------createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第1物件, age=0)createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第1物件, age=0)12345678910111213141516

注意最後4行輸出,有3行輸出的都是同一個createByFactoryBean,程式中透過getBean從spring容器中查詢createByFactoryBean了3次,3次結果都是一樣的,說明返回的都是同一個UserModel物件。

下面我們將UserFactoryBean中的isSingleton調整一下,返回false

@Overridepublic boolean isSingleton() {
    return false;}1234

當這個方法返回false的時候,表示由這個FactoryBean建立的物件是多例的,那麼我們每次從容器中getBean的時候都會去重新呼叫FactoryBean中的getObject方法獲取一個新的物件。

再執行一下Client,最後4行輸出:

createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第1物件, age=0)--------------------------createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第2物件, age=0)createByFactoryBean:UserModel(name=我是透過FactoryBean建立的第3物件, age=0)1234

這3次獲取的物件不一樣了。

總結 chengdou/

spring容器提供了4種建立bean例項的方式,除了建構函式的方式,其他幾種方式可以讓我們手動去控制物件的建立,這幾種方式大家都掌握一下,能夠靈活使用。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2730823/,如需轉載,請註明出處,否則將追究法律責任。

相關文章