終於,月薪過5萬了!

路人甲Java發表於2020-04-06

來看幾個問題

  1. 想不想月薪超過5萬?

  2. 想不想進入公司架構組?

  3. 想不想成為專案組的負責人?

  4. 想不想成為spring的高手,超越99%的對手?

那麼本文內容是你必須要掌握的。

本文主要詳解bean的生命週期,分為13個環節,每個環節中spring都提供了一些擴充套件點,我們都將進行詳細說明,讓大家全面掌握這塊的知識。

Spring bean生命週期13個環節

  1. 階段1:Bean元資訊配置階段

  2. 階段2:Bean元資訊解析階段

  3. 階段3:將Bean註冊到容器中

  4. 階段4:BeanDefinition合併階段

  5. 階段5:Bean Class載入階段

  6. 階段6:Bean例項化階段(2個小階段)

  • Bean例項化前階段

  • Bean例項化階段

  1. 階段7:合併後的BeanDefinition處理

  2. 階段8:屬性賦值階段(3個小階段)

  • Bean例項化後階段

  • Bean屬性賦值前階段

  • Bean屬性賦值階段

  1. 階段9:Bean初始化階段(5個小階段)

  • Bean Aware介面回撥階段

  • Bean初始化前階段

  • Bean初始化階段

  • Bean初始化後階段

  1. 階段10:所有單例bean初始化完成後階段

  2. 階段11:Bean的使用階段

  3. 階段12:Bean銷燬前階段

  4. 階段13:Bean銷燬階段

階段1:Bean元資訊配置階段

這個階段主要是bean資訊的定義階段。

Bean資訊定義4種方式

  • API的方式

  • Xml檔案方式

  • properties檔案的方式

  • 註解的方式

API的方式

先來說這種方式,因為其他幾種方式最終都會採用這種方式來定義bean配置資訊。

Spring容器啟動的過程中,會將Bean解析成Spring內部的BeanDefinition結構
不管是是通過xml配置檔案的<Bean>標籤,還是通過註解配置的@Bean,還是@Compontent標註的類,還是掃描得到的類,它最終都會被解析成一個BeanDefinition物件,最後我們的Bean工廠就會根據這份Bean的定義資訊,對bean進行例項化、初始化等等操作。

你可以把BeanDefinition丟給Bean工廠,然後Bean工廠就會根據這個資訊幫你生產一個Bean例項,拿去使用。

BeanDefinition裡面裡面包含了bean定義的各種資訊,如:bean對應的class、scope、lazy資訊、dependOn資訊、autowireCandidate(是否是候選物件)、primary(是否是主要的候選者)等資訊。

BeanDefinition是個介面,有幾個實現類,看一下類圖:

終於,月薪過5萬了!

BeanDefinition介面:bean定義資訊介面

表示bean定義資訊的介面,裡面定義了一些獲取bean定義配置資訊的各種方法,來看一下原始碼:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

    /**
     * 設定此bean的父bean名稱(對應xml中bean元素的parent屬性)
     */
    void setParentName(@Nullable String parentName);

    /**
     * 返回此bean定義時指定的父bean的名稱
     */
    @Nullable
    String getParentName();

    /**
     * 指定此bean定義的bean類名(對應xml中bean元素的class屬性)
     */
    void setBeanClassName(@Nullable String beanClassName);

    /**
     * 返回此bean定義的當前bean類名
     * 注意,如果子定義重寫/繼承其父類的類名,則這不一定是執行時使用的實際類名。此外,這可能只是呼叫工廠方法的類,或者在呼叫方法的工廠bean引用的情況下,它甚至可能是空的。因此,不要認為這是執行時的最終bean型別,而只將其用於單個bean定義級別的解析目的。
     */
    @Nullable
    String getBeanClassName();

    /**
     * 設定此bean的生命週期,如:singleton、prototype(對應xml中bean元素的scope屬性)
     */
    void setScope(@Nullable String scope);

    /**
     * 返回此bean的生命週期,如:singleton、prototype
     */
    @Nullable
    String getScope();

    /**
     * 設定是否應延遲初始化此bean(對應xml中bean元素的lazy屬性)
     */
    void setLazyInit(boolean lazyInit);

    /**
     * 返回是否應延遲初始化此bean,只對單例bean有效
     */
    boolean isLazyInit();

    /**
     * 設定此bean依賴於初始化的bean的名稱,bean工廠將保證dependsOn指定的bean會在當前bean初始化之前先初始化好
     */
    void setDependsOn(@Nullable String... dependsOn);

    /**
     * 返回此bean所依賴的bean名稱
     */
    @Nullable
    String[] getDependsOn();

    /**
     * 設定此bean是否作為其他bean自動注入時的候選者
     * autowireCandidate
     */
    void setAutowireCandidate(boolean autowireCandidate);

    /**
     * 返回此bean是否作為其他bean自動注入時的候選者
     */
    boolean isAutowireCandidate();

    /**
     * 設定此bean是否為自動注入的主要候選者
     * primary:是否為主要候選者
     */
    void setPrimary(boolean primary);

    /**
     * 返回此bean是否作為自動注入的主要候選者
     */
    boolean isPrimary();

    /**
     * 指定要使用的工廠bean(如果有)。這是要對其呼叫指定工廠方法的bean的名稱。
     * factoryBeanName:工廠bean名稱
     */
    void setFactoryBeanName(@Nullable String factoryBeanName);

    /**
     * 返回工廠bean名稱(如果有)(對應xml中bean元素的factory-bean屬性)
     */
    @Nullable
    String getFactoryBeanName();

    /**
     * 指定工廠方法(如果有)。此方法將使用建構函式引數呼叫,如果未指定任何引數,則不使用任何引數呼叫。該方法將在指定的工廠bean(如果有的話)上呼叫,或者作為本地bean類上的靜態方法呼叫。
     * factoryMethodName:工廠方法名稱
     */
    void setFactoryMethodName(@Nullable String factoryMethodName);

    /**
     * 返回工廠方法名稱(對應xml中bean的factory-method屬性)
     */
    @Nullable
    String getFactoryMethodName();

    /**
     * 返回此bean的建構函式引數值
     */
    ConstructorArgumentValues getConstructorArgumentValues();

    /**
     * 是否有構造器引數值設定資訊(對應xml中bean元素的<constructor-arg />子元素)
     */
    default boolean hasConstructorArgumentValues() {
        return !getConstructorArgumentValues().isEmpty();
    }

    /**
     * 獲取bean定義是配置的屬性值設定資訊
     */
    MutablePropertyValues getPropertyValues();

    /**
     * 這個bean定義中是否有屬性設定資訊(對應xml中bean元素的<property />子元素)
     */
    default boolean hasPropertyValues() {
        return !getPropertyValues().isEmpty();
    }

    /**
     * 設定bean初始化方法名稱
     */
    void setInitMethodName(@Nullable String initMethodName);

    /**
     * bean初始化方法名稱
     */
    @Nullable
    String getInitMethodName();

    /**
     * 設定bean銷燬方法的名稱
     */
    void setDestroyMethodName(@Nullable String destroyMethodName);

    /**
     * bean銷燬的方法名稱
     */
    @Nullable
    String getDestroyMethodName();

    /**
     * 設定bean的role資訊
     */
    void setRole(int role);

    /**
     * bean定義的role資訊
     */
    int getRole();

    /**
     * 設定bean描述資訊
     */
    void setDescription(@Nullable String description);

    /**
     * bean描述資訊
     */
    @Nullable
    String getDescription();

    /**
     * bean型別解析器
     */
    ResolvableType getResolvableType();

    /**
     * 是否是單例的bean
     */
    boolean isSingleton();

    /**
     * 是否是多列的bean
     */
    boolean isPrototype();

    /**
     * 對應xml中bean元素的abstract屬性,用來指定是否是抽象的
     */
    boolean isAbstract();

    /**
     * 返回此bean定義來自的資源的描述(以便在出現錯誤時顯示上下文)
     */
    @Nullable
    String getResourceDescription();

    @Nullable
    BeanDefinition getOriginatingBeanDefinition();

}

BeanDefinition介面上面還繼承了2個介面:

  • AttributeAccessor

  • BeanMetadataElement

AttributeAccessor介面:屬性訪問介面
public interface AttributeAccessor {

    /**
     * 設定屬性->值
     */
    void setAttribute(String name, @Nullable Object value);

    /**
     * 獲取某個屬性對應的值
     */
    @Nullable
    Object getAttribute(String name);

    /**
     * 移除某個屬性
     */
    @Nullable
    Object removeAttribute(String name);

    /**
     * 是否包含某個屬性
     */
    boolean hasAttribute(String name);

    /**
     * 返回所有的屬性名稱
     */
    String[] attributeNames();

}

這個介面相當於key->value資料結構的一種操作,BeanDefinition繼承這個,內部實際上是使用了LinkedHashMap來實現這個介面中的所有方法,通常我們通過這些方法來儲存BeanDefinition定義過程中產生的一些附加資訊。

BeanMetadataElement介面

看一下其原始碼:

public interface BeanMetadataElement {

    @Nullable
    default Object getSource() {
        return null;
    }

}

BeanDefinition繼承這個介面,getSource返回BeanDefinition定義的來源,比如我們通過xml定義BeanDefinition的,此時getSource就表示定義bean的xml資源;若我們通過api的方式定義BeanDefinition,我們可以將source設定為定義BeanDefinition時所在的類,出錯時,可以根據這個來源方便排錯。

RootBeanDefinition類:表示根bean定義資訊

通常bean中沒有父bean的就使用這種表示

ChildBeanDefinition類:表示子bean定義資訊

如果需要指定父bean的,可以使用ChildBeanDefinition來定義子bean的配置資訊,裡面有個parentName屬性,用來指定父bean的名稱。

GenericBeanDefinition類:通用的bean定義資訊

既可以表示沒有父bean的bean配置資訊,也可以表示有父bean的子bean配置資訊,這個類裡面也有parentName屬性,用來指定父bean的名稱。

ConfigurationClassBeanDefinition類:表示通過配置類中@Bean方法定義bean資訊

可以通過配置類中使用@Bean來標註一些方法,通過這些方法來定義bean,這些方法配置的bean資訊最後會轉換為ConfigurationClassBeanDefinition型別的物件

AnnotatedBeanDefinition介面:表示通過註解的方式定義的bean資訊

裡面有個方法

AnnotationMetadata getMetadata();

用來獲取定義這個bean的類上的所有註解資訊。

BeanDefinitionBuilder:構建BeanDefinition的工具類

spring中為了方便操作BeanDefinition,提供了一個類:BeanDefinitionBuilder,內部提供了很多靜態方法,通過這些方法可以非常方便的組裝BeanDefinition物件,下面我們通過案例來感受一下。

