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 先写到这,明天再更

相关文章
相关标签/搜索