Spring 中的 IOC
Spring 的 IOC 其實很複雜,因爲它支持的情況,種類,以及開放的接口,拓展性(如各種PostProcessor)太豐富了。這導致我們在看 Spring 源碼的過程中非常喫力,經常點進去一個函數發現很深很深。這篇我主要針對 Spring 的 IOC 中的核心部分,例如 Spring 的 IOC 是如何實現的,Spring 是如何解決循環依賴的這類問題做一個介紹以及一份實現,因爲原理是相通的,對於 Spring 對各種情況的邏輯上的處理不做細緻的討論,對原型模式,或是 FactoryBean 類型的 Bean 的不同處理方式不做具體實現。
本文將實現一個怎樣的 IOC
-
僅支持 Singleton 單例模式的 Bean 管理。(這也是我們在平時項目中最常用的模式)
-
僅支持 無參構造器的 Bean 的管理。(這部分如果實現支持有參構造器的也很簡單,後續可能會補充)
-
僅支持 按照 BeanName 的方式加載 Bean 的方式,如果遇到 Class 的情況,將獲取Class 的 SimpleName 後繼續按照 BeanName 的方式加載。(這裏類似於在 Spring 當中使用 @AutoWaired 按類型匹配不到的情況依然會按照 Name 的方式去匹配)
-
支持 自動裝配,並完美解決循環依賴問題。
流程設計
基礎流程設計
如果不考慮循環依賴的問題,不考慮三級緩存的情況下,實現我們這樣一個IOC的功能很簡單:
-
加載所有的需要被我們管理的 Bean(被 @Component 修飾的類),轉換成 Bean 的定義(BeanDefinition,後面會說),存放在 Map 中。
-
利用反射得到這些 Bean 的實例,將這些 Bean 的實例存儲在我們的容器內。
-
填充我們需要自動裝配的屬性(被 @Resource 修飾的屬性)。
完成以上三步,就可以在外層容器調用 getBean() 方法獲取 Bean 的實例了。
如何解決循環依賴
網上關於 Spring 如何解決循環依賴的文章很多,簡單來說就是利用緩存,先將沒有填充屬性的對象緩存起來,需要的時候先去用這個對象,不必等待一個對象完整的初始化好。而爲什麼是三級緩存不是二級緩存呢,這裏籠統的來說還是方便 Spring 或者開發者們去拓展一些東西,例如在 Spring Bean 的生命週期中有很多的 Processor,這個我們後續再講。關於這部分的細節上的邏輯,在後面介紹完三級緩存會有一個很詳細的流程圖。
三級緩存
三級緩存的實現在代碼中的 SingletonBeanRegistry 中:
其中有以下幾個核心屬性:
-
singletonObjects:一級緩存,用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用。
-
earlySingletonObjects:二級緩存,用於存放提前曝光的單例對象的cache,原始的 bean 對象(尚未填充屬性)。
-
singletonFactories:三級緩存,用於存放 bean 工廠對象(ObjectFactory)。三級緩存中用到了 ObjectFactory,這裏的 ObjectFactory 可以理解爲三級緩存中 Bean 的代理對象,其 getObject() 方法描述瞭如何獲取這個三級緩存的對象。設計成這樣除了方便實現三級緩存解決循環依賴,另外也是方便 Spring 在 ObjectFactory 中做一些拓展。
-
singletonsCurrentlyInCreation:用於存放正在被創建的 Bean 對象。
流程圖(重要)
代碼設計
BeanFactory(參考自 Spring 的 BeanFactory)
和 Spring 一樣,這是 IOC 相關的頂級接口,裏面包含了獲取 Bean,判斷 Bean 是否存在的定義。
public interface BeanFactory {
Object getBean(String name);
<T> T getBean(Class<T> requiredType);
boolean containsBean(String name);
}
SingletonBeanRegistry(參考自 Spring 的 DefaultSingletonBeanRegistry)
單例 Bean 的註冊中心,裏面包含了所有 Bean 的實例以及所有 Bean 實例的緩存,以及獲取單例 Bean 的邏輯,這個類的方法結合 DefaultBeanFactory 中的 getBean() 調用鏈就是上面流程圖的全部內容。
public class SingletonBeanRegistry {
private static final Object NULL_OBJECT = new Object();
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();
private final Map<String, Object> earlySingletonObjects = new HashMap<>();
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>();
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());
protected Object getSingleton(String beanName) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
protected Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
this.singletonsCurrentlyInCreation.add(beanName);
singletonObject = singletonFactory.getObject();
this.singletonsCurrentlyInCreation.remove(beanName);
addSingleton(beanName, singletonObject);
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
}
}
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
}
}
}
protected void removeSingleton(String beanName) {
synchronized (this.singletonObjects) {
this.singletonObjects.remove(beanName);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
}
}
protected boolean isSingletonCurrentlyInCreation(String beanName) {
return this.singletonsCurrentlyInCreation.contains(beanName);
}
protected boolean containsSingleton(String name) {
return this.singletonObjects.containsKey(name);
}
}
DefaultBeanFactory(參考自 Spring 的 DefaultListableBeanFactory)
BeanFactory 的一個實現,繼承了 SingletonBeanRegistry ,同時也作爲一個成員變量存在於 ApplicationContext 當中。getBean() 是入口,其調用鏈爲:getBean()->doGetBean()獲取Bean如果不存在則創建->doCreateBean()->createBeanInstance()創建 Bean 的實例->populateBean()Bean屬性的自動裝配。(在 Spring 中多了一步 createBean() 用於實現 AOP,和一步 initalizeBean() 用於執行後置處理器和 init-method,這裏我們都暫不實現)
public class DefaultBeanFactory extends SingletonBeanRegistry implements BeanFactory {
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
this.beanDefinitionMap.put(beanName, beanDefinition);
}
public void preInstantiateSingletons() {
this.beanDefinitionMap.forEach((beanName, beanDef) -> {
getBean(beanName);
});
}
@Override
public Object getBean(String name) {
return doGetBean(name);
}
private <T> T doGetBean(final String beanName) {
Object bean;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null) {
bean = sharedInstance;
} else {
BeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
if (beanDefinition == null) {
throw new DumpException("can not find the definition of bean '" + beanName + "'");
}
bean = getSingleton(beanName, () -> {
try {
return doCreateBean(beanName, beanDefinition);
} catch (Exception ex) {
removeSingleton(beanName);
throw ex;
}
});
}
return (T) bean;
}
private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
Object bean = createBeanInstance(beanName, beanDefinition);
boolean earlySingletonExposure = isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
addSingletonFactory(beanName, () -> bean);
}
Object exposedObject = bean;
populateBean(beanName, beanDefinition, bean);
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName);
if (earlySingletonReference != null) {
exposedObject = earlySingletonReference;
}
}
return exposedObject;
}
private Object createBeanInstance(String beanName, BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Constructor<?> constructorToUse;
if (beanClass.isInterface()) {
throw new DumpException("Specified class '" + beanName + "' is an interface");
}
try {
constructorToUse = beanClass.getDeclaredConstructor((Class<?>[]) null);
return BeanUtils.instantiateClass(constructorToUse);
} catch (Exception e) {
throw new DumpException("'" + beanName + "',No default constructor found", e);
}
}
private void populateBean(String beanName, BeanDefinition beanDefinition, Object beanInstance) {
Field[] beanFields = beanDefinition.getBeanClass().getDeclaredFields();
try {
for (Field field : beanFields) {
if (field.getAnnotation(Resource.class) == null) {
continue;
}
if (!containsBean(field.getName())) {
throw new DumpException("'@Resource' for field '" + field.getClass().getName() + "' can not find");
}
field.setAccessible(true);
field.set(beanInstance, getBean(field.getName()));
}
} catch (Exception e) {
throw new DumpException("populateBean '" + beanName + "' error", e);
}
}
private boolean containsBeanDefinition(String name) {
return beanDefinitionMap.containsKey(name);
}
@Override
@SuppressWarnings("unchecked")
public <T> T getBean(Class<T> requiredType) {
return (T) getBean(StringUtils.lowerFirst(requiredType.getSimpleName()));
}
@Override
public boolean containsBean(String name) {
return this.containsSingleton(name) || containsBeanDefinition(name);
}
}
ApplicationContext
應用的最外層容器,利用內部的 DefaultBeanFactory 對象實現了 BeanFactory。在new ApplicationContext()時,會執行讀取所有的 Bean 轉化成 BeanDefinition,並對所有的 BeanDefinition 執行 getBean() 獲取所有 Bean 的實例,存放在 SingletonBeanRegistry 當中。在 ApplicationContext 中調用 getBean() 其實就是調用 DefaultBeanFactory 中的 getBean()。
public class ApplicationContext implements BeanFactory {
private DefaultBeanFactory beanFactory = new DefaultBeanFactory();
public ApplicationContext() {
loadBeanDefinitions(beanFactory);
finishBeanFactoryInitialization(beanFactory);
}
private void loadBeanDefinitions(DefaultBeanFactory beanFactory) {
ComponentBeanReader beanReader = new ComponentBeanReader();
beanReader.readBeanDefinition(beanFactory);
}
public void finishBeanFactoryInitialization(DefaultBeanFactory beanFactory) {
beanFactory.preInstantiateSingletons();
}
@Override
public Object getBean(String name) {
return getBeanFactory().getBean(name);
}
@Override
public <T> T getBean(Class<T> requiredType) {
return getBeanFactory().getBean(requiredType);
}
@Override
public boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
public DefaultBeanFactory getBeanFactory() {
return beanFactory;
}
}
BeanDefinition(參考自 Spring 的 BeanDefinition)
Bean 的描述,理論上應包含很多 Bean 的信息,但目前的實現只存了一個該 Bean 的 Class。
public class BeanDefinition {
private volatile Class<?> beanClass;
public Class<?> getBeanClass() {
return beanClass;
}
public void setBeanClass(Class<?> beanClass) {
this.beanClass = beanClass;
}
}
ComponentBeanReader(參考自 Spring 的 XmlBeanDefinitionReader)
用於初始化 ApplicationContext 時,讀取所有的 Bean,轉化爲 BeanDefinition。
public class ComponentBeanReader {
public void readBeanDefinition(DefaultBeanFactory beanFactory) {
Set<Class<?>> componentSet = ReflectionUtils.getAllClass(Component.class);
componentSet.forEach((componentClass) -> {
BeanDefinition beanDefinition = new BeanDefinition();
String beanName = componentClass.getAnnotation(Component.class).value();
if ("".equals(beanName)) {
beanName = StringUtils.lowerFirst(componentClass.getSimpleName());
}
beanDefinition.setBeanClass(componentClass);
beanFactory.registerBeanDefinition(beanName, beanDefinition);
});
}
}
測試
@Component
class A{
@Resource
private B b;
@Component
class A{
@Resource
private B b;
public void setB(B b) {
this.b = b;
}
public B getB() {
return b;
}
}
@Component
class B{
@Resource
private A a;
public void setA(A a) {
this.a = a;
}
public A getA() {
return a;
}
}
@Component
class C{
@Resource
private A a;
@Resource
B b;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ApplicationContext();
A a = context.getBean(A.class);
B b = context.getBean(B.class);
C c = (C)context.getBean("c");
System.out.println(a.getB());
System.out.println(b.getA());
System.out.println(c.getA());
System.out.println(c.getB());
}
}
最後
以上就是對 Spring 中單例 Bean 管理的一個簡單實現,代碼中比較難懂的部分是三級緩存的部分,對於三級緩存的詳細流程和介紹其實全部都在上面的流程圖裏,如果看懂了流程圖再看代碼就會覺得很簡單了。
同時這部分代碼也會作爲我實現的一個 web 框架 Dump 的一部分:Dump - A lightweight web framework:https://github.com/yuanguangxin/Dump
最後附上關於這部分實現的完整代碼:
https://github.com/yuanguangxin/Dump/tree/master/src/main/java/group/dump/beans
作者
本文作者「袁廣鑫」,歡迎關注作者的知乎:https://www.zhihu.com/people/yuan-yan-xin-53 專注於 Java 技術分享,點擊閱讀原文即可關注。