案例1:組裝一個簡單的bean

來個簡單的類
package com.javacode2018.lesson002.demo1;

public class Car {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Car{" +
                "name='" + name + '\'' +
                '}';
    }
}
測試用例
@Test
public void test1() {
    //指定class
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
    //獲取BeanDefinition
    BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
    System.out.println(beanDefinition);
}

等效於

<bean class="com.javacode2018.lesson002.demo1.Car" />
執行輸出
Root bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null

案例2:組裝一個有屬性的bean

程式碼
@Test
public void test2() {
    //指定class
    BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName());
    //設定普通型別屬性
    beanDefinitionBuilder.addPropertyValue("name", "奧迪"); //@1
    //獲取BeanDefinition
    BeanDefinition carBeanDefinition = beanDefinitionBuilder.getBeanDefinition();
    System.out.println(carBeanDefinition);

    //建立spring容器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); //@2
    //呼叫registerBeanDefinition向容器中註冊bean
    factory.registerBeanDefinition("car", carBeanDefinition); //@3
    Car bean = factory.getBean("car", Car.class); //@4
    System.out.println(bean);
}

@1:呼叫addPropertyValue給Car中的name設定值

@2:建立了一個spring容器

@3:將carBeanDefinition這個bean配置資訊註冊到spring容器中,bean的名稱為car

@4:從容器中獲取car這個bean,最後進行輸出

執行輸出
Root bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Car{name='奧迪'}

第二行輸出了從容器中獲取的car這個bean例項物件。

案例3:組裝一個有依賴關係的bean

再來個類

下面這個類中有個car屬性,我們通過spring將這個屬性注入進來。

package com.javacode2018.lesson002.demo1;

public class User {
    private String name;

    private Car car;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Car getCar() {
        return car;
    }

    public void setCar(Car car) {
        this.car = car;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", car=" + car +
                '}';
    }
}
重點程式碼
@Test
public void test3() {
    //先建立car這個BeanDefinition
    BeanDefinition carBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Car.class.getName()).addPropertyValue("name", "奧迪").getBeanDefinition();
    //建立User這個BeanDefinition
    BeanDefinition userBeanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class.getName()).
            addPropertyValue("name", "路人甲Java").
            addPropertyReference("car", "car"). //@1
            getBeanDefinition();

    //建立spring容器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    //呼叫registerBeanDefinition向容器中註冊bean
    factory.registerBeanDefinition("car", carBeanDefinition); 
    factory.registerBeanDefinition("user", userBeanDefinition);
    System.out.println(factory.getBean("car"));
    System.out.println(factory.getBean("user"));
}

@1:注入依賴的bean,需要使用addPropertyReference方法,2個引數,第一個為屬性的名稱,第二個為需要注入的bean的名稱

上面程式碼等效於

<bean id="car" class="com.javacode2018.lesson002.demo1.Car">
    <property name="name" value="奧迪"/>
</bean>

<bean id="user" class="com.javacode2018.lesson002.demo1.User">
    <property name="name" value="路人甲Java"/>
    <property name="car" ref="car"/>
</bean>
執行輸出
Car{name='奧迪'}
User{name='路人甲Java', car=Car{name='奧迪'}}

案例4:來2個有父子關係的bean

@Test
public void test4() {
    //先建立car這個BeanDefinition
    BeanDefinition carBeanDefinition1 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "保時捷").
            getBeanDefinition();

    BeanDefinition carBeanDefinition2 = BeanDefinitionBuilder.
            genericBeanDefinition(). //內部生成一個GenericBeanDefinition物件
            setParentName("car1"). //@1:設定父bean的名稱為car1
            getBeanDefinition();

    //建立spring容器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    //呼叫registerBeanDefinition向容器中註冊bean
    //註冊car1->carBeanDefinition1
    factory.registerBeanDefinition("car1", carBeanDefinition1);
    //註冊car2->carBeanDefinition2
    factory.registerBeanDefinition("car2", carBeanDefinition2);
    //從容器中獲取car1
    System.out.println(String.format("car1->%s", factory.getBean("car1")));
    //從容器中獲取car2
    System.out.println(String.format("car2->%s", factory.getBean("car2")));
}

等效於

<bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
    <property name="name" value="保時捷"/>
</bean>
<bean id="car2" parent="car1" />
執行輸出
car1->Car{name='保時捷'}
car2->Car{name='保時捷'}

案例5:通過api設定(Map、Set、List)屬性

下面我們來演示注入List、Map、Set,內部元素為普通型別及其他bean元素。

來個類
package com.javacode2018.lesson002.demo1;

import java.util.List;
import java.util.Map;
import java.util.Set;

public class CompositeObj {

    private String name;
    private Integer salary;

    private Car car1;
    private List<String> stringList;
    private List<Car> carList;

    private Set<String> stringSet;
    private Set<Car> carSet;

    private Map<String, String> stringMap;
    private Map<String, Car> stringCarMap;

    //此處省略了get和set方法,大家寫的時候記得補上

    @Override
    public String toString() {
        return "CompositeObj{" +
                "name='" + name + '\'' +
                "\n\t\t\t, salary=" + salary +
                "\n\t\t\t, car1=" + car1 +
                "\n\t\t\t, stringList=" + stringList +
                "\n\t\t\t, carList=" + carList +
                "\n\t\t\t, stringSet=" + stringSet +
                "\n\t\t\t, carSet=" + carSet +
                "\n\t\t\t, stringMap=" + stringMap +
                "\n\t\t\t, stringCarMap=" + stringCarMap +
                '}';
    }
}

注意:上面省略了get和set方法,大家寫的時候記得補上

先用xml來定義一個CompositeObj的bean,如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="奧迪"/>
    </bean>

    <bean id="car2" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="保時捷"/>
    </bean>

    <bean id="compositeObj" class="com.javacode2018.lesson002.demo1.CompositeObj">
        <property name="name" value="路人甲Java"/>
        <property name="salary" value="50000"/>
        <property name="car1" ref="car1"/>
        <property name="stringList">
            <list>
                <value>java高併發系列</value>
                <value>mysql系列</value>
                <value>maven高手系列</value>
            </list>
        </property>
        <property name="carList">
            <list>
                <ref bean="car1"/>
                <ref bean="car2"/>
            </list>
        </property>
        <property name="stringSet">
            <set>
                <value>java高併發系列</value>
                <value>mysql系列</value>
                <value>maven高手系列</value>
            </set>
        </property>
        <property name="carSet">
            <set>
                <ref bean="car1"/>
                <ref bean="car2"/>
            </set>
        </property>
        <property name="stringMap">
            <map>
                <entry key="系列1" value="java高併發系列"/>
                <entry key="系列2" value="Maven高手系列"/>
                <entry key="系列3" value="mysql系列"/>
            </map>
        </property>
        <property name="stringCarMap">
            <map>
                <entry key="car1" value-ref="car1"/>
                <entry key="car2" value-ref="car2"/>
            </map>
        </property>
    </bean>
</beans>
下面我們採用純api的方式實現,如下
@Test
public void test5() {
    //定義car1
    BeanDefinition car1 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "奧迪").
            getBeanDefinition();
    //定義car2
    BeanDefinition car2 = BeanDefinitionBuilder.
            genericBeanDefinition(Car.class).
            addPropertyValue("name", "保時捷").
            getBeanDefinition();

    //定義CompositeObj這個bean
    //建立stringList這個屬性對應的值
    ManagedList<String> stringList = new ManagedList<>();
    stringList.addAll(Arrays.asList("java高併發系列", "mysql系列", "maven高手系列"));

    //建立carList這個屬性對應的值,內部引用其他兩個bean的名稱[car1,car2]
    ManagedList<RuntimeBeanReference> carList = new ManagedList<>();
    carList.add(new RuntimeBeanReference("car1"));
    carList.add(new RuntimeBeanReference("car2"));

    //建立stringList這個屬性對應的值
    ManagedSet<String> stringSet = new ManagedSet<>();
    stringSet.addAll(Arrays.asList("java高併發系列", "mysql系列", "maven高手系列"));

    //建立carSet這個屬性對應的值,內部引用其他兩個bean的名稱[car1,car2]
    ManagedList<RuntimeBeanReference> carSet = new ManagedList<>();
    carSet.add(new RuntimeBeanReference("car1"));
    carSet.add(new RuntimeBeanReference("car2"));

    //建立stringMap這個屬性對應的值
    ManagedMap<String, String> stringMap = new ManagedMap<>();
    stringMap.put("系列1", "java高併發系列");
    stringMap.put("系列2", "Maven高手系列");
    stringMap.put("系列3", "mysql系列");

    ManagedMap<String, RuntimeBeanReference> stringCarMap = new ManagedMap<>();
    stringCarMap.put("car1", new RuntimeBeanReference("car1"));
    stringCarMap.put("car2", new RuntimeBeanReference("car2"));


    //下面我們使用原生的api來建立BeanDefinition
    GenericBeanDefinition compositeObj = new GenericBeanDefinition();
    compositeObj.setBeanClassName(CompositeObj.class.getName());
    compositeObj.getPropertyValues().add("name", "路人甲Java").
            add("salary", 50000).
            add("car1", new RuntimeBeanReference("car1")).
            add("stringList", stringList).
            add("carList", carList).
            add("stringSet", stringSet).
            add("carSet", carSet).
            add("stringMap", stringMap).
            add("stringCarMap", stringCarMap);

    //將上面bean 註冊到容器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition("car1", car1);
    factory.registerBeanDefinition("car2", car2);
    factory.registerBeanDefinition("compositeObj", compositeObj);

    //下面我們將容器中所有的bean輸出
    for (String beanName : factory.getBeanDefinitionNames()) {
        System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
    }
}

有幾點需要說一下:

RuntimeBeanReference:用來表示bean引用型別,類似於xml中的ref

ManagedList:屬性如果是List型別的,t需要用到這個類進行操作,這個類繼承了ArrayList

ManagedSet:屬性如果是Set型別的,t需要用到這個類進行操作,這個類繼承了LinkedHashSet

ManagedMap:屬性如果是Map型別的,t需要用到這個類進行操作,這個類繼承了LinkedHashMap

上面也就是這幾個類結合的結果。

看一下效果,執行輸出
car1->Car{name='奧迪'}
car2->Car{name='保時捷'}
compositeObj->CompositeObj{name='路人甲Java'
            , salary=50000
            , car1=Car{name='奧迪'}
            , stringList=[java高併發系列, mysql系列, maven高手系列]
            , carList=[Car{name='奧迪'}, Car{name='保時捷'}]
            , stringSet=[java高併發系列, mysql系列, maven高手系列]
            , carSet=[Car{name='奧迪'}, Car{name='保時捷'}]
            , stringMap={系列1=java高併發系列, 系列2=Maven高手系列, 系列3=mysql系列}
            , stringCarMap={car1=Car{name='奧迪'}, car2=Car{name='保時捷'}}}

