
Spring IOC容器可以自動裝配(autowire)相互協(xié)作bean之間的關(guān)聯(lián)關(guān)系,簡單來說,Spring的自動裝配可以幫助我們處理bean與bean之間的關(guān)系,不用我們?nèi)ヅ渲盟麄冊撌褂媚膫€類。這樣帶來的好處是能明顯減少配置的工作量(用bean模板其實也可以實現(xiàn)同樣的效果),并且能使配置與代碼同步更新。但其壞處就是會導致裝配不明確,降低配置文件的可讀性。
Spring自動裝配有5種方式:
- no:默認不使用
- byName:根據(jù)屬性名
- byType:根據(jù)屬性類型
- constructor:根據(jù)構(gòu)造器參數(shù)
- autodetect:根據(jù)bean類的自省機制(introspection)來決定是用constructor還是byType
眾所周知,所有Spring Boot項目的啟動入口都是@SpringBootApplication,同時本文分析的“自動裝配”也要從這下手。
參考文章:
Spring的自動裝配
深入理解SpringBoot之自動裝配
P.S. 本文源碼是Spring Boot 1.3.7.RELEASE
1、@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
//省略
}
@SpringBootApplication是個復合的注解,包括@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration。通過源碼可以知道@SpringBootConfiguration本質(zhì)上是個@Configuration,由于@ComponentScan沒有指定掃描包,因此其默認掃描與該類同級的類或者同級包下的所有類。
2、@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//省略
}
該注解就是開啟自動裝配功能,是自動裝配的核心注解。Spring會試圖在classpath下找到所有配置的bean,接著進行裝配。裝配過程中,會根據(jù)若干個Conditional條件(Spring4.X引入了@Conditional)定制規(guī)則來進行初始化。
@EnableAutoConfiguration主要是@Import(EnableAutoConfigurationImportSelector.class),引入EnableAutoConfigurationImportSelector.class,來看下其中的關(guān)鍵函數(shù)selectImports()
@Override
public String[] selectImports(AnnotationMetadata metadata) {
try {
AnnotationAttributes attributes = getAttributes(metadata);
List<String> configurations = getCandidateConfigurations(metadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(metadata, attributes);
configurations.removeAll(exclusions);
configurations = sort(configurations);
recordWithConditionEvaluationReport(configurations, exclusions);
return configurations.toArray(new String[configurations.size()]);
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
}
從該函數(shù)可以看出,先從META-INF/spring-autoconfigure-metadata.properties獲取元數(shù)據(jù)與元數(shù)據(jù)之間的相關(guān)屬性,接著調(diào)用getCandidateConfigurations()函數(shù)
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
return SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}
/**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
在這里很巧遇到了SpringFactoriesLoader,他會獲取EnableAutoConfiguration.class中的配置。
接著selectImports函數(shù)將相關(guān)數(shù)據(jù)進行去重、過濾、排除,最后得到需要裝配的類。
3、invokeBeanFactoryPostProcessors
前面講了怎么獲取需要裝配的類,那么接下來探討下具體怎么裝配。
從抽象類AbstractApplicationContext的refresh()方法下手,refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory)就是關(guān)鍵。
可以看出該方法是觸發(fā)BeanFactoryPostProcessors,那么跟進去看下
/**
* Instantiate and invoke all registered BeanFactoryPostProcessor beans,
* respecting explicit order if given.
* <p>Must be called before singleton instantiation.
*/
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
來看下BeanFactoryPostProcessor接口的其中一種實現(xiàn)ConfigurationClassPostProcessor類(該類主要處理@Configuration注解)
/**
* Derive further bean definitions from the configuration classes in the registry.
*/
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class);
iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp);
RootBeanDefinition ecbpp = new RootBeanDefinition(EnhancedConfigurationBeanPostProcessor.class);
ecbpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(ENHANCED_CONFIGURATION_PROCESSOR_BEAN_NAME, ecbpp);
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
processConfigBeanDefinitions(registry);
}
postProcessBeanDefinitionRegistry方法是將configuration配置類中派生出bean定義
/**
* Build and validate a configuration model based on the registry of
* {@link Configuration} classes.
*/
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//省略代碼
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
//省略代碼
}
processConfigBeanDefinitions方法是基于@Configuration的類中構(gòu)建和校驗出配置的模型model,其中解析@Configuration關(guān)鍵類是ConfigurationClassParser,我們跟進去看下
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Exception ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
processDeferredImportSelectors();
}
很巧的是,parse()方法最后會調(diào)用前面聊到的processDeferredImportSelectors()。
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
try {
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Exception ex) {
throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
processDeferredImportSelectors()中有句關(guān)鍵代碼
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
其中deferredImport為DeferredImportSelectorHolder類,
private static class DeferredImportSelectorHolder {
private final ConfigurationClass configurationClass;
private final DeferredImportSelector importSelector;
public DeferredImportSelectorHolder(ConfigurationClass configurationClass, DeferredImportSelector importSelector) {
this.configurationClass = configurationClass;
this.importSelector = importSelector;
}
public ConfigurationClass getConfigurationClass() {
return this.configurationClass;
}
public DeferredImportSelector getImportSelector() {
return this.importSelector;
}
}
DeferredImportSelectorHolder內(nèi)部類中有DeferredImportSelector 的引用,到這里也就完成了整個自動裝配的所有操作。
小結(jié)
- 自動裝配歸根到底,是使用SpringFactoriesLoader來加載所有被@EnableAutoConfiguration修飾的類中的配置,通過selectImports函數(shù)將相關(guān)數(shù)據(jù)去重、過濾、排除,最終確定需要裝配的類;
- 當AbstractApplicationContext執(zhí)行refresh()方法時,其中的invokeBeanFactoryPostProcessors方法會觸發(fā)BeanFactoryPostProcessors去執(zhí)行自動裝配。
比如處理@Configuration注解的ConfigurationClassPostProcessor類,其本身是BeanFactoryPostProcessors接口的具體實現(xiàn),通過ConfigurationClassParser.parse()調(diào)用processDeferredImportSelectors(),執(zhí)行DeferredImportSelector,來完成自動裝配。
Spring Boot自動裝配源碼分析就到這里了,建議讀者選擇其中關(guān)鍵源碼閱讀,再加上調(diào)試源碼加深理解。后面可能會不定期更新,有興趣的朋友可以在評論區(qū)一起討論研究。
最后有件很重要的事,那就是麻煩點贊關(guān)注贊賞,謝謝(??????)??