Spring功能實現與源碼解讀 - IOC

    解析IOC的工做原理,以及代碼邏輯。java

    在解讀以前先用本身的思路嘗試實現一下Spring。初學過Spring的同窗應該都知道一個名詞ApplicationContext,意爲應用上下文。說的通俗一點,「應用上下文」就是描述一個應用內部對象之間的相互依賴關係,好比「對象A有一個屬性是對象B」,那麼稱「對象A依賴一個對象B」,前半句來自於你的代碼,後半句來自於Spring對你代碼的解讀。spring

    那麼Spring如何實現解讀對象之間的依賴關係呢?咱們先不急着看代碼,假如咱們本身實現的話,至少有如下一種方法能夠實現:反射。好比咱們能夠在配置文件中定義對象之間的依賴關係,而後經過反射實例化這個類。緩存

    定義類A(其對象依賴一個B實例):app

package com.wuuyao.study;

public class A {

    private B bInstance;

}

    定義類B(不依賴任何其它對象):less

package com.wuuyao.study;

public class B {
}

    在類C中編寫實例化化類A以及類B的方法:this

package com.wuuyao.study;

public class C {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 能夠寫在配置文件中,此處不贅述
        String aClasspath = "com.wuuyao.study.A";
        String bClasspath = "com.wuuyao.study.B";

        /**
         * 使用反射初始化類B,暫不設置屬性。
         */
        // B類加載
        Class classB = Class.forName(bClasspath);
        // B對象實例化
        B bInstance = (B) classB.newInstance();

        // A同操做
        Class classA = Class.forName(aClasspath);
        A aInstance = (A) classA.newInstance();
    }

}

    但上述代碼並未實現A的屬性注入,咱們能夠經過增長一條配置來實現:spa

package com.wuuyao.study;

import java.lang.reflect.Field;

public class C {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        // 能夠寫在配置文件中,此處不贅述
        String aClasspath = "com.wuuyao.study.A";
        String bClasspath = "com.wuuyao.study.B";
        // 新增配置,用於描述依賴關係
        String aDependencyConfig = "A need field bInstance";

        /**
         * 使用反射初始化類B,暫不設置屬性。
         */
        Object B = instance(bClasspath);
        Object A = instance(aClasspath, aDependencyConfig);
    }

    /**
     * 經過完整類路徑,以及依賴配置進行實例化對象
     *
     * @param classpath    類路徑
     * @param dependencies 依賴關係
     * @return
     */
    private static final Object instance(String classpath, String... dependencies) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        // 目標對象實例化
        Class targetClass = Class.forName(classpath);
        Object instance = targetClass.newInstance();

        /**
         * 爲目標對象設置屬性。咱們的配置格式是"A need field bInstance",即"類型A 須要屬性 bInstance 的注入"
         */
        for (String dependencyConfig : dependencies) {
            String injectFieldName = dependencyConfig.split(" need field ")[1];

            Field injectField = targetClass.getDeclaredField(injectFieldName);
            // 將屬性設置爲可見
            injectField.setAccessible(true);

            // 獲取屬性的類路徑並實例化
            String fieldClasspath = injectField.getType().getCanonicalName();
            // TODO 經過遞歸調用instance方法實例化屬性並設值,但此處有問題,由於咱們不知道屬性bInstance是否有依賴
            Object fieldObject = instance(fieldClasspath);
            injectField.set(instance, fieldObject);

            // 還原屬性設置
            injectField.setAccessible(false);
        }


        return instance;
    }

}

    上述代碼是有缺陷的。首先,實際上咱們的目的是實例化後的B注入實例化的A,但上述代碼是另外建立了一個B實例去注入A實例。所以咱們須要一個集合去保存實例化的結果。另外,不支持多個同類型實例注入,好比A中有兩個不一樣的B屬性時,注入將會失敗。所以,經過反射實例化的屬性還須要一個標識(ID),而且在依賴配置聲明時也須要一個標識(ID)。咱們能夠以下實現。code

    定義一個對象配置類對象

package com.wuuyao.study;

import java.util.List;

public class ObjectCreateConfig {
    private String id;
    private String instanceConfig;
    private List<String> dependencyConfigList;
}

    修改實例化流程遞歸

package com.wuuyao.study;

import java.lang.reflect.Field;
import java.util.*;

public class C {