Xml檔案方式

這種方式已經講過很多次了,大家也比較熟悉,即通過xml的方式來定義bean,如下

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <bean id="bean名稱" class="bean完整類名"/>

</beans>

xml中的bean配置資訊會被解析器解析為BeanDefinition物件,一會在第二階段詳解。

properties檔案的方式

這種方式估計大家比較陌生,將bean定義資訊放在properties檔案中,然後通過解析器將配置資訊解析為BeanDefinition物件。

properties內容格式如下:

employee.(class)=MyClass       // 等同於:<bean class="MyClass" />
employee.(abstract)=true       // 等同於:<bean abstract="true" />
employee.group=Insurance       // 為屬性設定值,等同於:<property name="group" value="Insurance" />
employee.usesDialUp=false      // 為employee這個bean中的usesDialUp屬性設定值,等同於:等同於:<property name="usesDialUp" value="false" />

salesrep.(parent)=employee     // 定義了一個id為salesrep的bean,指定父bean為employee,等同於:<bean id="salesrep" parent="employee" />
salesrep.(lazy-init)=true      // 設定延遲初始化,等同於:<bean lazy-init="true" />
salesrep.manager(ref)=tony     // 設定這個bean的manager屬性值,是另外一個bean,名稱為tony,等同於:<property name="manager" ref="tony" />
salesrep.department=Sales      // 等同於:<property name="department" value="Sales" />

techie.(parent)=employee       // 定義了一個id為techie的bean,指定父bean為employee,等同於:<bean id="techie" parent="employee" />
techie.(scope)=prototype       // 設定bean的作用域,等同於<bean scope="prototype" />
techie.manager(ref)=jeff       // 等同於:<property name="manager" ref="jeff" />
techie.department=Engineering  // <property name="department" value="Engineering" />
techie.usesDialUp=true         // <property name="usesDialUp" value="true" />

ceo.$0(ref)=secretary          // 設定建構函式第1個引數值,等同於:<constructor-arg index="0" ref="secretary" />
ceo.$1=1000000                 // 設定建構函式第2個引數值,等同於:<constructor-arg index="1" value="1000000" />

註解的方式

常見的2種:

  1. 類上標註@Compontent註解來定義一個bean

  2. 配置類中使用@Bean註解來定義bean

小結

bean註冊者只識別BeanDefinition物件,不管什麼方式最後都會將這些bean定義的資訊轉換為BeanDefinition物件,然後註冊到spring容器中。

階段2:Bean元資訊解析階段

Bean元資訊的解析就是將各種方式定義的bean配置資訊解析為BeanDefinition物件。

Bean元資訊的解析主要有3種方式

  1. xml檔案定義bean的解析

  2. properties檔案定義bean的解析

  3. 註解方式定義bean的解析

XML方式解析:XmlBeanDefinitionReader

spring中提供了一個類XmlBeanDefinitionReader,將xml中定義的bean解析為BeanDefinition物件。

直接來看案例程式碼

來一個bean xml配置檔案

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <bean id="car" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="奧迪"/>
    </bean>

    <bean id="car1" class="com.javacode2018.lesson002.demo1.Car">
        <property name="name" value="保時捷"/>
    </bean>

    <bean id="car2" parent="car1"/>

    <bean id="user" class="com.javacode2018.lesson002.demo1.User">
        <property name="name" value="路人甲Java"/>
        <property name="car" ref="car1"/>
    </bean>
</beans>

上面註冊了4個bean,不多解釋了。

將bean xml解析為BeanDefinition物件

/**
 * xml方式bean配置資訊解析
 */
@Test
public void test1() {
    //定義一個spring容器,這個容器預設實現了BeanDefinitionRegistry,所以本身就是一個bean註冊器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //定義一個xml的BeanDefinition讀取器,需要傳遞一個BeanDefinitionRegistry(bean註冊器)物件
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);

    //指定bean xml配置檔案的位置
    String location = "classpath:/com/javacode2018/lesson002/demo2/beans.xml";
    //通過XmlBeanDefinitionReader載入bean xml檔案,然後將解析產生的BeanDefinition註冊到容器容器中
    int countBean = xmlBeanDefinitionReader.loadBeanDefinitions(location);
    System.out.println(String.format("共註冊了 %s 個bean", countBean));

    //列印出註冊的bean的配置資訊
    for (String beanName : factory.getBeanDefinitionNames()) {
        //通過名稱從容器中獲取對應的BeanDefinition資訊
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //獲取BeanDefinition具體使用的是哪個類
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //通過名稱獲取bean物件
        Object bean = factory.getBean(beanName);
        //列印輸出
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
        System.out.println("    beanDefinition:" + beanDefinition);
        System.out.println("    bean:" + bean);
    }
}

上面註釋比較詳細,這裡就不解釋了。

注意一點:建立XmlBeanDefinitionReader的時候需要傳遞一個bean註冊器(BeanDefinitionRegistry),解析過程中生成的BeanDefinition會丟到bean註冊器中。

執行輸出

共註冊了 4 個bean
car:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean:Car{name='奧迪'}
car1:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean:Car{name='保時捷'}
car2:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean with parent 'car1': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean:Car{name='保時捷'}
user:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.User]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo2/beans.xml]
    bean:User{name='路人甲Java', car=Car{name='奧迪'}}

上面的輸出認真看一下,這幾個BeanDefinition都是GenericBeanDefinition這種型別的,也就是說xml中定義的bean被解析之後都是通過GenericBeanDefinition這種型別表示的。

properties檔案定義bean的解析:PropertiesBeanDefinitionReader

spring中提供了一個類XmlBeanDefinitionReader,將xml中定義的bean解析為BeanDefinition物件,過程和xml的方式類似。

來看案例程式碼。

下面通過properties檔案的方式實現上面xml方式定義的bean。

來個properties檔案:beans.properties

car.(class)=com.javacode2018.lesson002.demo1.Car
car.name=奧迪

car1.(class)=com.javacode2018.lesson002.demo1.Car
car1.name=保時捷

car2.(parent)=car1

user.(class)=com.javacode2018.lesson002.demo1.User
user.name=路人甲Java
user.car(ref)=car

將bean properties檔案解析為BeanDefinition物件

/**
 * properties檔案方式bean配置資訊解析
 */
@Test
public void test2() {
    //定義一個spring容器,這個容器預設實現了BeanDefinitionRegistry,所以本身就是一個bean註冊器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //定義一個properties的BeanDefinition讀取器,需要傳遞一個BeanDefinitionRegistry(bean註冊器)物件
    PropertiesBeanDefinitionReader propertiesBeanDefinitionReader = new PropertiesBeanDefinitionReader(factory);

    //指定bean xml配置檔案的位置
    String location = "classpath:/com/javacode2018/lesson002/demo2/beans.properties";
    //通過PropertiesBeanDefinitionReader載入bean properties檔案,然後將解析產生的BeanDefinition註冊到容器容器中
    int countBean = propertiesBeanDefinitionReader.loadBeanDefinitions(location);
    System.out.println(String.format("共註冊了 %s 個bean", countBean));

    //列印出註冊的bean的配置資訊
    for (String beanName : factory.getBeanDefinitionNames()) {
        //通過名稱從容器中獲取對應的BeanDefinition資訊
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //獲取BeanDefinition具體使用的是哪個類
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //通過名稱獲取bean物件
        Object bean = factory.getBean(beanName);
        //列印輸出
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
        System.out.println("    beanDefinition:" + beanDefinition);
        System.out.println("    bean:" + bean);
    }
}

執行輸出

user:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.User]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:User{name='路人甲Java', car=Car{name='奧迪'}}
car1:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:Car{name='保時捷'}
car:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo1.Car]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:Car{name='奧迪'}
car2:
    beanDefinitionClassName:org.springframework.beans.factory.support.GenericBeanDefinition
    beanDefinition:Generic bean with parent 'car1': class [null]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:Car{name='保時捷'}

輸出和xml方式輸出基本上一致。

properties方式使用起來並不是太方便,所以平時我們很少看到有人使用。

註解方式:PropertiesBeanDefinitionReader

註解的方式定義的bean,需要使用PropertiesBeanDefinitionReader這個類來進行解析,方式也和上面2種方式類似,直接來看案例。

通過註解來標註2個類

Service1
package com.javacode2018.lesson002.demo2;


import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Primary
@Lazy
public class Service1 {
}

這個類上面使用了3個註解,這些註解前面都介紹過,可以用來配置bean的資訊

上面這個bean是個多例的。

Service2
package com.javacode2018.lesson002.demo2;

import org.springframework.beans.factory.annotation.Autowired;

public class Service2 {

    @Autowired
    private Service1 service1; //@1

    @Override
    public String toString() {
        return "Service2{" +
                "service1=" + service1 +
                '}';
    }
}

@1:標註了@Autowired,說明需要注入這個物件

註解定義的bean解析為BeanDefinition,如下:

@Test
public void test3() {
    //定義一個spring容器,這個容器預設實現了BeanDefinitionRegistry,所以本身就是一個bean註冊器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //定義一個註解方式的BeanDefinition讀取器,需要傳遞一個BeanDefinitionRegistry(bean註冊器)物件
    AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);

    //通過PropertiesBeanDefinitionReader載入bean properties檔案,然後將解析產生的BeanDefinition註冊到容器容器中
    annotatedBeanDefinitionReader.register(Service1.class, Service2.class);

    //列印出註冊的bean的配置資訊
    for (String beanName : new String[]{"service1", "service2"}) {
        //通過名稱從容器中獲取對應的BeanDefinition資訊
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //獲取BeanDefinition具體使用的是哪個類
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //通過名稱獲取bean物件
        Object bean = factory.getBean(beanName);
        //列印輸出
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
        System.out.println("    beanDefinition:" + beanDefinition);
        System.out.println("    bean:" + bean);
    }
}

執行輸出

service1:
    beanDefinitionClassName:org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo2.Service1]; scope=prototype; abstract=false; lazyInit=true; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:com.javacode2018.lesson002.demo2.Service1@21a947fe
service2:
    beanDefinitionClassName:org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo2.Service2]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:Service2{service1=null}

輸出中可以看出service1這個bean的beanDefinition中lazyInit確實為true,primary也為true,scope為prototype,說明類Service1註解上標註3個註解資訊被解析之後放在了beanDefinition中。

注意下:最後一行中的service1為什麼為null,不是標註了@Autowired麼?

這個地方提前劇透一下,看不懂的沒關係,這篇文章都結束之後,就明白了。

調整一下上面的程式碼,加上下面@1這行程式碼,如下:

