仿springIoc實現自己的IOC,支持三級緩存——本人見過講springIOC最通透的文章,附git地址,代碼下載!

Spring 中的 IOC

Spring 的 IOC 其實很複雜,因爲它支持的情況,種類,以及開放的接口,拓展性(如各種PostProcessor)太豐富了。這導致我們在看 Spring 源碼的過程中非常喫力,經常點進去一個函數發現很深很深。這篇我主要針對 Spring 的 IOC 中的核心部分,例如 Spring 的 IOC 是如何實現的,Spring 是如何解決循環依賴的這類問題做一個介紹以及一份實現,因爲原理是相通的,對於 Spring 對各種情況的邏輯上的處理不做細緻的討論,對原型模式,或是 FactoryBean 類型的 Bean 的不同處理方式不做具體實現。

本文將實現一個怎樣的 IOC

  1. 僅支持 Singleton 單例模式的 Bean 管理。(這也是我們在平時項目中最常用的模式)

  2. 僅支持 無參構造器的 Bean 的管理。(這部分如果實現支持有參構造器的也很簡單,後續可能會補充)

  3. 僅支持 按照 BeanName 的方式加載 Bean 的方式,如果遇到 Class 的情況,將獲取Class 的 SimpleName 後繼續按照 BeanName 的方式加載。(這裏類似於在 Spring 當中使用 @AutoWaired 按類型匹配不到的情況依然會按照 Name 的方式去匹配)

  4. 支持 自動裝配,並完美解決循環依賴問題。

流程設計

基礎流程設計

如果不考慮循環依賴的問題,不考慮三級緩存的情況下,實現我們這樣一個IOC的功能很簡單:

  1. 加載所有的需要被我們管理的 Bean(被 @Component 修飾的類),轉換成 Bean 的定義(BeanDefinition,後面會說),存放在 Map 中。

  2. 利用反射得到這些 Bean 的實例,將這些 Bean 的實例存儲在我們的容器內。

  3. 填充我們需要自動裝配的屬性(被 @Resource 修飾的屬性)。

完成以上三步,就可以在外層容器調用 getBean() 方法獲取 Bean 的實例了。

如何解決循環依賴

網上關於 Spring 如何解決循環依賴的文章很多,簡單來說就是利用緩存,先將沒有填充屬性的對象緩存起來,需要的時候先去用這個對象,不必等待一個對象完整的初始化好。而爲什麼是三級緩存不是二級緩存呢,這裏籠統的來說還是方便 Spring 或者開發者們去拓展一些東西,例如在 Spring Bean 的生命週期中有很多的 Processor,這個我們後續再講。關於這部分的細節上的邏輯,在後面介紹完三級緩存會有一個很詳細的流程圖。

三級緩存

三級緩存的實現在代碼中的 SingletonBeanRegistry 中:

其中有以下幾個核心屬性:

  1. singletonObjects:一級緩存,用於存放完全初始化好的 bean,從該緩存中取出的 bean 可以直接使用。

  2. earlySingletonObjects:二級緩存,用於存放提前曝光的單例對象的cache,原始的 bean 對象(尚未填充屬性)。

  3. singletonFactories:三級緩存,用於存放 bean 工廠對象(ObjectFactory)。三級緩存中用到了 ObjectFactory,這裏的 ObjectFactory 可以理解爲三級緩存中 Bean 的代理對象,其 getObject() 方法描述瞭如何獲取這個三級緩存的對象。設計成這樣除了方便實現三級緩存解決循環依賴,另外也是方便 Spring 在 ObjectFactory 中做一些拓展。

  4. 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 技術分享,點擊閱讀原文即可關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章