解析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 先寫到這,明天再更