@Test
public void test3() {
    //定義一個spring容器,這個容器預設實現了BeanDefinitionRegistry,所以本身就是一個bean註冊器
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //定義一個註解方式的BeanDefinition讀取器,需要傳遞一個BeanDefinitionRegistry(bean註冊器)物件
    AnnotatedBeanDefinitionReader annotatedBeanDefinitionReader = new AnnotatedBeanDefinitionReader(factory);

    //通過PropertiesBeanDefinitionReader載入bean properties檔案,然後將解析產生的BeanDefinition註冊到容器容器中
    annotatedBeanDefinitionReader.register(Service1.class, Service2.class);

    factory.getBeansOfType(BeanPostProcessor.class).values().forEach(factory::addBeanPostProcessor); // @1
    //列印出註冊的bean的配置資訊
    for (String beanName : new String[]{"service1", "service2"}) {
        //通過名稱從容器中獲取對應的BeanDefinition資訊
        BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
        //獲取BeanDefinition具體使用的是哪個類
        String beanDefinitionClassName = beanDefinition.getClass().getName();
        //通過名稱獲取bean物件
        Object bean = factory.getBean(beanName);
        //列印輸出
        System.out.println(beanName + ":");
        System.out.println("    beanDefinitionClassName:" + beanDefinitionClassName);
        System.out.println("    beanDefinition:" + beanDefinition);
        System.out.println("    bean:" + bean);
    }
}

再次執行一下,最後一行有值了:

service1:
    beanDefinitionClassName:org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo2.Service1]; scope=prototype; abstract=false; lazyInit=true; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=true; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:com.javacode2018.lesson002.demo2.Service1@564718df
service2:
    beanDefinitionClassName:org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
    beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo2.Service2]; scope=singleton; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
    bean:Service2{service1=com.javacode2018.lesson002.demo2.Service1@52aa2946}

目前進行到第二個階段了,還有14個階段,本文內容比較長,建議先收藏起來,慢慢看,我們們繼續。

階段3:Spring Bean註冊階段

bean註冊階段需要用到一個非常重要的介面:BeanDefinitionRegistry

Bean註冊介面:BeanDefinitionRegistry

這個介面中定義了註冊bean常用到的一些方法,原始碼如下:

public interface BeanDefinitionRegistry extends AliasRegistry {

    /**
     * 註冊一個新的bean定義
     * beanName:bean的名稱
     * beanDefinition:bean定義資訊
     */
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException;

    /**
     * 通過bean名稱移除已註冊的bean
     * beanName:bean名稱
     */
    void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    /**
     * 通過名稱獲取bean的定義資訊
     * beanName:bean名稱
     */
    BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;

    /**
     * 檢視beanName是否註冊過
     */
    boolean containsBeanDefinition(String beanName);

    /**
     * 獲取已經定義(註冊)的bean名稱列表
     */
    String[] getBeanDefinitionNames();

    /**
     * 返回註冊器中已註冊的bean數量
     */
    int getBeanDefinitionCount();

    /**
     * 確定給定的bean名稱或者別名是否已在此登錄檔中使用
     * beanName:可以是bean名稱或者bean的別名
     */
    boolean isBeanNameInUse(String beanName);

}

別名註冊介面:AliasRegistry

BeanDefinitionRegistry介面繼承了AliasRegistry介面,這個介面中定義了操作bean別名的一些方法,看一下其原始碼:

public interface AliasRegistry {

    /**
     * 給name指定別名alias
     */
    void registerAlias(String name, String alias);

    /**
     * 從此登錄檔中刪除指定的別名
     */
    void removeAlias(String alias);

    /**
     * 判斷name是否作為別名已經被使用了
     */
    boolean isAlias(String name);

    /**
     * 返回name對應的所有別名
     */
    String[] getAliases(String name);

}

BeanDefinitionRegistry唯一實現:DefaultListableBeanFactory

spring中BeanDefinitionRegistry介面有一個唯一的實現類:

org.springframework.beans.factory.support.DefaultListableBeanFactory

大家可能看到有很多類也實現了BeanDefinitionRegistry介面,比如我們經常用到的AnnotationConfigApplicationContext,但實際上其內部是轉發給了DefaultListableBeanFactory進行處理的,所以真正實現這個介面的類是DefaultListableBeanFactory

大家再回頭看一下開頭的幾個案例,都使用的是DefaultListableBeanFactory作為bean註冊器,此時你們應該可以理解為什麼了。

下面我們來個案例演示一下上面常用的一些方法。

案例

程式碼

package com.javacode2018.lesson002.demo3;

import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import java.util.Arrays;

/**
 * BeanDefinitionRegistry 案例
 */
public class BeanDefinitionRegistryTest {

    @Test
    public void test1() {
        //建立一個bean工廠,這個預設實現了BeanDefinitionRegistry介面,所以也是一個bean註冊器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //定義一個bean
        GenericBeanDefinition nameBdf = new GenericBeanDefinition();
        nameBdf.setBeanClass(String.class);
        nameBdf.getConstructorArgumentValues().addIndexedArgumentValue(0, "路人甲Java");

        //將bean註冊到容器中
        factory.registerBeanDefinition("name", nameBdf);

        //通過名稱獲取BeanDefinition
        System.out.println(factory.getBeanDefinition("name"));
        //通過名稱判斷是否註冊過BeanDefinition
        System.out.println(factory.containsBeanDefinition("name"));
        //獲取所有註冊的名稱
        System.out.println(Arrays.asList(factory.getBeanDefinitionNames()));
        //獲取已註冊的BeanDefinition的數量
        System.out.println(factory.getBeanDefinitionCount());
        //判斷指定的name是否使用過
        System.out.println(factory.isBeanNameInUse("name"));

        //別名相關方法
        //為name註冊2個別名
        factory.registerAlias("name", "alias-name-1");
        factory.registerAlias("name", "alias-name-2");

        //判斷alias-name-1是否已被作為別名使用
        System.out.println(factory.isAlias("alias-name-1"));

        //通過名稱獲取對應的所有別名
        System.out.println(Arrays.asList(factory.getAliases("name")));

        //最後我們再來獲取一下這個bean
        System.out.println(factory.getBean("name"));


    }
}

執行輸出

Generic bean: class [java.lang.String]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
true
[name]
1
true
true
[alias-name-2, alias-name-1]
路人甲Java

下面要介紹的從階段4到階段14,也就是從:BeanDefinition合併階段Bean初始化完成階段,都是在呼叫getBean從容器中獲取bean物件的過程中傳送的操作,要注意細看了,大家下去了建議去看getBean這個方法的原始碼,以下過程均來自於這個方法:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

階段4:BeanDefinition合併階段

合併階段是做什麼的?

可能我們定義bean的時候有父子bean關係,此時子BeanDefinition中的資訊是不完整的,比如設定屬性的時候配置在父BeanDefinition中,此時子BeanDefinition中是沒有這些資訊的,需要將子bean的BeanDefinition和父bean的BeanDefinition進行合併,得到最終的一個RootBeanDefinition,合併之後得到的RootBeanDefinition包含bean定義的所有資訊,包含了從父bean中繼繼承過來的所有資訊,後續bean的所有建立工作就是依靠合併之後BeanDefinition來進行的。

合併BeanDefinition會使用下面這個方法:

org.springframework.beans.factory.support.AbstractBeanFactory#getMergedBeanDefinition

bean定義可能存在多級父子關係,合併的時候進進行遞迴合併,最終得到一個包含完整資訊的RootBeanDefinition

案例

來一個普通的類

package com.javacode2018.lesson002.demo4;

public class LessonModel {
    //課程名稱
    private String name;
    //課時
    private int lessonCount;
    //描述資訊
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLessonCount() {
        return lessonCount;
    }

    public void setLessonCount(int lessonCount) {
        this.lessonCount = lessonCount;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "LessonModel{" +
                "name='" + name + '\'' +
                ", lessonCount=" + lessonCount +
                ", description='" + description + '\'' +
                '}';
    }
}

通過xml定義3個具有父子關係的bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">

    <bean id="lesson1" class="com.javacode2018.lesson002.demo4.LessonModel"/>

    <bean id="lesson2" parent="lesson1">
        <property name="name" value="spring高手系列"/>
        <property name="lessonCount" value="100"/>
    </bean>

    <bean id="lesson3" parent="lesson2">
        <property name="description" value="路人甲Java帶你學spring,超越90%開發者!"/>
    </bean>

</beans>

lesson2相當於lesson1的兒子,lesson3相當於lesson1的孫子。

解析xml註冊bean

下面將解析xml,進行bean註冊,然後遍歷輸出bean的名稱,解析過程中註冊的原始的BeanDefinition,合併之後的BeanDefinition,以及合併前後BeanDefinition中的屬性資訊

package com.javacode2018.lesson002.demo4;

import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;

/**
 * BeanDefinition 合併
 */
public class MergedBeanDefinitionTest {
    @Test
    public void test1() {
        //建立bean容器
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        //建立一個bean xml解析器
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(factory);
        //解析bean xml,將解析過程中產生的BeanDefinition註冊到DefaultListableBeanFactory中
        beanDefinitionReader.loadBeanDefinitions("com/javacode2018/lesson002/demo4/beans.xml");
        //遍歷容器中註冊的所有bean資訊
        for (String beanName : factory.getBeanDefinitionNames()) {
            //通過bean名稱獲取原始的註冊的BeanDefinition資訊
            BeanDefinition beanDefinition = factory.getBeanDefinition(beanName);
            //獲取合併之後的BeanDefinition資訊
            BeanDefinition mergedBeanDefinition = factory.getMergedBeanDefinition(beanName);

            System.out.println(beanName);
            System.out.println("解析xml過程中註冊的beanDefinition:" + beanDefinition);
            System.out.println("beanDefinition中的屬性資訊" + beanDefinition.getPropertyValues());
            System.out.println("合併之後得到的mergedBeanDefinition:" + mergedBeanDefinition);
            System.out.println("mergedBeanDefinition中的屬性資訊" + mergedBeanDefinition.getPropertyValues());
            System.out.println("---------------------------");
        }
    }
}

執行輸出

lesson1
解析xml過程中註冊的beanDefinition:Generic bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition中的屬性資訊PropertyValues: length=0
合併之後得到的mergedBeanDefinition:Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition中的屬性資訊PropertyValues: length=0
---------------------------
lesson2
解析xml過程中註冊的beanDefinition:Generic bean with parent 'lesson1': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition中的屬性資訊PropertyValues: length=2; bean property 'name'; bean property 'lessonCount'
合併之後得到的mergedBeanDefinition:Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition中的屬性資訊PropertyValues: length=2; bean property 'name'; bean property 'lessonCount'
---------------------------
lesson3
解析xml過程中註冊的beanDefinition:Generic bean with parent 'lesson2': class [null]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
beanDefinition中的屬性資訊PropertyValues: length=1; bean property 'description'
合併之後得到的mergedBeanDefinition:Root bean: class [com.javacode2018.lesson002.demo4.LessonModel]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in class path resource [com/javacode2018/lesson002/demo4/beans.xml]
mergedBeanDefinition中的屬性資訊PropertyValues: length=3; bean property 'name'; bean property 'lessonCount'; bean property 'description'
---------------------------

