Spring 源碼繼續開整!java
在 XML 文件解析流程一文中,鬆哥和你們分享了 Spring 中配置文件的加載方式,若是小夥伴們還沒看過,必定先看一下,這有助於更好的理解本文,傳送門:Spring 源碼第一篇開整!配置文件是怎麼加載的?。bootstrap
還記得該篇文章中的代碼嗎?緩存
XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));
User user = factory.getBean(User.class);
System.out.println("user = " + user);
複製代碼
當 ClassPathResource 將文件以 IO 流的方式輸出後,接下來就是構造 XmlBeanFactory ,XmlBeanFactory 功能有限,它的大部分功能都在它的父類 DefaultListableBeanFactory 中實現了,而 DefaultListableBeanFactory 也至關因而容器的始祖,爲何這麼說呢?咱們今天就來講一說這個話題。併發
本文是 Spring 源碼解讀第七篇,閱讀本系列前面文章有助於更好的理解本文:app
要說 XmlBeanFactory 就不得不先說它的父類 DefaultListableBeanFactory,由於 XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了,XmlBeanFactory 只是對 IO 流的讀取作了一些定製而已。ide
DefaultListableBeanFactory 是一個完整的、功能成熟的 IoC 容器,若是你的需求很簡單,甚至能夠直接使用 DefaultListableBeanFactory,若是你的需求比較複雜,那麼經過擴展 DefaultListableBeanFactory 的功能也能夠達到,能夠說 DefaultListableBeanFactory 是整個 Spring IoC 容器的始祖。源碼分析
咱們先來看一下 DefaultListableBeanFactory 的繼承關係:post
從這張類的關係圖中能夠看出,DefaultListableBeanFactory 實際上也是一個集大成者。在 Spring 中,針對 Bean 的不一樣操做都有不一樣的接口進行規範,每一個接口都有本身對應的實現,最終在 DefaultListableBeanFactory 中將全部的實現匯聚到一塊兒。從這張類的繼承關係圖中咱們大概就能感覺到 Spring 中關於類的設計是多麼厲害,代碼耦合度很是低。ui
這些類,在本系列後面的介紹中,大部分都會涉及到,如今我先大概介紹一下每一個類的做用,你們先混個臉熟:this
上面的內容可能看的你們眼花繚亂,鬆哥這裏經過幾個簡單實際的例子,來帶你們使用一下 DefaultListableBeanFactory 的功能,可能你們的理解就比較清晰了。
DefaultListableBeanFactory 做爲一個集大成者,提供了很是多的功能,咱們一個一個來看。
首先文章中一開始的三行代碼咱們能夠對其略加改造,由於咱們已經說了 XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了,XmlBeanFactory 只是對 IO 流的讀取作了一些定製而已,文件的讀取主要是經過 XmlBeanDefinitionReader 來完成的(本系列前面文章已經講過),咱們能夠對文章一開始的三行代碼進行改造,以便更好的體現「XmlBeanFactory 中的大部分功能實際上在 DefaultListableBeanFactory 中就已經提供好了」:
ClassPathResource res=new ClassPathResource("beans.xml");
DefaultListableBeanFactory factory=new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader=new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(res);
User user = factory.getBean(User.class);
System.out.println("user = " + user);
複製代碼
使用前四行代碼代替 XmlBeanFactory,這樣 XmlBeanFactory 的功能是否是就很明確了?就是前四行代碼的功能。
動態註冊 Bean,這是 DefaultListableBeanFactory 的功能之一,不過準確來講應該是動態註冊 BeanDefinition 。
咱們先來看一個簡單的例子:
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
複製代碼
首先咱們本身手動構建一個 DefaultListableBeanFactory 對象。固然也可使用前面的 XmlBeanFactory。
而後再手動構建一個 GenericBeanDefinition。在前面的文章中,鬆哥和你們講過,如今默認使用的 BeanDefinition 就是 GenericBeanDefinition,因此這裏咱們本身也手動構建一個 GenericBeanDefinition。有了 GenericBeanDefinition 以後,咱們設置相關的類和屬性。
接下來再將 userBeanDefinition 註冊到 defaultListableBeanFactory。註冊完成以後,咱們就能夠從 defaultListableBeanFactory 中獲取相應的 Bean 了。
這裏說一句題外話,但願你們在閱讀本系列每一篇文章的時候,可以將本系列先後文章聯繫起來一塊兒理解,這樣會有不少意料以外的收穫。例如上面的,咱們既能夠聲明一個 DefaultListableBeanFactory,也能夠聲明一個 XmlBeanFactory,那你大概就能據此推斷出 XmlBeanFactory 的主要目的可能就是對資源文件進行讀取和註冊。
那麼究竟是怎麼註冊的呢?咱們來看一下 defaultListableBeanFactory.registerBeanDefinition 方法的定義:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
複製代碼
registerBeanDefinition 方法是在 BeanDefinitionRegistry 接口中聲明的,DefaultListableBeanFactory 類實現了 BeanDefinitionRegistry 接口,並實現了該方法,咱們來看分析下該方法:
這即是 registerBeanDefinition 方法的工做流程。
有小夥伴會說,這個方法從頭至尾都是 BeanDefinition,跟 Bean 有什麼關係呢?
咋一看確實好像和 Bean 沒有直接關係。
其實這涉及到另一個問題,就是 Bean 的懶加載。這個時候先把 BeanDefinition 定義好,等到真正調用 Bean 的時候,纔會去初始化 Bean。咱們能夠在 User 類的構造方法中打印日誌看下,以下:
public class User {
private String username;
private String address;
public User() {
System.out.println("--------user init--------");
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", address='" + address + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
複製代碼
從下圖能夠看到,當 BeanDefinition 註冊完成後,User 並無初始化,等到 getBean 方法被調用的時候,User 才初始化了。
須要注意的是,咱們平常開發中使用的 ApplicationContext 並不是懶加載,這個在鬆哥的 Spring 入門視頻中能夠看到效果【www.bilibili.com/video/BV1Wv…】,具體原理鬆哥將在本系列後面的文章中和你們分享。
那麼若是不想懶加載該怎麼辦呢?固然有辦法。
在 DefaultListableBeanFactory 中還有一個 preInstantiateSingletons 方法能夠提早註冊 Bean,該方法是在 ConfigurableListableBeanFactory 接口中聲明的,DefaultListableBeanFactory 類實現了 ConfigurableListableBeanFactory 接口並實現了接口中的方法:
@Override
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}
複製代碼
preInstantiateSingletons 方法的總體邏輯比較簡單,就是遍歷 beanNames,對符合條件的 Bean 進行實例化,並且你們注意,這裏所謂的提早初始化其實就是在咱們調用 getBean 方法以前,它本身先調用了一下 getBean。
咱們能夠在案例中手動調用該方法:
DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
GenericBeanDefinition userBeanDefinition = new GenericBeanDefinition();
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("username", "javaboy");
pvs.add("address", "www.javaboy.org");
userBeanDefinition.setPropertyValues(pvs);
userBeanDefinition.setBeanClass(User.class);
defaultListableBeanFactory.registerBeanDefinition("user", userBeanDefinition);
defaultListableBeanFactory.preInstantiateSingletons();
User user = defaultListableBeanFactory.getBean(User.class);
System.out.println("user = " + user);
複製代碼
此時在調用 getBean 方法以前,User 就已經初始化了,以下圖:
DefaultListableBeanFactory 中另一個重量級方法就是 getBean 了。不過 getBean 方法的真正實現是在 DefaultListableBeanFactory 的父類 AbstractBeanFactory 中,具體的實現方法是 doGetBean,原本想和你們子在這裏聊一聊這個問題,可是發現這是一個很是龐大的問題,BeanFactory 和 FactoryBean 都還沒和你們分享,因此這個話題咱們仍是暫且押後,一個點一個點來。
好啦,今天就先說這麼多,每篇源碼我都儘可能配置套一些小案例來演示,這樣避免你們看的太枯燥了,咱們下週繼續~
若是你們以爲有收穫,記得點個在看鼓勵下鬆哥哦~