我們知道Spring框架創建實例是通過ioc容器自動創建,那麼它到底是怎麼通過配置文件創建出實例的,下面從一個最基本的例子來debug分析實例創建的過程
準備
首先,創建一個User類並將user注入到Spring容器中去,並且在無參構造器中打印創建實例對象提示,然後創建一個測試類在main方法中創建容器
User類
package com.stanley.springSource;
public class User {
private String name;
private int age;
public User() {
//提示什麼時候創建實例對象
System.out.println("創建了User...");
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
配置文件
<?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.xsd">
<bean id="user" class="com.stanley.springSource.User">
<property name="name" value="stanley"></property>
<property name="age" value="18"></property>
</bean>
</beans>
測試類
package com.stanley.springSource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
User bean = context.getBean(User.class);
}
}
調試
我們在創建context這一步打上斷點,發現控制檯沒並有輸出創建user,當我們放行後控制檯打印了創建User,所以我們回到斷點,step into一步步往裏看
放行前
放行後
進入ClassPathXmlApplicationContext類以後,我們發現最終會調用有一個三個參數的構造器
找到這個個構造器之後,再進行放行,由於構造器傳入的refresh是true,所以將執行refresh方法,我們發現在執行refresh之後,實例創建,因此我們在refresh上打上斷點再step into進行進一步查看
進入refresh方法後,通過一個prepareBeanFactory來刷新容器,我們發現通過beanFactory接口的實現類的obtainFreshBeanFactory方法解析xml配置文件來獲取容器中的信息,這其中包括即將創建的實例對象的信息
對代碼進行一步步放行,接下來都是一些初始化和註冊的方法,走到一個finishBeanFactoryInitialization方法,對象被創建,於是我們進入這個方法再進一步查看
進入finishBeanFactoryInitialization方法後,開始是一些類型轉換和自動裝配的方法,我們直接放行,放行到最後一個方法實例創建,老樣子我將preInstantiateSingletons方法打上斷點,進入方法內部
進入preInstantiateSingletons後,發現有一個beanName的集合,集合裏存放容器中需要創建bean的id,如果有多個bean,集合的順序將和xml配置文件中的一直
通過迭代beanNames集合遍歷所有註冊的bean,首先判斷實例創建是否爲抽象類,懶加載和單實例
如果通過 在判斷一下是否爲工廠bean,最終通過getBean方法創建實例
getBean這個方法和我們初始化容器後 通過context.getBean的方法一致,都是實現BeanFactory接口,打上斷點,進入方法後看到一個通過doGetBean方法返回的object
這個方法首先先檢查是否創建過這個實例,通過getSingleton方法,如果有則直接返回bean,我們通過context獲取bean走的就是這一步,初始化容器的則走下一步
通過一系列的判斷,比如是否有父類bean工廠,是否有依賴(依賴是指是否有依賴的類,如果有依賴則將遞歸調用方法)
再次獲取bean信息
最後判斷bean是否爲單實例,如果是則再次調用getSingleton方法,這次傳入兩個參數,第二參數是ObjectFactory接口的實現匿名類,並且重寫了getObject方法,通過createBean來創建bean,然後進入getSingleton方法
進入getSingleton,判斷beanname的實例爲null,則通過singletonFactory創建實例
創建完以後將bean加入到一個map
進入addSingleton,通過put將實例放入map,後面通過context.getBean獲取實例會優先從map找有沒有對應的beanname
總結
通過源碼我們可以看到ioc容器從本質上是一個map,將beanname和實例通過鍵值對放入容器,getBean則是通過beanname拿到實例對象。