從輸出的結果中可以看到,合併之前,BeanDefinition是不完整的,比lesson2和lesson3中的class是null,屬性資訊也不完整,但是合併之後這些資訊都完整了。

合併之前是GenericBeanDefinition型別的,合併之後得到的是RootBeanDefinition型別的。

獲取lesson3合併的BeanDefinition時,內部會遞迴進行合併,先將lesson1和lesson2合併,然後將lesson2再和lesson3合併,最後得到合併之後的BeanDefinition。

後面的階段將使用合併產生的RootBeanDefinition。

階段5:Bean Class載入階段

這個階段就是將bean的class名稱轉換為Class型別的物件。

BeanDefinition中有個Object型別的欄位:beanClass

private volatile Object beanClass;

用來表示bean的class物件,通常這個欄位的值有2種型別,一種是bean對應的Class型別的物件,另一種是bean對應的Class的完整類名,第一種情況不需要解析,第二種情況:即這個欄位是bean的類名的時候,就需要通過類載入器將其轉換為一個Class物件。

此時會對階段4中合併產生的RootBeanDefinition中的beanClass進行解析,將bean的類名轉換為Class物件,然後賦值給beanClass欄位。

原始碼位置:

org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass

上面得到了Bean Class物件以及合併之後的BeanDefinition,下面就開始進入例項化這個物件的階段了。

Bean例項化分為3個階段:前階段、例項化階段、後階段;下面詳解介紹。

階段6:Bean例項化階段

分2個小的階段

  1. Bean例項化前操作

  2. Bean例項化操作

Bean例項化前操作

先來看一下DefaultListableBeanFactory,這個類中有個非常非常重要的欄位:

private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

是一個BeanPostProcessor型別的集合

BeanPostProcessor是一個介面,還有很多子介面,這些介面中提供了很多方法,spring在bean生命週期的不同階段,會呼叫上面這個列表中的BeanPostProcessor中的一些方法,來對生命週期進行擴充套件,bean生命週期中的所有擴充套件點都是依靠這個集合中的BeanPostProcessor來實現的,所以如果大家想對bean的生命週期進行干預,這塊一定要掌握好。

注意:本文中很多以BeanPostProcessor結尾的,都實現了BeanPostProcessor介面,有些是直接實現的,有些是實現了它的子介面。

Bean例項化之前會呼叫一段程式碼:

@Nullable
    protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

這段程式碼在bean例項化之前給開發者留了個口子,開發者自己可以在這個地方直接去建立一個物件作為bean例項,而跳過spring內部例項化bean的過程。

上面程式碼中輪詢beanPostProcessors列表,如果型別是InstantiationAwareBeanPostProcessor, 嘗試呼叫InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation獲取bean的例項物件,如果能夠獲取到,那麼將返回值作為當前bean的例項,那麼spring自帶的例項化bean的過程就被跳過了。

postProcessBeforeInstantiation方法如下:

default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    return null;
}

這個地方給開發者提供了一個擴充套件點,允許開發者在這個方法中直接返回bean的一個例項。

下面我們來個案例看一下。

案例

package com.javacode2018.lesson002.demo5;

import com.javacode2018.lesson002.demo1.Car;
import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.lang.Nullable;

/**
 * bean初始化前階段,會呼叫:{@link org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessBeforeInitialization(Object, String)}
 */
public class InstantiationAwareBeanPostProcessorTest {
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //新增一個BeanPostProcessor:InstantiationAwareBeanPostProcessor
        factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { //@1
            @Nullable
            @Override
            public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
                System.out.println("呼叫postProcessBeforeInstantiation()");
                //發現型別是Car型別的時候,硬編碼建立一個Car物件返回
                if (beanClass == Car.class) {
                    Car car = new Car();
                    car.setName("保時捷");
                    return car;
                }
                return null;
            }
        });

        //定義一個car bean,車名為:奧迪
        AbstractBeanDefinition carBeanDefinition = BeanDefinitionBuilder.
                genericBeanDefinition(Car.class).
                addPropertyValue("name", "奧迪").  //@2
                getBeanDefinition();
        factory.registerBeanDefinition("car", carBeanDefinition);
        //從容器中獲取car這個bean的例項,輸出
        System.out.println(factory.getBean("car"));

    }
}

@1:建立了一個InstantiationAwareBeanPostProcessor,丟到了容器中的BeanPostProcessor列表中

@2:建立了一個car bean,name為奧迪

執行輸出

呼叫postProcessBeforeInstantiation()
Car{name='保時捷'}

bean定義的時候,名稱為:奧迪,最後輸出的為:保時捷

定義和輸出不一致的原因是因為我們在InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法中手動建立了一個例項直接返回了,而不是依靠spring內部去建立這個例項。

小結

實際上,在例項化前階段對bean的建立進行干預的情況,用的非常少,所以大部分bean的建立還會繼續走下面的階段。

Bean例項化操作

這個過程可以幹什麼?

這個過程會通過反射來呼叫bean的構造器來建立bean的例項。

具體需要使用哪個構造器,spring為開發者提供了一個介面,允許開發者自己來判斷用哪個構造器。

看一下這塊的程式碼邏輯:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
        SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
        Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);
        if (ctors != null) {
            return ctors;
        }
    }
}

會呼叫SmartInstantiationAwareBeanPostProcessor介面的determineCandidateConstructors方法,這個方法會返回候選的構造器列表,也可以返回空,看一下這個方法的原始碼:

@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {

    return null;
}

這個方法有個比較重要的實現類

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor

可以將@Autowired標註的方法作為候選構造器返回,有興趣的可以去看一下程式碼。

案例

下面我們來個案例,自定義一個註解,當構造器被這個註解標註的時候,讓spring自動選擇使用這個構造器建立物件。

自定義一個註解

下面這個註解可以標註在構造器上面,使用這個標註之後,建立bean的時候將使用這個構造器。

package com.javacode2018.lesson002.demo6;

import java.lang.annotation.*;

@Target(ElementType.CONSTRUCTOR)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAutowried {
}
來個普通的類

下面這個類3個構造器,其中一個使用@MyAutowried,讓其作為bean例項化的方法。

package com.javacode2018.lesson002.demo6;

public class Person {
    private String name;
    private Integer age;

    public Person() {
        System.out.println("呼叫 Person()");
    }

    @MyAutowried
    public Person(String name) {
        System.out.println("呼叫 Person(String name)");
        this.name = name;
    }

    public Person(String name, Integer age) {
        System.out.println("呼叫 Person(String name, int age)");
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
自定義一個SmartInstantiationAwareBeanPostProcessor

程式碼的邏輯:將@MyAutowried標註的構造器列表返回

package com.javacode2018.lesson002.demo6;


import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor;
import org.springframework.lang.Nullable;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class MySmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
    @Nullable
    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        System.out.println(beanClass);
        System.out.println("呼叫 MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 方法");
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        if (declaredConstructors != null) {
            //獲取有@MyAutowried註解的構造器列表
            List<Constructor<?>> collect = Arrays.stream(declaredConstructors).
                    filter(constructor -> constructor.isAnnotationPresent(MyAutowried.class)).
                    collect(Collectors.toList());
            Constructor[] constructors = collect.toArray(new Constructor[collect.size()]);
            return constructors.length != 0 ? constructors : null;
        } else {
            return null;
        }
    }
}
來個測試用例
package com.javacode2018.lesson002.demo6;

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

/**
 * 通過{@link org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors(Class, String)}來確定使用哪個構造器來建立bean例項
 */
public class SmartInstantiationAwareBeanPostProcessorTest {
    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //建立一個SmartInstantiationAwareBeanPostProcessor,將其新增到容器中
        factory.addBeanPostProcessor(new MySmartInstantiationAwareBeanPostProcessor());

        factory.registerBeanDefinition("name",
                BeanDefinitionBuilder.
                        genericBeanDefinition(String.class).
                        addConstructorArgValue("路人甲Java").
                        getBeanDefinition());

        factory.registerBeanDefinition("age",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Integer.class).
                        addConstructorArgValue(30).
                        getBeanDefinition());

        factory.registerBeanDefinition("person",
                BeanDefinitionBuilder.
                        genericBeanDefinition(Person.class).
                        getBeanDefinition());

        Person person = factory.getBean("person", Person.class);
        System.out.println(person);

    }
}
執行輸出
class com.javacode2018.lesson002.demo6.Person
呼叫 MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 方法
class java.lang.String
呼叫 MySmartInstantiationAwareBeanPostProcessor.determineCandidateConstructors 方法
呼叫 Person(String name)
Person{name='路人甲Java', age=null}

從輸出中可以看出呼叫了Person中標註@MyAutowired標註的構造器。

到目前為止bean例項化階段結束了,繼續進入後面的階段。

階段7:合併後的BeanDefinition處理

這塊的原始碼如下

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof MergedBeanDefinitionPostProcessor) {
                MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
                bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
            }
        }
    }

會呼叫MergedBeanDefinitionPostProcessor介面的postProcessMergedBeanDefinition方法,看一下這個方法的原始碼:

void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

spring會輪詢BeanPostProcessor,依次呼叫MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition

第一個引數為beanDefinition,表示合併之後的RootBeanDefinition,我們可以在這個方法內部對合並之後的BeanDefinition進行再次處理

postProcessMergedBeanDefinition有2個實現類,前面我們介紹過,用的也比較多,面試的時候也會經常問的:

org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
在 postProcessMergedBeanDefinition 方法中對 @Autowired、@Value 標註的方法、欄位進行快取

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
在 postProcessMergedBeanDefinition 方法中對 @Resource 標註的欄位、@Resource 標註的方法、 @PostConstruct 標註的欄位、 @PreDestroy標註的方法進行快取

階段8:Bean屬性設定階段

屬性設定階段分為3個小的階段

  • 例項化後階段

  • Bean屬性賦值前處理

  • Bean屬性賦值

例項化後階段

會呼叫InstantiationAwareBeanPostProcessor介面的postProcessAfterInstantiation這個方法,呼叫邏輯如下:

看一下具體的呼叫邏輯如下:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            return;
        }
    }
}

postProcessAfterInstantiation方法返回false的時候,後續的Bean屬性賦值前處理、Bean屬性賦值都會被跳過了。

來看一下postProcessAfterInstantiation這個方法的定義

