SpringMvc學習心得(二)spring註解配置原理淺析
除了使用xml檔案配置以外,spring還支援使用註解實現JavaBean的配置,其具體實現方式網上已經介紹的很詳細,這裡就不再贅述了。本文將主要通過原始碼解析spring註解配置JavaBean的全過程。這裡主要分析的是@component和@Autowired這兩個註解。
首先要明確這樣一個問題,註解配置和xml檔案配置二者並“完全”不衝突。說不完全是因為如果使用@component修飾一個類,那麼就無法通過xml為這個類設定某些引數,如property的值。但是通過xml檔案配置的JavaBean則可以使用@Autowired完成屬性的配置。這個問題說明了註解配置方法是xml檔案配置方法的進化版,並且是依附於xml檔案配置的。下面將主要介紹其實現原理。
使用註解配置方法的xml檔案配置:
<context:component-scan base-package="com.luanbin.annotation" />
根據上一篇部落格的介紹,可以找到這個component-scan對應的解析類AnnotationConfigBeanDefinitionParser
該解析類的parse方法:
public BeanDefinition parse(Element element, ParserContext parserContext) { Object source = parserContext.extractSource(element); // Obtain bean definitions for all relevant BeanPostProcessors. Set<BeanDefinitionHolder> processorDefinitions = AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source); // Register component for the surrounding <context:annotation-config> element. CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source); parserContext.pushContainingComponent(compDefinition); // Nest the concrete beans in the surrounding component. for (BeanDefinitionHolder processorDefinition : processorDefinitions) { parserContext.registerComponent(new BeanComponentDefinition(processorDefinition)); } // Finally register the composite component. parserContext.popAndRegisterContainingComponent(); return null; }
parse方法只完成了一項工作,掃描指定的package,同時將被@component修飾的類包裝成beandefinition,需要尤其注意的事,因為註解相比於xml檔案擁有的資訊較少,因此該beandefinition除了實現類,即被@component修飾的類資訊以外,不包含其他資訊,如建構函式,各個property的屬性等。JavaBean在AbstractAutowireCapableBeanFactory的doCreatBean函式中被例項化並且配置相關屬性,其具體程式碼如下:
其中createBeanInstance(beanName, mbd, args)的功能是根據beandefinition反射出來累的建構函式,並通過建構函式完成JavaBean的例項化,其具體程式碼如下:protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); } // Initialize the bean instance. Object exposedObject = bean; try { populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { exposedObject = initializeBean(beanName, exposedObject, mbd); } } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
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);
}
在完成JavaBean的例項化之後,spring通過BeanPostProcessor對該JavaBean進行處理。applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
其中AutowiredAnnotationBeanPostProcessor的主要工作就是判斷JavaBean中是否有被@Autowired修飾的field和method,並將該資訊包裝成InjecMetadata儲存在list中。其核心程式碼如下:
private InjectionMetadata findAutowiringMetadata(Class<?> clazz) {
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(clazz, metadata);
}
}
}
return metadata;
}
和xml配置的JavaBean一樣,spring通過populateBean方法完成JavaBean中被@Autowired所修飾的屬性的注入。而注入的過程依舊是呼叫BeanPostProcessor的相關方法。注入的方法如下:
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
而metadata的注入過程實際上就是從beanfactory中獲取JavaBean的例項並配置到目標bean上。可以看得出來,這種例項化方法存在遞迴過程。因此在例項化之前,spring會檢測是否存在迴圈依賴,檢測迴圈依賴的方法將在之後進行介紹。