終於,月薪過5萬了!
來看幾個問題
想不想月薪超過5萬?
想不想進入公司架構組?
想不想成為專案組的負責人?
想不想成為spring的高手,超越99%的對手?
那麼本文內容是你必須要掌握的。
本文主要詳解bean的生命週期,分為13個環節,每個環節中spring都提供了一些擴充套件點,我們都將進行詳細說明,讓大家全面掌握這塊的知識。
Spring bean生命週期13個環節
階段1:Bean元資訊配置階段
階段2:Bean元資訊解析階段
階段3:將Bean註冊到容器中
階段4:BeanDefinition合併階段
階段5:Bean Class載入階段
階段6:Bean例項化階段(2個小階段)
Bean例項化前階段
Bean例項化階段
階段7:合併後的BeanDefinition處理
階段8:屬性賦值階段(3個小階段)
Bean例項化後階段
Bean屬性賦值前階段
Bean屬性賦值階段
階段9:Bean初始化階段(5個小階段)
Bean Aware介面回撥階段
Bean初始化前階段
Bean初始化階段
Bean初始化後階段
階段10:所有單例bean初始化完成後階段
階段11:Bean的使用階段
階段12:Bean銷燬前階段
階段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是個介面,有幾個實現類,看一下類圖:
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種:
類上標註@Compontent註解來定義一個bean
配置類中使用@Bean註解來定義bean
小結
bean註冊者只識別BeanDefinition物件,不管什麼方式最後都會將這些bean定義的資訊轉換為BeanDefinition物件,然後註冊到spring容器中。
階段2:Bean元資訊解析階段
Bean元資訊的解析就是將各種方式定義的bean配置資訊解析為BeanDefinition物件。
Bean元資訊的解析主要有3種方式
xml檔案定義bean的解析
properties檔案定義bean的解析
註解方式定義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個小的階段
Bean例項化前操作
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
中的postProcessProperties
和postProcessPropertyValues
都返回空的時候,表示這個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的原始碼,其內部會新增很多BeanPostProcessor
到DefaultListableBeanFactory
中。
Bean初始化階段
2個步驟
呼叫InitializingBean介面的afterPropertiesSet方法
呼叫定義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銷燬的幾種方式
呼叫org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#destroyBean
呼叫org.springframework.beans.factory.config.ConfigurableBeanFactory#destroySingletons
呼叫ApplicationContext中的close方法
Bean銷燬階段會依次執行
輪詢beanPostProcessors列表,如果是DestructionAwareBeanPostProcessor這種型別的,會呼叫其內部的postProcessBeforeDestruction方法
如果bean實現了org.springframework.beans.factory.DisposableBean介面,會呼叫這個介面中的destroy方法
呼叫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()
容器關閉完畢
可以看出銷燬方法呼叫的順序:
@PreDestroy標註的所有方法
DisposableBean介面中的destroy()
自定義的銷燬方法
下面來說一個非常非常重要的類,打起精神,一定要注意看。
AbstractApplicationContext類(非常重要的類)
來看一下UML圖:
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列表。
大家可以去看一下
CommonAnnotationBeanPostProcessor
和AutowiredAnnotationBeanPostProcessor
,這兩個類都實現了PriorityOrdered
介面,但是他們也實現了MergedBeanDefinitionPostProcessor
介面,所以最終他們會被丟到internalPostProcessors
這個集合中,會被放入BeanPostProcessor的最後面。
Bean生命週期流程圖
Spring學習建議
這裡我列一下自己學習spring的一些方法。
看官方文件
spring中的所有知識點,官網上都有,質量方面是非常高的,可能對英文有些要求,不過可以使用翻譯軟體翻一下。
官網地址:
https://docs.spring.io/spring/docs/5.2.3.RELEASE/spring-framework-reference/
看好的視訊
視訊算是學習技術最快的一種方式,視訊中可以呈現更多細節的東西,同樣的內容,可能視訊只需要1小時,而用文件描述出來可能需要寫一整天,視訊的資訊量更大,讓觀看者更容易理解一些。
網上關於spring的視訊也比較多,質量不一,很難去選擇,不過有一套我強烈建議大家去看,這套視訊是有門檻的,前提是你對spring要有一定的瞭解。
就是極客時間上面的《小馬哥講Spring核心程式設計思想》,這個系列如果你都能看懂,spring方面不說多的,在大部分公司基本上是無敵狀態,目前這套課程已經出了一半了,所有的內容我都看過,質量非常棒,可以掃描下面二維碼購買。
看原始碼
想窺視spring中的原貌,成為頂尖高手,原始碼是必須要看的,spring整體的設計是非常優秀的,裡面用到了很多設計模式,很多java中的api,看原始碼的過程中,這些好東西慢慢就變成你自己的了。
寫部落格
技術有沒有掌握,你寫篇文章就知道了,如果你能通過文章的方式將技術描述出來,讓別人能夠看懂,那麼說明你確實是掌握了這個技術。寫部落格的過程中會讓你想各種辦法去深入瞭解需要介紹的要點,這樣收穫是非常大的。
總結
本文內容比較多,強烈建議大家先收藏,要反覆看,反覆去聯絡,一定要掌握。
關於spring方面有任何問題的,歡迎大家留言。
順便說下:幫忙點個再看,轉發到朋友圈,讓更多朋友一起學習。
案例原始碼
https://gitee.com/javacode2018/spring-series
路人甲java所有案例程式碼以後都會放到這個上面,大家watch一下,可以持續關注動態。
Spring系列
更多好文章
感謝大家的閱讀,也歡迎您把這篇文章分享給更多的朋友一起閱讀!謝謝!
路人甲java
▲長按圖片識別二維碼關注
路人甲Java:工作10年的前阿里P7分享Java、演算法、資料庫方面的技術乾貨!堅信用技術改變命運,讓家人過上更體面的生活!
相關文章
- 月薪2W起的程式設計師做事細節曝光,終於知道了他們高薪的真相了程式設計師高薪
- CF終於打上1900了
- AI 終於受涼了??AI
- 有了XMLHttpRequest,前後端們終於過上了幸福的生活XMLHTTP後端
- 微軟終於放棄了Electron了微軟
- 那些IT培訓過的人怎麼樣了?月薪都上萬嗎?
- VS Code Day,終於來了!
- 幽默:終於黑掉ChatGPT了ChatGPT
- GPT-4,終於來了!GPT
- 攜程,終於反彈了?
- 裝修終於結束了
- GitHub 官方終於出 App 了!GithubAPP
- 行,Python終於玩大了!Python
- 它來了它終於來了- Beego 1.12.2Go
- 太難了,我終於把JDBC的程式碼終於優化了!JDBC優化
- Spring容器IOC初始化過程---今天終於進行總結了Spring
- Clickhouse 的 mysql CDC,終於好使了MySql
- 原生 CSS Custom Highlight 終於來了~CSS
- 終於等到了這篇文章了
- 終於下定決心離職了
- 終於拿Rumbaugh 的新書了新書
- 終於找回這個部落格了
- Win10螢幕亮度切換終於正常了 這個煩人Bug終於修復了Win10
- 幹了 2 年多 Java 外包,終於脫離了!Java
- 終於,我也來學習VUE了Vue
- Node.js終於有了Fetch APINode.jsAPI
- React全新文件終於來了React
- Freadhub終於升級Flutter2.0了Flutter
- 我終於搞清了啥是 HTTPS 了HTTP
- 終於有人把MYSQL索引講清楚了MySql索引
- VSCode 的 Live Share 功能終於來了VSCode
- 超前看!匯聚全球頂尖白帽黑客的TCFT 2020終終終終於來了!黑客
- 學了元件作用域,我終於對JMeter開竅了元件JMeter
- 學了元件作用域,我終於對 JMeter 開竅了元件JMeter
- 終於弄明白了 RocketMQ 的儲存模型MQ模型
- 終於來了!FastGPT 正式相容 GPT 應用ASTGPT
- 大劉終於當上架構師了架構
- 幽默:終於能讓BUG反覆出現了