default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
    return true;
}

來看個案例,案例中返回false,跳過屬性的賦值操作。

案例

來個類
package com.javacode2018.lesson002.demo7;


public class UserModel {
    private String name;
    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "UserModel{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
測試用例

下面很簡單,來註冊一個UserModel的bean

package com.javacode2018.lesson002.demo7;


import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * {@link InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation(java.lang.Object, java.lang.String)}
 * 返回false,可以阻止bean屬性的賦值
 */
public class InstantiationAwareBeanPostProcessoryTest1 {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        factory.registerBeanDefinition("user1", BeanDefinitionBuilder.
                genericBeanDefinition(UserModel.class).
                addPropertyValue("name", "路人甲Java").
                addPropertyValue("age", 30).
                getBeanDefinition());

        factory.registerBeanDefinition("user2", BeanDefinitionBuilder.
                genericBeanDefinition(UserModel.class).
                addPropertyValue("name", "劉德華").
                addPropertyValue("age", 50).
                getBeanDefinition());

        for (String beanName : factory.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
        }
    }

}

上面定義了2個bean:[user1,user2],獲取之後輸出

執行輸出
user1->UserModel{name='路人甲Java', age=30}
user2->UserModel{name='劉德華', age=50}

此時UserModel中2個屬性都是有值的。

下面來阻止user1的賦值,對程式碼進行改造,加入下面程式碼:

factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() {
    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if ("user1".equals(beanName)) {
            return false;
        } else {
            return true;
        }
    }
});

再次執行測試輸出:

user1->UserModel{name='null', age=null}
user2->UserModel{name='劉德華', age=50}

user1的屬性賦值被跳過了。

Bean屬性賦值前階段

這個階段會呼叫InstantiationAwareBeanPostProcessor介面的postProcessProperties方法,呼叫邏輯:

for (BeanPostProcessor bp : getBeanPostProcessors()) {
    if (bp instanceof InstantiationAwareBeanPostProcessor) {
        InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
        PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
            if (filteredPds == null) {
                filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                return;
            }
        }
        pvs = pvsToUse;
    }
}

從上面可以看出,如果InstantiationAwareBeanPostProcessor中的postProcessPropertiespostProcessPropertyValues都返回空的時候,表示這個bean不需要設定屬性,直接返回了,直接進入下一個階段。

來看一下postProcessProperties這個方法的定義:

@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
    throws BeansException {

    return null;
}

PropertyValues中儲存了bean例項物件中所有屬性值的設定,所以我們可以在這個這個方法中對PropertyValues值進行修改。

這個方法有2個比較重要的實現類

AutowiredAnnotationBeanPostProcessor在這個方法中對@Autowired、@Value標註的欄位、方法注入值。
CommonAnnotationBeanPostProcessor在這個方法中對@Resource標註的欄位和方法注入值。

來個案例,我們在案例中對pvs進行修改。

案例

案例程式碼
@Test
public void test3() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    factory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { // @0
        @Nullable
        @Override
        public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
            if ("user1".equals(beanName)) {
                if (pvs == null) {
                    pvs = new MutablePropertyValues();
                }
                if (pvs instanceof MutablePropertyValues) {
                    MutablePropertyValues mpvs = (MutablePropertyValues) pvs;
                    //將姓名設定為:路人
                    mpvs.add("name", "路人");
                    //將年齡屬性的值修改為18
                    mpvs.add("age", 18);
                }
            }
            return null;
        }
    });

    //注意 user1 這個沒有給屬性設定值
    factory.registerBeanDefinition("user1", BeanDefinitionBuilder.
            genericBeanDefinition(UserModel.class).
            getBeanDefinition()); //@1

    factory.registerBeanDefinition("user2", BeanDefinitionBuilder.
            genericBeanDefinition(UserModel.class).
            addPropertyValue("name", "劉德華").
            addPropertyValue("age", 50).
            getBeanDefinition());

    for (String beanName : factory.getBeanDefinitionNames()) {
        System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
    }
}

@1:user1這個bean沒有設定屬性的值

@0:這個實現 org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor#postProcessProperties 方法,在其內部對 user1 這個bean進行屬性值資訊進行修改。

執行輸出
user1->UserModel{name='路人', age=18}
user2->UserModel{name='劉德華', age=50}

上面過程都ok,進入bean賦值操作

Bean屬性賦值階段

這個過程比較簡單了,迴圈處理PropertyValues中的屬性值資訊,通過反射呼叫set方法將屬性的值設定到bean例項中。

PropertyValues中的值是通過bean xml中property元素配置的,或者呼叫MutablePropertyValues中add方法設定的值。

階段9:Bean初始化階段

這個階段分為5個小的階段

  • Bean Aware介面回撥

  • Bean初始化前操作

  • Bean初始化操作

  • Bean初始化後操作

  • Bean初始化完成操作

Bean Aware介面回撥

這塊的原始碼:

private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

如果我們的bean例項實現了上面的介面,會按照下面的順序依次進行呼叫:

BeanNameAware:將bean的名稱注入進去
BeanClassLoaderAware:將BeanClassLoader注入進去
BeanFactoryAware:將BeanFactory注入進去

來個案例感受一下

來個類,實現上面3個介面。

package com.javacode2018.lesson002.demo8;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;

public class AwareBean implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {

    @Override
    public void setBeanName(String name) {
        System.out.println("setBeanName:" + name);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("setBeanFactory:" + beanFactory);
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("setBeanClassLoader:" + classLoader);
    }

}

來個測試類,建立上面這個物件的的bean

package com.javacode2018.lesson002.demo8;

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

public class InvokeAwareTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        factory.registerBeanDefinition("awareBean", BeanDefinitionBuilder.genericBeanDefinition(AwareBean.class).getBeanDefinition());
        //呼叫getBean方法獲取bean,將觸發bean的初始化
        factory.getBean("awareBean");
    }
}

執行輸出

setBeanName:awareBean
setBeanClassLoader:sun.misc.Launcher$AppClassLoader@18b4aac2
setBeanFactory:org.springframework.beans.factory.support.DefaultListableBeanFactory@5bb21b69: defining beans [awareBean]; root of factory hierarchy

Bean初始化前操作

這個階段的原始碼:

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

會呼叫BeanPostProcessor的postProcessBeforeInitialization方法,若返回null,當前方法將結束。

通常稱postProcessBeforeInitialization這個方法為:bean初始化前操作。

這個介面有2個實現類,比較重要:

org.springframework.context.support.ApplicationContextAwareProcessor
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

ApplicationContextAwareProcessor注入6個Aware介面物件

如果bean實現了下面的介面,在ApplicationContextAwareProcessor#postProcessBeforeInitialization中會依次呼叫下面介面中的方法,將Aware字首對應的物件注入到bean例項中。

EnvironmentAware:注入Environment物件
EmbeddedValueResolverAware:注入EmbeddedValueResolver物件
ResourceLoaderAware:注入ResourceLoader物件
ApplicationEventPublisherAware:注入ApplicationEventPublisher物件
MessageSourceAware:注入MessageSource物件
ApplicationContextAware:注入ApplicationContext物件

從名稱上可以看出這個類以ApplicationContext開頭的,說明這個類只能在ApplicationContext環境中使用。

CommonAnnotationBeanPostProcessor呼叫@PostConstruct標註的方法

CommonAnnotationBeanPostProcessor#postProcessBeforeInitialization中會呼叫bean中所有標註@PostConstruct註解的方法

來個案例,感受一下。

案例

來個類

下面的類有2個方法標註了@PostConstruct,並且實現了上面說的那6個Aware介面。

package com.javacode2018.lesson002.demo9;

import org.springframework.beans.BeansException;
import org.springframework.context.*;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringValueResolver;

import javax.annotation.PostConstruct;

public class Bean1 implements EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware {

    @PostConstruct
    public void postConstruct1() { //@1
        System.out.println("postConstruct1()");
    }

    @PostConstruct
    public void postConstruct2() { //@2
        System.out.println("postConstruct2()");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("setApplicationContext:" + applicationContext);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("setApplicationEventPublisher:" + applicationEventPublisher);
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        System.out.println("setEmbeddedValueResolver:" + resolver);
    }

    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("setEnvironment:" + environment.getClass());
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        System.out.println("setMessageSource:" + messageSource);
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        System.out.println("setResourceLoader:" + resourceLoader);
    }
}
來個測試案例
package com.javacode2018.lesson002.demo9;


import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class PostProcessBeforeInitializationTest {

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(Bean1.class);
        context.refresh();
    }
}
執行輸出
setEmbeddedValueResolver:org.springframework.beans.factory.config.EmbeddedValueResolver@15b204a1
setResourceLoader:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setApplicationEventPublisher:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setMessageSource:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
setApplicationContext:org.springframework.context.annotation.AnnotationConfigApplicationContext@64bf3bbf, started on Sun Apr 05 21:16:00 CST 2020
postConstruct1()
postConstruct2()

大家可以去看一下AnnotationConfigApplicationContext的原始碼,其內部會新增很多BeanPostProcessorDefaultListableBeanFactory中。

Bean初始化階段

2個步驟

  1. 呼叫InitializingBean介面的afterPropertiesSet方法

  2. 呼叫定義bean的時候指定的初始化方法。

呼叫InitializingBean介面的afterPropertiesSet方法

來看一下InitializingBean這個介面

public interface InitializingBean {

    void afterPropertiesSet() throws Exception;

}

當我們的bean實現了這個介面的時候,會在這個階段被呼叫

呼叫bean定義的時候指定的初始化方法

先來看一下如何指定bean的初始化方法,3種方式

方式1:xml方式指定初始化方法
<bean init-method="bean中方法名稱"/>
方式2:@Bean的方式指定初始化方法
@Bean(initMethod = "初始化的方法")
方式3:api的方式指定初始化方法
this.beanDefinition.setInitMethodName(methodName);

初始化方法最終會賦值給下面這個欄位

org.springframework.beans.factory.support.AbstractBeanDefinition#initMethodName

案例

來個類
package com.javacode2018.lesson002.demo10;

import org.springframework.beans.factory.InitializingBean;

public class Service implements InitializingBean{
    public void init() {
        System.out.println("呼叫init()方法");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("呼叫afterPropertiesSet()");
    }
}
下面我們定義Service這個bean,指定init方法為初始化方法
package com.javacode2018.lesson002.demo10;

import org.junit.Test;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;

/**
 * 初始化方法測試
 */
public class InitMethodTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        BeanDefinition service = BeanDefinitionBuilder.genericBeanDefinition(Service.class).
                setInitMethodName("init"). //@1:指定初始化方法
                getBeanDefinition();

        factory.registerBeanDefinition("service", service);

        System.out.println(factory.getBean("service"));
    }
}
執行輸出
呼叫afterPropertiesSet()
呼叫init()方法
com.javacode2018.lesson002.demo10.Service@12f41634

