javascript
Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 簡介
本篇文章是上一篇文章(創建單例 bean 的過程)的延續。在上一篇文章中,我們從戰略層面上領略了doCreateBean方法的全過程。本篇文章,我們就從戰術的層面上,詳細分析doCreateBean方法中的一個重要的調用,即createBeanInstance方法。在本篇文章中,你將看到三種不同的構造 bean 對象的方式。你也會了解到構造 bean 對象的兩種策略。如果你對這些內容感興趣,那么不妨繼續往下讀。我會在代碼進行大量的注解,相信能幫助你理解代碼邏輯。好了,其他的就不多說了,進入正題吧。
?2. 源碼分析
?2.1 創建 bean 對象的過程
本節,我們一起來來分析一下本篇文章的主角createBeanInstance方法。按照慣例,我們還是先分析一下方法的大致脈絡,然后我們再按照這個脈絡去分析一些重要的調用。So. Let`s go → ↓
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {Class<?> beanClass = resolveBeanClass(mbd, beanName);/** 檢測類的訪問權限。默認情況下,對于非 public 的類,是允許訪問的。* 若禁止訪問,這里會拋出異常*/if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());}/** 如果工廠方法不為空,則通過工廠方法構建 bean 對象。這種構建 bean 的方式* 就不深入分析了,有興趣的朋友可以自己去看一下。*/if (mbd.getFactoryMethodName() != null) {// 通過“工廠方法”的方式構建 bean 對象return instantiateUsingFactoryMethod(beanName, mbd, args);}/** 當多次構建同一個 bean 時,可以使用此處的快捷路徑,即無需再次推斷應該使用哪種方式構造實例,* 以提高效率。比如在多次構建同一個 prototype 類型的 bean 時,就可以走此處的捷徑。* 這里的 resolved 和 mbd.constructorArgumentsResolved 將會在 bean 第一次實例* 化的過程中被設置,在后面的源碼中會分析到,先繼續往下看。*/boolean resolved = false;boolean autowireNecessary = false;if (args == null) {synchronized (mbd.constructorArgumentLock) {if (mbd.resolvedConstructorOrFactoryMethod != null) {resolved = true;autowireNecessary = mbd.constructorArgumentsResolved;}}}if (resolved) {if (autowireNecessary) {// 通過“構造方法自動注入”的方式構造 bean 對象return autowireConstructor(beanName, mbd, null, null);}else {// 通過“默認構造方法”的方式構造 bean 對象return instantiateBean(beanName, mbd);}}// 由后置處理器決定返回哪些構造方法,這里不深入分析了Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);/** 下面的條件分支條件用于判斷使用什么方式構造 bean 實例,有兩種方式可選 - 構造方法自動* 注入和默認構造方法。判斷的條件由4部分綜合而成,如下:* * 條件1:ctors != null -> 后置處理器返回構造方法數組是否為空* * 條件2:mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR * -> bean 配置中的 autowire 屬性是否為 constructor * 條件3:mbd.hasConstructorArgumentValues() * -> constructorArgumentValues 是否存在元素,即 bean 配置文件中* 是否配置了 <construct-arg/>* 條件4:!ObjectUtils.isEmpty(args) * -> args 數組是否存在元素,args 是由用戶調用 * getBean(String name, Object... args) 傳入的* * 上面4個條件,只要有一個為 true,就會通過構造方法自動注入的方式構造 bean 實例*/if (ctors != null ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {// 通過“構造方法自動注入”的方式構造 bean 對象return autowireConstructor(beanName, mbd, ctors, args);}// 通過“默認構造方法”的方式構造 bean 對象return instantiateBean(beanName, mbd); } |
以上就是 createBeanInstance 方法的源碼,不是很長。配合著注釋,應該不是很難懂。下面我們來總結一下這個方法的執行流程,如下:
這里有三種構造 bean 對象的方式,如下:
下面我將會分析第2和第3種構造 bean 對象方式的實現源碼。至于第1種方式,實現邏輯和第2種方式較為相似。所以就不分析了,大家有興趣可以自己看一下。
?2.2 通過構造方法自動注入的方式創建 bean 實例
本節,我將會分析構造方法自動注入的實現邏輯。代碼邏輯較為復雜,需要大家耐心閱讀。代碼如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, Constructor<?>[] ctors, Object[] explicitArgs) {// 創建 ConstructorResolver 對象,并調用其 autowireConstructor 方法return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs); }public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd,Constructor<?>[] chosenCtors, final Object[] explicitArgs) {// 創建 BeanWrapperImpl 對象BeanWrapperImpl bw = new BeanWrapperImpl();this.beanFactory.initBeanWrapper(bw);Constructor<?> constructorToUse = null;ArgumentsHolder argsHolderToUse = null;Object[] argsToUse = null;// 確定參數值列表(argsToUse)if (explicitArgs != null) {argsToUse = explicitArgs;}else {Object[] argsToResolve = null;synchronized (mbd.constructorArgumentLock) {// 獲取已解析的構造方法constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;if (constructorToUse != null && mbd.constructorArgumentsResolved) {// 獲取已解析的構造方法參數列表argsToUse = mbd.resolvedConstructorArguments;if (argsToUse == null) {// 若 argsToUse 為空,則獲取未解析的構造方法參數列表argsToResolve = mbd.preparedConstructorArguments;}}}if (argsToResolve != null) {// 解析參數列表argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);}}if (constructorToUse == null) {boolean autowiring = (chosenCtors != null ||mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR);ConstructorArgumentValues resolvedValues = null;int minNrOfArgs;if (explicitArgs != null) {minNrOfArgs = explicitArgs.length;}else {ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();resolvedValues = new ConstructorArgumentValues();/** 確定構造方法參數數量,比如下面的配置:* <bean id="persion" class="xyz.coolblog.autowire.Person">* <constructor-arg index="0" value="xiaoming"/>* <constructor-arg index="1" value="1"/>* <constructor-arg index="2" value="man"/>* </bean>** 此時 minNrOfArgs = maxIndex + 1 = 2 + 1 = 3,除了計算 minNrOfArgs,* 下面的方法還會將 cargs 中的參數數據轉存到 resolvedValues 中*/minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);}// 獲取構造方法列表Constructor<?>[] candidates = chosenCtors;if (candidates == null) {Class<?> beanClass = mbd.getBeanClass();try {candidates = (mbd.isNonPublicAccessAllowed() ?beanClass.getDeclaredConstructors() : beanClass.getConstructors());}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Resolution of declared constructors on bean Class [" + beanClass.getName() +"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);}}// 按照構造方法的訪問權限級別和參數數量進行排序AutowireUtils.sortConstructors(candidates);int minTypeDiffWeight = Integer.MAX_VALUE;Set<Constructor<?>> ambiguousConstructors = null;LinkedList<UnsatisfiedDependencyException> causes = null;for (Constructor<?> candidate : candidates) {Class<?>[] paramTypes = candidate.getParameterTypes();/** 下面的 if 分支的用途是:若匹配到到合適的構造方法了,提前結束 for 循環* constructorToUse != null 這個條件比較好理解,下面分析一下條件 argsToUse.length > paramTypes.length:* 前面說到 AutowireUtils.sortConstructors(candidates) 用于對構造方法進行* 排序,排序規則如下:* 1. 具有 public 訪問權限的構造方法排在非 public 構造方法前* 2. 參數數量多的構造方法排在前面** 假設現在有一組構造方法按照上面的排序規則進行排序,排序結果如下(省略參數名稱):** 1. public Hello(Object, Object, Object)* 2. public Hello(Object, Object)* 3. public Hello(Object)* 4. protected Hello(Integer, Object, Object, Object)* 5. protected Hello(Integer, Object, Object)* 6. protected Hello(Integer, Object)** argsToUse = [num1, obj2],可以匹配上的構造方法2和構造方法6。由于構造方法2有* 更高的訪問權限,所以沒理由不選他(盡管后者在參數類型上更加匹配)。由于構造方法3* 參數數量 < argsToUse.length,參數數量上不匹配,也不應該選。所以 * argsToUse.length > paramTypes.length 這個條件用途是:在條件 * constructorToUse != null 成立的情況下,通過判斷參數數量與參數值數量* (argsToUse.length)是否一致,來決定是否提前終止構造方法匹配邏輯。*/if (constructorToUse != null && argsToUse.length > paramTypes.length) {break;}/** 構造方法參數數量低于配置的參數數量,則忽略當前構造方法,并重試。比如 * argsToUse = [obj1, obj2, obj3, obj4],上面的構造方法列表中,* 構造方法1、2和3顯然不是合適選擇,忽略之。*/if (paramTypes.length < minNrOfArgs) {continue;}ArgumentsHolder argsHolder;if (resolvedValues != null) {try {/** 判斷否則方法是否有 ConstructorProperties 注解,若有,則取注解中的* 值。比如下面的代碼:* * public class Persion {* private String name;* private Integer age;** @ConstructorProperties(value = {"coolblog", "20"})* public Persion(String name, Integer age) {* this.name = name;* this.age = age;* }* }*/String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length);if (paramNames == null) {ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();if (pnd != null) {/** 獲取構造方法參數名稱列表,比如有這樣一個構造方法:* public Person(String name, int age, String sex)* * 調用 getParameterNames 方法返回 paramNames = [name, age, sex]*/paramNames = pnd.getParameterNames(candidate);}}/* * 創建參數值列表,返回 argsHolder 會包含進行類型轉換后的參數值,比如下* 面的配置:** <bean id="persion" class="xyz.coolblog.autowire.Person">* <constructor-arg name="name" value="xiaoming"/>* <constructor-arg name="age" value="1"/>* <constructor-arg name="sex" value="man"/>* </bean>** Person 的成員變量 age 是 Integer 類型的,但由于在 Spring 配置中* 只能配成 String 類型,所以這里要進行類型轉換。*/argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,getUserDeclaredConstructor(candidate), autowiring);}catch (UnsatisfiedDependencyException ex) {if (this.beanFactory.logger.isTraceEnabled()) {this.beanFactory.logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);}if (causes == null) {causes = new LinkedList<UnsatisfiedDependencyException>();}causes.add(ex);continue;}}else {if (paramTypes.length != explicitArgs.length) {continue;}argsHolder = new ArgumentsHolder(explicitArgs);}/** 計算參數值(argsHolder.arguments)每個參數類型與構造方法參數列表* (paramTypes)中參數的類型差異量,差異量越大表明參數類型差異越大。參數類型差異* 越大,表明當前構造方法并不是一個最合適的候選項。引入差異量(typeDiffWeight)* 變量目的:是將候選構造方法的參數列表類型與參數值列表類型的差異進行量化,通過量化* 后的數值篩選出最合適的構造方法。* * 講完差異量,再來說說 mbd.isLenientConstructorResolution() 條件。* 官方的解釋是:返回構造方法的解析模式,有寬松模式(lenient mode)和嚴格模式* (strict mode)兩種類型可選。具體的細節沒去研究,就不多說了。*/int typeDiffWeight = (mbd.isLenientConstructorResolution() ?argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));if (typeDiffWeight < minTypeDiffWeight) {constructorToUse = candidate;argsHolderToUse = argsHolder;argsToUse = argsHolder.arguments;minTypeDiffWeight = typeDiffWeight;ambiguousConstructors = null;}/* * 如果兩個構造方法與參數值類型列表之間的差異量一致,那么這兩個方法都可以作為* 候選項,這個時候就出現歧義了,這里先把有歧義的構造方法放入 * ambiguousConstructors 集合中*/else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {if (ambiguousConstructors == null) {ambiguousConstructors = new LinkedHashSet<Constructor<?>>();ambiguousConstructors.add(constructorToUse);}ambiguousConstructors.add(candidate);}}// 若上面未能篩選出合適的構造方法,這里將拋出 BeanCreationException 異常if (constructorToUse == null) {if (causes != null) {UnsatisfiedDependencyException ex = causes.removeLast();for (Exception cause : causes) {this.beanFactory.onSuppressedException(cause);}throw ex;}throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Could not resolve matching constructor " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");}/** 如果 constructorToUse != null,且 ambiguousConstructors 也不為空,表明解析* 出了多個的合適的構造方法,此時就出現歧義了。Spring 不會擅自決定使用哪個構造方法,* 所以拋出異常。*/else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Ambiguous constructor matches found in bean '" + beanName + "' " +"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +ambiguousConstructors);}if (explicitArgs == null) {/** 緩存相關信息,比如:* 1. 已解析出的構造方法對象 resolvedConstructorOrFactoryMethod* 2. 構造方法參數列表是否已解析標志 constructorArgumentsResolved* 3. 參數值列表 resolvedConstructorArguments 或 preparedConstructorArguments** 這些信息可用在其他地方,用于進行快捷判斷*/argsHolderToUse.storeCache(mbd, constructorToUse);}}try {Object beanInstance;if (System.getSecurityManager() != null) {final Constructor<?> ctorToUse = constructorToUse;final Object[] argumentsToUse = argsToUse;beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {return beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse);}}, beanFactory.getAccessControlContext());}else {/** 調用實例化策略創建實例,默認情況下使用反射創建實例。如果 bean 的配置信息中* 包含 lookup-method 和 replace-method,則通過 CGLIB 增強 bean 實例*/beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);}// 設置 beanInstance 到 BeanWrapperImpl 對象中bw.setBeanInstance(beanInstance);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Bean instantiation via constructor failed", ex);} } |
上面的方法邏輯比較復雜,做了不少事情,該方法的核心邏輯是根據參數值類型篩選合適的構造方法。解析出合適的構造方法后,剩下的工作就是構建 bean 對象了,這個工作交給了實例化策略去做。下面羅列一下這個方法的工作流程吧:
由上面的流程可以看得出,通過構造方法自動注入的方式構造 bean 對象的過程還是很復雜的。為了看懂這個流程,我進行了多次調試,算是勉強弄懂大致邏輯。由于時間有限,我并未能詳細分析 autowireConstructor 方法及其所調用的一些方法,比如 resolveConstructorArguments、 autowireConstructor 等。關于這些方法,這里只寫了個大概,有興趣的朋友自己去探索吧。
?2.3 通過默認構造方法創建 bean 對象
看完了上面冗長的邏輯,本節來看點輕松的吧 - 通過默認構造方法創建 bean 對象。如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {try {Object beanInstance;final BeanFactory parent = this;// if 條件分支里的一大坨是 Java 安全相關的代碼,可以忽略,直接看 else 分支if (System.getSecurityManager() != null) {beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic Object run() {return getInstantiationStrategy().instantiate(mbd, beanName, parent);}}, getAccessControlContext());}else {/** 調用實例化策略創建實例,默認情況下使用反射創建對象。如果 bean 的配置信息中* 包含 lookup-method 和 replace-method,則通過 CGLIB 創建 bean 對象*/beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);}// 創建 BeanWrapperImpl 對象BeanWrapper bw = new BeanWrapperImpl(beanInstance);initBeanWrapper(bw);return bw;}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);} }public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {// 檢測 bean 配置中是否配置了 lookup-method 或 replace-method,若配置了,則需使用 CGLIB 構建 bean 對象if (bd.getMethodOverrides().isEmpty()) {Constructor<?> constructorToUse;synchronized (bd.constructorArgumentLock) {constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;if (constructorToUse == null) {final Class<?> clazz = bd.getBeanClass();if (clazz.isInterface()) {throw new BeanInstantiationException(clazz, "Specified class is an interface");}try {if (System.getSecurityManager() != null) {constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {@Overridepublic Constructor<?> run() throws Exception {return clazz.getDeclaredConstructor((Class[]) null);}});}else {// 獲取默認構造方法constructorToUse = clazz.getDeclaredConstructor((Class[]) null);}// 設置 resolvedConstructorOrFactoryMethodbd.resolvedConstructorOrFactoryMethod = constructorToUse;}catch (Throwable ex) {throw new BeanInstantiationException(clazz, "No default constructor found", ex);}}}// 通過無參構造方法創建 bean 對象return BeanUtils.instantiateClass(constructorToUse);}else {// 使用 GCLIG 創建 bean 對象return instantiateWithMethodInjection(bd, beanName, owner);} } |
上面就是通過默認構造方法創建 bean 對象的過程,比較簡單,就不多說了。最后我們再來看看簡單看看通過無參構造方法剛創建 bean 對象的代碼(通過 CGLIB 創建 bean 對象的方式就不看了)是怎樣的,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {Assert.notNull(ctor, "Constructor must not be null");try {// 設置構造方法為可訪問ReflectionUtils.makeAccessible(ctor);// 通過反射創建 bean 實例,這里的 args 是一個沒有元素的空數組return ctor.newInstance(args);}catch (InstantiationException ex) {throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);}catch (IllegalAccessException ex) {throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);}catch (IllegalArgumentException ex) {throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);}catch (InvocationTargetException ex) {throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());} } |
到這里,終于看到了創建 bean 對象的代碼了。在經歷層層調用后,我們總算是追到了調用棧的最深處。看到這里,大家可以休息一下了,本文也差不多要結束了。好了,最后再容我多啰嗦一會,往下看。
?3.寫在最后
寫到這里,我也算是松了一口氣,終于快寫完了。這篇文章寫起來感覺挺不容易的,原因是 createBeanInstance 及其調用的方法是在太多了,而且很多方法邏輯還是比較復雜的,尤其是 autowireConstructor 中調用的一些方法。autowireConstructor 中調用的方法我基本上都看了一遍,但并非全部都弄懂了,有些方法只是知道個大概。所以,這篇文章寫的我挺糾結的,生怕有些地方分析的不對。由于我后續還有很多東西要看,以至于我暫時沒法抽出大量的時間去詳細閱讀 Spring 的源碼。所以如果上面的分析有不對的地方,歡迎指正,我會虛心聽之。如果這些不對的地方給你造成了困擾,實在很抱歉,抱歉。
好了,本篇文章先到這里。謝謝閱讀!
?參考
- 《Spring 源碼深度解析》- 郝佳
?附錄:Spring 源碼分析文章列表
?Ⅰ. IOC
| 2018-05-30 | Spring IOC 容器源碼分析系列文章導讀 |
| 2018-06-01 | Spring IOC 容器源碼分析 - 獲取單例 bean |
| 2018-06-04 | Spring IOC 容器源碼分析 - 創建單例 bean 的過程 |
| 2018-06-06 | Spring IOC 容器源碼分析 - 創建原始 bean 對象 |
| 2018-06-08 | Spring IOC 容器源碼分析 - 循環依賴的解決辦法 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 填充屬性到 bean 原始對象 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
?Ⅱ. AOP
| 2018-06-17 | Spring AOP 源碼分析系列文章導讀 |
| 2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
| 2018-06-20 | Spring AOP 源碼分析 - 創建代理對象 |
| 2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執行過程 |
?Ⅲ. MVC
| 2018-06-29 | Spring MVC 原理探秘 - 一個請求的旅行過程 |
| 2018-06-30 | Spring MVC 原理探秘 - 容器的創建過程 |
- 本文鏈接:?https://www.tianxiaobo.com/2018/06/06/Spring-IOC-容器源碼分析-創建原始-bean-對象/
from:?http://www.tianxiaobo.com/2018/06/06/Spring-IOC-%E5%AE%B9%E5%99%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E5%88%9B%E5%BB%BA%E5%8E%9F%E5%A7%8B-bean-%E5%AF%B9%E8%B1%A1/?
總結
以上是生活随笔為你收集整理的Spring IOC 容器源码分析 - 创建原始 bean 对象的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring IOC 容器源码分析 -
- 下一篇: Spring IOC 容器源码分析 -