SpringMvc學習心得(三) spring例項化JavaBean的過程
我們一般是通過呼叫一個類的建構函式完成例項化的工作,spring當然也不例外。然而相比於直接通過建構函式對類進行例項化,spring的例項化過程要複雜得多。
在之前的部落格中,本人曾經說過,任何一個JavaBean都有一個beandefinition。然而beandefinition並沒有規定一個類的建構函式是哪個。原因很簡單,因為在xml檔案中沒有規定建構函式的方法。spring只能通過<constructor-arg/>標籤中的資訊去“猜測”哪一個建構函式最合適。而“猜測建構函式”這個工作並不適合在構建beandefinition時完成,其更適合在例項化JavaBean的時候進行。在合適的時間做合適的事情,spring很好地把握了這一點。
spring通過AbstractAutowireCapableBeanFactory類中的createBeanInstance完成類的例項化。其程式碼如下:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) { // Make sure bean class is actually resolved at this point. Class beanClass = resolveBeanClass(mbd, beanName); 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()); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same 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) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } // Need to determine the constructor... Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
對於被<constructor-arg/>修飾的JavaBean,其最終歸宿是在autowireConstructor這個函式中,而該函式則呼叫了其他類的autowireConstructor,而該方法的具體實現如下所示。
上面的程式碼很長,但通過分析,其主要包含以下幾段程式碼:public BeanWrapper autowireConstructor( final String beanName, final RootBeanDefinition mbd, Constructor[] chosenCtors, final Object[] explicitArgs) { BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; if (explicitArgs != null) { argsToUse = explicitArgs; } else { Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { constructorToUse = (Constructor) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { argsToResolve = mbd.preparedConstructorArguments; } } } if (argsToResolve != null) { argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } if (constructorToUse == null) { // Need to resolve the constructor. 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(); minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // Take specified constructors, if any. 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; List<Exception> causes = null; for (int i = 0; i < candidates.length; i++) { Constructor<?> candidate = candidates[i]; Class[] paramTypes = candidate.getParameterTypes(); if (constructorToUse != null && argsToUse.length > paramTypes.length) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. break; } if (paramTypes.length < minNrOfArgs) { continue; } ArgumentsHolder argsHolder; if (resolvedValues != null) { try { String[] paramNames = null; if (constructorPropertiesAnnotationAvailable) { paramNames = ConstructorPropertiesChecker.evaluateAnnotation(candidate, paramTypes.length); } if (paramNames == null) { ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { paramNames = pnd.getParameterNames(candidate); } } argsHolder = createArgumentArray( beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring); } catch (UnsatisfiedDependencyException ex) { if (this.beanFactory.logger.isTraceEnabled()) { this.beanFactory.logger.trace( "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex); } if (i == candidates.length - 1 && constructorToUse == null) { if (causes != null) { for (Exception cause : causes) { this.beanFactory.onSuppressedException(cause); } } throw ex; } else { // Swallow and try next constructor. if (causes == null) { causes = new LinkedList<Exception>(); } causes.add(ex); continue; } } } else { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<Constructor>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } if (constructorToUse == null) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Could not resolve matching constructor " + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)"); } 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) { 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>() { public Object run() { return beanFactory.getInstantiationStrategy().instantiate( mbd, beanName, beanFactory, ctorToUse, argumentsToUse); } }, beanFactory.getAccessControlContext()); } else { beanInstance = this.beanFactory.getInstantiationStrategy().instantiate( mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } bw.setWrappedInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
程式碼段1:該段程式碼比較了candidate引數與<constructor-arg/>中規定引數之間的差異值,並將差異值最小的那一個賦予給constuctorToUse,並將引數列表賦予給argsToUse物件,在該段程式碼之外包裹了一個for迴圈,該迴圈會遍歷類的所有建構函式。
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this constructor if it represents the closest match.
if (typeDiffWeight < minTypeDiffWeight) {
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
}
程式碼段2:該段程式碼呼叫類的建構函式完成類的例項化,引數列表作為函式的入參。
beanInstance = this.beanFactory.getInstantiationStrategy().instantiate(
mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
beanfactory呼叫getInstantiationStrategy方法,該方法主要決定了類的反射策略,預設是cglib。
除了通過<constructor-arg />標籤,spring也能夠使用@Autowired註解控制建構函式的入參。而解析被@Autowired修飾的建構函式這一任務由determineCandidateConstructors完成,該函式在autowireConstructor之前被執行。而該函式一旦成功解析出了建構函式,那麼就不再執行autowireConstructor方法。determineCandidateConstructors具體程式碼如下:
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
Constructor<?>[] rawCandidates = beanClass.getDeclaredConstructors();
List<Constructor<?>> candidates = new ArrayList<Constructor<?>>(rawCandidates.length);
Constructor<?> requiredConstructor = null;
Constructor<?> defaultConstructor = null;
for (Constructor<?> candidate : rawCandidates) {
Annotation annotation = findAutowiredAnnotation(candidate);
if (annotation != null) {
if (requiredConstructor != null) {
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
if (candidate.getParameterTypes().length == 0) {
throw new IllegalStateException(
"Autowired annotation requires at least one argument: " + candidate);
}
boolean required = determineRequiredStatus(annotation);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException(
"Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " +
requiredConstructor);
}
requiredConstructor = candidate;
}
candidates.add(candidate);
}
else if (candidate.getParameterTypes().length == 0) {
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null && defaultConstructor != null) {
candidates.add(defaultConstructor);
}
candidateConstructors = candidates.toArray(new Constructor[candidates.size()]);
}
else {
candidateConstructors = new Constructor[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
仔細閱讀程式碼,我們可以發現其主要過程就是遍歷所有的建構函式,找到被@Autowired修飾的那一個並返回。同時,對於多個建構函式被@Autowired修飾的情況以及建構函式沒有引數的情況,spring會丟擲異常。spring例項化JavaBean存在一個隱患,即無論JavaBean是通過xml配置還是註解配置,其都有可能導致建構函式的迴圈依賴。
什麼是建構函式的迴圈依賴?假設我們有兩個類,分別是A和B,其定義如下:
A:
public class A{
public A(B b){
//some code
}
}
B:
public class B{
public B(A a){
//some code
}
}
在程式碼中無法直接通過建構函式完成類A的例項化,因為這會導致無窮的遞迴,其結果如下一行程式碼所示:
A a=new A(new B(new A(new B(.......))))
對於迴圈依賴,建構函式的程式碼根本無法繼續書寫,更別說變成位元組碼供JVM呼叫了。但是,由於spring允許通過<constructor-arg/>標籤對建構函式入參進行配置,因此在spring的世界裡,迴圈依賴能夠被人為的構造出來。spring中的迴圈依賴例子如下所示:<bean id="test1" class="com.tan.tool.Test1" >
<constructor-arg ref="test"/>
</bean>
<bean id="test" class="com.tan.tool.Test" >
<constructor-arg ref="test1"/>
</bean>
為了避免這一問題,spring會在例項化的過程中檢測是否存在迴圈依賴,其具體實現方法是在beanfactory呼叫creatBean函式之前會呼叫isPrototypeCurrentlyInCreation(beanName)函式,該函式具體實現如下:
protected final boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return (curVal != null &&
(curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName))));
}
其中prototypesCurrentlyIncreation是一個ThreadLocal<Object>類,該物件儲存了所有正在被例項化的JavaBean。一旦出現重複的beanname,該函式會返回true,並導致丟擲BeanCurrentlyInCreationException異常類。其具體程式碼在AbstractBeanFactory的doGetBean函式中:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}