呼叫順序:InitializingBean中的afterPropertiesSet、然後在呼叫自定義的初始化方法

Bean初始化後階段

這塊的原始碼:

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

呼叫BeanPostProcessor介面的postProcessAfterInitialization方法,返回null的時候,會中斷上面的操作。

通常稱postProcessAfterInitialization這個方法為:bean初始化後置操作。

來個案例:

package com.javacode2018.lesson002.demo11;

import org.junit.Test;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.lang.Nullable;

/**
 * {@link BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)}
 * bean初始化後置處理
 */
public class PostProcessAfterInitializationTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //加入bean初始化後置處理器方法實現
        factory.addBeanPostProcessor(new BeanPostProcessor() {
            @Nullable
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                System.out.println("postProcessAfterInitialization:" + beanName);
                return bean;
            }
        });

        //下面註冊2個String型別的bean
        factory.registerBeanDefinition("name",
                BeanDefinitionBuilder.
                        genericBeanDefinition(String.class).
                        addConstructorArgValue("公眾號:【路人甲Java】").
                        getBeanDefinition());
        factory.registerBeanDefinition("personInformation",
                BeanDefinitionBuilder.genericBeanDefinition(String.class).
                        addConstructorArgValue("帶領大家成為java高手!").
                        getBeanDefinition());

        System.out.println("-------輸出bean資訊---------");

        for (String beanName : factory.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, factory.getBean(beanName)));
        }
    }
}

執行輸出

-------輸出bean資訊---------
postProcessAfterInitialization:name
name->公眾號:【路人甲Java】
postProcessAfterInitialization:personInformation
personInformation->帶領大家成為java高手!

階段10:所有單例bean初始化完成後階段

所有單例bean例項化完成之後,spring會回撥下面這個介面:

public interface SmartInitializingSingleton {
    void afterSingletonsInstantiated();
}

呼叫邏輯在下面這個方法中

/**
 * 確保所有非lazy的單例都被例項化,同時考慮到FactoryBeans。如果需要,通常在工廠設定結束時呼叫。
 */
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons

這個方法內部會先觸發所有非延遲載入的單例bean初始化,然後從容器中找到型別是SmartInitializingSingleton的bean,呼叫他們的afterSingletonsInstantiated方法。

有興趣的可以去看一下帶有ApplicationContext的容器,內部最終都會呼叫上面這個方法觸發所有單例bean的初始化。

來個2個案例演示一下SmartInitializingSingleton的使用。

案例1:ApplicationContext自動回撥SmartInitializingSingleton介面

Service1:

package com.javacode2018.lesson002.demo12;

import org.springframework.stereotype.Component;

@Component
public class Service1 {

    public Service1() {
        System.out.println("create " + this.getClass());
    }
}

Service2:

package com.javacode2018.lesson002.demo12;

import org.springframework.stereotype.Component;

@Component
public class Service2 {
    public Service2() {
        System.out.println("create " + this.getClass());
    }
}

自定義一個SmartInitializingSingleton

package com.javacode2018.lesson002.demo12;

import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;

@Component
public class MySmartInitializingSingleton implements SmartInitializingSingleton {
    @Override
    public void afterSingletonsInstantiated() {
        System.out.println("所有bean初始化完畢!");
    }
}

來個測試類,通過包掃描的方式註冊上面3個bean

package com.javacode2018.lesson002.demo12;

import org.junit.Test;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;

/**
 * 所有bean初始化完畢,容器會回撥{@link SmartInitializingSingleton#afterSingletonsInstantiated()}
 */
@ComponentScan
public class SmartInitializingSingletonTest {
    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(SmartInitializingSingletonTest.class);
        System.out.println("開始啟動容器!");
        context.refresh();
        System.out.println("容器啟動完畢!");
    }
}

執行輸出

開始啟動容器!
create class com.javacode2018.lesson002.demo12.Service1
create class com.javacode2018.lesson002.demo12.Service2
所有bean初始化完畢!
容器啟動完畢!

案例2:通過api的方式讓DefaultListableBeanFactory去回撥SmartInitializingSingleton

@Test
public void test2() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    factory.registerBeanDefinition("service1", BeanDefinitionBuilder.genericBeanDefinition(Service1.class).getBeanDefinition());
    factory.registerBeanDefinition("service2", BeanDefinitionBuilder.genericBeanDefinition(Service2.class).getBeanDefinition());
    factory.registerBeanDefinition("mySmartInitializingSingleton", BeanDefinitionBuilder.genericBeanDefinition(MySmartInitializingSingleton.class).getBeanDefinition());
    System.out.println("準備觸發所有單例bean初始化");
    //觸發所有bean初始化,並且回撥 SmartInitializingSingleton#afterSingletonsInstantiated 方法
    factory.preInstantiateSingletons();
}

上面通過api的方式註冊bean

最後呼叫factory.preInstantiateSingletons觸發所有非lazy單例bean初始化,所有bean裝配完畢之後,會回撥SmartInitializingSingleton介面。

階段11:Bean使用階段

這個階段就不說了,呼叫getBean方法得到了bean之後,大家可以隨意使用,任意發揮。

階段12:Bean銷燬階段

觸發bean銷燬的幾種方式

  1. 呼叫org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#destroyBean

  2. 呼叫org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons

  3. 呼叫ApplicationContext中的close方法

Bean銷燬階段會依次執行

  1. 輪詢beanPostProcessors列表,如果是DestructionAwareBeanPostProcessor這種型別的,會呼叫其內部的postProcessBeforeDestruction方法

  2. 如果bean實現了org.springframework.beans.factory.DisposableBean介面,會呼叫這個介面中的destroy方法

  3. 呼叫bean自定義的銷燬方法

DestructionAwareBeanPostProcessor介面

看一下原始碼:

public interface DestructionAwareBeanPostProcessor extends BeanPostProcessor {

    /**
     * bean銷燬前呼叫的方法
     */
    void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException;

    /**
     * 用來判斷bean是否需要觸發postProcessBeforeDestruction方法
     */
    default boolean requiresDestruction(Object bean) {
        return true;
    }

}

這個介面有個關鍵的實現類:

org.springframework.context.annotation.CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction方法中會呼叫bean中所有標註了@PreDestroy的方法。

再來說一下自定義銷燬方法有3種方式

方式1:xml中指定銷燬方法

<bean destroy-method="bean中方法名稱"/>

方式2:@Bean中指定銷燬方法

@Bean(destroyMethod = "初始化的方法")

方式3:api的方式指定銷燬方法

this.beanDefinition.setDestroyMethodName(methodName);

初始化方法最終會賦值給下面這個欄位

org.springframework.beans.factory.support.AbstractBeanDefinition#destroyMethodName

下面來看銷燬的案例

案例1:自定義DestructionAwareBeanPostProcessor

來個類

package com.javacode2018.lesson002.demo13;

public class ServiceA {
    public ServiceA() {
        System.out.println("create " + this.getClass());
    }
}

自定義一個DestructionAwareBeanPostProcessor

package com.javacode2018.lesson002.demo13;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;

public class MyDestructionAwareBeanPostProcessor implements DestructionAwareBeanPostProcessor {
    @Override
    public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
        System.out.println("準備銷燬bean:" + beanName);
    }
}

來個測試類

package com.javacode2018.lesson002.demo13;

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

/**
 * 自定義 {@link org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor}
 */
public class DestructionAwareBeanPostProcessorTest {

    @Test
    public void test1() {
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

        //新增自定義的DestructionAwareBeanPostProcessor
        factory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor());

        //向容器中注入3個單例bean
        factory.registerBeanDefinition("serviceA1", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());
        factory.registerBeanDefinition("serviceA2", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());
        factory.registerBeanDefinition("serviceA3", BeanDefinitionBuilder.genericBeanDefinition(ServiceA.class).getBeanDefinition());

        //觸發所有單例bean初始化
        factory.preInstantiateSingletons(); //@1

        System.out.println("銷燬serviceA1"); 
        //銷燬指定的bean
        factory.destroySingleton("serviceA1");//@2

        System.out.println("觸發所有單例bean的銷燬");
        factory.destroySingletons();
    }
}

上面使用了2種方式來觸發bean的銷燬[@1和@2]

執行輸出

create class com.javacode2018.lesson002.demo13.ServiceA
create class com.javacode2018.lesson002.demo13.ServiceA
create class com.javacode2018.lesson002.demo13.ServiceA
銷燬serviceA1
準備要銷燬bean:serviceA1
觸發所有單例bean的銷燬
準備要銷燬bean:serviceA3
準備要銷燬bean:serviceA2

可以看到postProcessBeforeDestruction被呼叫了3次,依次銷燬3個自定義的bean

案例2:觸發@PreDestroy標註的方法被呼叫

上面說了這個註解是在CommonAnnotationBeanPostProcessor#postProcessBeforeDestruction中被處理的,所以只需要將這個加入BeanPostProcessor列表就可以了。

再來個類

package com.javacode2018.lesson002.demo13;

import javax.annotation.PreDestroy;

public class ServiceB {
    public ServiceB() {
        System.out.println("create " + this.getClass());
    }

    @PreDestroy
    public void preDestroy() { //@1
        System.out.println("preDestroy()");
    }
}

@1:標註了@PreDestroy註解

測試用例

@Test
public void test2() {
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();

    //新增自定義的DestructionAwareBeanPostProcessor
    factory.addBeanPostProcessor(new MyDestructionAwareBeanPostProcessor()); //@1
    //將CommonAnnotationBeanPostProcessor加入
    factory.addBeanPostProcessor(new CommonAnnotationBeanPostProcessor()); //@2

    //向容器中注入bean
    factory.registerBeanDefinition("serviceB", BeanDefinitionBuilder.genericBeanDefinition(ServiceB.class).getBeanDefinition());

    //觸發所有單例bean初始化
    factory.preInstantiateSingletons();

    System.out.println("銷燬serviceB");
    //銷燬指定的bean
    factory.destroySingleton("serviceB");
}

@1:放入了一個自定義的DestructionAwareBeanPostProcessor

@2:放入了CommonAnnotationBeanPostProcessor,這個會處理bean中標註@PreDestroy註解的方法

看效果執行輸出

create class com.javacode2018.lesson002.demo13.ServiceB
銷燬serviceB
準備銷燬bean:serviceB
preDestroy()

案例3:看一下銷燬階段的執行順序

實際上ApplicationContext內部已經將spring內部一些常見的必須的BeannPostProcessor自動裝配到beanPostProcessors列表中,比如我們熟悉的下面的幾個:

1.org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
  用來處理@Resource、@PostConstruct、@PreDestroy的