    /**
     * 緩存實例化任務
     * key: 實例化對象的ID
     * val: 實例化對象配置清單
     */
    private static Map<String, ObjectCreateConfig> configMap = new HashMap<>();

    /**
     * 緩存已經實例化的對象
     * key: 實例ID
     * val: 實例對象
     */
    private static Map<String, Object> objectMap = new HashMap<>();

    public static void main(String[] args) throws Exception {
        // 能夠寫在配置文件中,此處不贅述
        String instanceConfigA = "com.wuuyao.study.A , id = instanceA01";
        // 新增配置,用於描述依賴關係
        String aDependencyConfig = "A need field bInstance, id=instanceB01";
        // 將實例化配置添加到配置表中
        addDependencyConfig(instanceConfigA, aDependencyConfig);

        String instanceConfigB = "com.wuuyao.study.B , id = instanceB01";
        addDependencyConfig(instanceConfigB);

        // 根據配置進行裝配
        Iterator<Map.Entry<String, ObjectCreateConfig>> iterator = configMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, ObjectCreateConfig> config = iterator.next();
            Object instance = instance(config.getKey());
            if (instance != null) {
                iterator.remove();
            }
        }
    }

    private static void addDependencyConfig(String classpath, String... dependencies) {
        ObjectCreateConfig config = new ObjectCreateConfig();

        List<String> configList = new ArrayList<>();
        // "A need field bInstance, id=bInstance01"
        for (String dependency : dependencies) {
            configList.add(dependency);
        }
        // 添加到配置緩存中
        // "com.wuuyao.study.A , id = instanceA01"
        String[] objectInfo = classpath.split(" , id = ");
        config.setId(objectInfo[1]);
        config.setInstanceConfig(classpath);
        config.setDependencyConfigList(configList);
        configMap.put(config.getId(), config);
    }

    /**
     * 經過完整類路徑,以及依賴配置進行實例化對象
     *
     * @param instanceId 配置對象
     * @return
     */
    private static final Object instance(String instanceId) throws Exception {
        // 目標對象實例化
        ObjectCreateConfig config = configMap.get(instanceId);
        if (config == null) {
            throw new Exception("未定義的對象");
        }

        String classpath = config.getInstanceConfig().split(" , ")[0];
        Class targetClass = Class.forName(classpath);
        Object instance = targetClass.newInstance();
        // 若已經被初始化則直接返回該對象
        if (objectMap.get(instanceId) != null) {
            return objectMap.get(instanceId);
        }

        /**
         * 爲目標對象設置屬性。咱們的配置格式是"A need field bInstance, id=bInstance01",
         *                                   即"類型A 須要屬性 bInstance 的注入,屬性ID爲bInstanceId01"
         */
        for (String dependencyConfig : config.getDependencyConfigList()) {
            String injectFieldName = dependencyConfig.split(" need field ")[1].split(", id=")[0];
            String injectFieldObjectId = dependencyConfig.split(" need field ")[1].split(", id=")[1];
            Field injectField = targetClass.getDeclaredField(injectFieldName);
            // 將屬性設置爲可見
            injectField.setAccessible(true);

            // 獲取屬性的類路徑並實例化
            String fieldClasspath = injectField.getType().getCanonicalName();

            // 嘗試從緩存中獲取對象
            Object fieldObject = objectMap.get(injectFieldObjectId);
            if (fieldObject == null) {
                // 嘗試建立子對象
                fieldObject = instance(injectFieldObjectId);
            }

            injectField.set(instance, fieldObject);

            // 還原屬性設置
            injectField.setAccessible(false);
        }

        // TODO 放入緩存中。此處暫不討論ID重複的狀況
        objectMap.put(instanceId, instance);

        return instance;
    }

}

    到此,咱們已經大體實現了IOC的原型,這個原型很是粗糙,僅爲理解Spring提供一些思路,另外,爲了和防止和Spring的概念重合,故意使用了自創的命名。

-------------------------------------------------------------------------------------------------------------------------------------------------

    接下來咱們來看Spring是怎麼實現的。

package org.springframework.boot;

public class SpringApplication{

	/**
	 * Run the Spring application, creating and refreshing a new
	 * {@link ApplicationContext}.
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return a running {@link ApplicationContext}
	 */
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

}

    上述代碼是Spring建立上下文的主要代碼。能夠一眼看見其中與ApplicationContext有關的代碼段。

context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);

    // TODO 先寫到這,明天再更

相關文章
相關標籤/搜索