2.org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
  用來處理@Autowired、@Value註解
3.org.springframework.context.support.ApplicationContextAwareProcessor
  用來回撥Bean實現的各種Aware介面

所以通過ApplicationContext來銷燬bean,會觸發3中方式的執行。

下面我們就以AnnotationConfigApplicationContext來演示一下銷燬操作。

來一個類

package com.javacode2018.lesson002.demo14;

import org.springframework.beans.factory.DisposableBean;

import javax.annotation.PreDestroy;

public class ServiceA implements DisposableBean {

    public ServiceA() {
        System.out.println("建立ServiceA例項");
    }

    @PreDestroy
    public void preDestroy1() {
        System.out.println("preDestroy1()");
    }

    @PreDestroy
    public void preDestroy2() {
        System.out.println("preDestroy2()");
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("DisposableBean介面中的destroy()");
    }

    //自定義的銷燬方法
    public void customDestroyMethod() { //@1
        System.out.println("我是自定義的銷燬方法:customDestroyMethod()");
    }
}

上面的類中有2個方法標註了@PreDestroy

這個類實現了DisposableBean介面,重寫了介面的中的destroy方法

@1:這個destroyMethod我們一會通過@Bean註解的方式,將其指定為自定義方法。

來看測試用例

package com.javacode2018.lesson002.demo14;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;

@Configurable
public class DestroyTest {

    @Bean(destroyMethod = "customDestroyMethod") //@1
    public ServiceA serviceA() {
        return new ServiceA();
    }

    @Test
    public void test1() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(DestroyTest.class);
        //啟動容器
        System.out.println("準備啟動容器");
        context.refresh();
        System.out.println("容器啟動完畢");
        System.out.println("serviceA:" + context.getBean(ServiceA.class));
        //關閉容器
        System.out.println("準備關閉容器");
        //呼叫容器的close方法,會觸發bean的銷燬操作
        context.close(); //@2
        System.out.println("容器關閉完畢");
    }
}

上面這個類標註了@Configuration,表示是一個配置類,內部有個@Bean標註的方法,表示使用這個方法來定義一個bean。

@1:通過destroyMethod屬性將customDestroyMethod指定為自定義銷燬方法

@2:關閉容器,觸發bean銷燬操作

來執行test1,輸出

準備啟動容器
建立ServiceA例項
容器啟動完畢
serviceA:com.javacode2018.lesson002.demo14.ServiceA@243c4f91
準備關閉容器
preDestroy1()
preDestroy2()
DisposableBean介面中的destroy()
我是自定義的銷燬方法:customDestroyMethod()
容器關閉完畢

可以看出銷燬方法呼叫的順序:

  1. @PreDestroy標註的所有方法

  2. DisposableBean介面中的destroy()

  3. 自定義的銷燬方法

下面來說一個非常非常重要的類,打起精神,一定要注意看。

AbstractApplicationContext類(非常重要的類)

來看一下UML圖:

終於,月薪過5萬了!

BeanFactory介面

這個我們已經很熟悉了,Bean工廠的頂層介面

DefaultListableBeanFactory類

實現了BeanFactory介面,可以說這個可以是BeanFactory介面真正的唯一實現,內部真正實現了bean生命週期中的所有程式碼。

其他的一些類都是依賴於DefaultListableBeanFactory類,將請求轉發給DefaultListableBeanFactory進行bean的處理的。

其他3個類

我們經常用到的就是這3個類:AnnotationConfigApplicationContext/ClassPathXmlApplicationContext/FileSystemXmlApplicationContext這3個類,他們的主要內部的功能是依賴他的父類AbstractApplicationContext來實現的,所以大家主要看AbstractApplicationContext這個類。

AbstractApplicationContext類

這個類中有2個比較重要的方法

public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)

大家是否注意過我們使用AnnotationConfigApplicationContext的時候,經常呼叫reflush方法,這個方法內部就會呼叫上面這2個方法。

第一個方法:getBeanFactory()

返回當前應用上下文中的ConfigurableListableBeanFactory,這也是個介面型別的,這個介面有一個唯一的實現類:DefaultListableBeanFactory

有沒有很熟悉,上面說過:DefaultListableBeanFactory是BeanFactory真正的唯一實現。

應用上線文中就會使用這個ConfigurableListableBeanFactory來操作spring容器。

第二個方法:registerBeanPostProcessors

說的通俗點:這個方法就是向ConfigurableListableBeanFactory中註冊BeanPostProcessor,內容會從spring容器中獲取所有型別的BeanPostProcessor,將其新增到DefaultListableBeanFactory#beanPostProcessors列表中

看一下這個方法的原始碼:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

會將請求轉發給PostProcessorRegistrationDelegate#registerBeanPostProcessors

內部比較長,大家可以去看一下原始碼,這個方法內部主要用到了4個BeanPostProcessor型別的List集合。

List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> orderedPostProcessors
List<BeanPostProcessor> nonOrderedPostProcessors;
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();

先說一下:當到方法的時候,spring容器中已經完成了所有Bean的註冊。

spring會從容器中找出所有型別的BeanPostProcessor列表,然後按照下面的規則將其分別放到上面的4個集合中,上面4個集合中的BeanPostProcessor會被依次新增到DefaultListableBeanFactory#beanPostProcessors列表中,來看一下4個集合的分別放的是那些BeanPostProcessor:

priorityOrderedPostProcessors(指定優先順序的BeanPostProcessor)

實現org.springframework.core.PriorityOrdered介面的BeanPostProcessor,但是不包含MergedBeanDefinitionPostProcessor型別的

orderedPostProcessors(指定了順序的BeanPostProcessor)

標註有@Order註解,或者實現了org.springframework.core.annotation.Order介面的BeanPostProcessor,但是不包含MergedBeanDefinitionPostProcessor型別的

nonOrderedPostProcessors(未指定順序的BeanPostProcessor)

上面2中型別置為以及MergedBeanDefinitionPostProcessor之外的

internalPostProcessors

MergedBeanDefinitionPostProcessor型別的BeanPostProcessor列表。

大家可以去看一下CommonAnnotationBeanPostProcessorAutowiredAnnotationBeanPostProcessor,這兩個類都實現了PriorityOrdered介面,但是他們也實現了MergedBeanDefinitionPostProcessor介面,所以最終他們會被丟到internalPostProcessors這個集合中,會被放入BeanPostProcessor的最後面。

Bean生命週期流程圖

終於,月薪過5萬了!

Spring學習建議

這裡我列一下自己學習spring的一些方法。

看官方文件

spring中的所有知識點,官網上都有,質量方面是非常高的,可能對英文有些要求,不過可以使用翻譯軟體翻一下。

官網地址:

https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/

看好的視訊

視訊算是學習技術最快的一種方式,視訊中可以呈現更多細節的東西,同樣的內容,可能視訊只需要1小時,而用文件描述出來可能需要寫一整天,視訊的資訊量更大,讓觀看者更容易理解一些。

網上關於spring的視訊也比較多,質量不一,很難去選擇,不過有一套我強烈建議大家去看,這套視訊是有門檻的,前提是你對spring要有一定的瞭解。

就是極客時間上面的《小馬哥講Spring核心程式設計思想》,這個系列如果你都能看懂,spring方面不說多的,在大部分公司基本上是無敵狀態,目前這套課程已經出了一半了,所有的內容我都看過,質量非常棒,可以掃描下面二維碼購買。

終於,月薪過5萬了!

看原始碼

想窺視spring中的原貌,成為頂尖高手,原始碼是必須要看的,spring整體的設計是非常優秀的,裡面用到了很多設計模式,很多java中的api,看原始碼的過程中,這些好東西慢慢就變成你自己的了。

寫部落格

技術有沒有掌握,你寫篇文章就知道了,如果你能通過文章的方式將技術描述出來,讓別人能夠看懂,那麼說明你確實是掌握了這個技術。寫部落格的過程中會讓你想各種辦法去深入瞭解需要介紹的要點,這樣收穫是非常大的。

總結

本文內容比較多,強烈建議大家先收藏,要反覆看,反覆去聯絡,一定要掌握。

關於spring方面有任何問題的,歡迎大家留言。

順便說下:幫忙點個再看,轉發到朋友圈,讓更多朋友一起學習。

案例原始碼

https://gitee.com/javacode2018/spring-series

路人甲java所有案例程式碼以後都會放到這個上面,大家watch一下,可以持續關注動態。

Spring系列

  1. Spring系列第1篇:為何要學spring?

  2. Spring系列第2篇:控制反轉(IoC)與依賴注入(DI)

  3. Spring系列第3篇:Spring容器基本使用及原理

  4. Spring系列第4篇:xml中bean定義詳解(-)

  5. Spring系列第5篇:建立bean例項這些方式你們都知道?

  6. Spring系列第6篇:玩轉bean scope,避免跳坑裡!

  7. Spring系列第7篇:依賴注入之手動注入

  8. Spring系列第8篇:自動注入(autowire)詳解,高手在於堅持

  9. Spring系列第9篇:depend-on到底是幹什麼的?

  10. Spring系列第10篇:primary可以解決什麼問題?

  11. Spring系列第11篇:bean中的autowire-candidate又是幹什麼的?

  12. Spring系列第12篇:lazy-init:bean延遲初始化

  13. Spring系列第13篇:使用繼承簡化bean配置(abstract & parent)

  14. Spring系列第14篇:lookup-method和replaced-method比較陌生,怎麼玩的?

  15. Spring系列第15篇:代理詳解(Java動態代理&cglib代理)?

  16. Spring系列第16篇:深入理解java註解及spring對註解的增強(預備知識)

  17. Spring系列第17篇:@Configration和@Bean註解詳解(bean批量註冊)

  18. Spring系列第18篇:@ComponentScan、@ComponentScans詳解(bean批量註冊)

  19. Spring系列第18篇:@import詳解(bean批量註冊)

  20. Spring系列第20篇:@Conditional通過條件來控制bean的註冊

  21. Spring系列第21篇:註解實現依賴注入(@Autowired、@Resource、@Primary、@Qulifier)

  22. Spring系列第22篇:@Scope、@DependsOn、@ImportResource、@Lazy 詳解

更多好文章

  1. Java高併發系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. Mybatis系列(共12篇)

  5. 聊聊db和快取一致性常見的實現方式

  6. 介面冪等性這麼重要,它是什麼?怎麼實現?

  7. 泛型,有點難度,會讓很多人懵逼,那是因為你沒有看這篇文章!

感謝大家的閱讀,也歡迎您把這篇文章分享給更多的朋友一起閱讀!謝謝!

路人甲java

終於,月薪過5萬了!

▲長按圖片識別二維碼關注

路人甲Java:工作10年的前阿里P7分享Java、演算法、資料庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!

相關文章