Spring IOC容器可以自动装配(autowire)相互协作bean之间的关联关系,简单来说,Spring的自动装配可以帮助我们处理bean与bean之间的关系,不用我们去配置他们该使用哪个类。这样带来的好处是能明显减少配置的工作量(用bean模板其实也可以实现同样的效果),并且能使配置与代码同步更新。但其坏处就是会导致装配不明确,降低配置文件的可读性。
Spring自动装配有5种方式:
- no:默认不使用
- byName:根据属性名
- byType:根据属性类型
- constructor:根据构造器参数
- autodetect:根据bean类的自省机制(introspection)来决定是用constructor还是byType
众所周知,所有Spring Boot项目的启动入口都是@SpringBootApplication,同时本文分析的“自动装配”也要从这下手。
1、@SpringBootApplication
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
//省略
}
@SpringBootApplication是个复合的注解,包括@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration。通过源码可以知道@SpringBootConfiguration本质上是个@Configuration,由于@ComponentScan没有指定扫描包,因此其默认扫描与该类同级的类或者同级包下的所有类。
2、@EnableAutoConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//省略
}
该注解就是开启自动装配功能,是自动装配的核心注解。Spring会试图在classpath下找到所有配置的bean,接着进行装配。装配过程中,会根据若干个Conditional条件(Spring4.X引入了@Conditional)定制规则来进行初始化。
@EnableAutoConfiguration主要是@Import(EnableAutoConfigurationImportSelector.class),引入EnableAutoConfigurationImportSelector.class,来看下其中的关键函数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);
}
}
从该函数可以看出,先从META-INF/spring-autoconfigure-metadata.properties获取元数据与元数据之间的相关属性,接着调用getCandidateConfigurations()函数
/**
* 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函数将相关数据进行去重、过滤、排除,最后得到需要装配的类。
3、invokeBeanFactoryPostProcessors
前面讲了怎么获取需要装配的类,那么接下来探讨下具体怎么装配。
从抽象类AbstractApplicationContext的refresh()方法下手,refresh()方法中的**invokeBeanFactoryPostProcessors(beanFactory)**就是关键。
可以看出该方法是触发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接口的其中一种实现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的类中构建和校验出配置的模型model,其中解析@Configuration关键类是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()方法最后会调用前面聊到的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()中有句关键代码
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内部类中有DeferredImportSelector 的引用,到这里也就完成了整个自动装配的所有操作。
小结
- 自动装配归根到底,是使用SpringFactoriesLoader来加载所有被@EnableAutoConfiguration修饰的类中的配置,通过selectImports函数将相关数据去重、过滤、排除,最终确定需要装配的类;
- 当AbstractApplicationContext执行refresh()方法时,其中的invokeBeanFactoryPostProcessors方法会触发BeanFactoryPostProcessors去执行自动装配。
比如处理@Configuration注解的ConfigurationClassPostProcessor类,其本身是BeanFactoryPostProcessors接口的具体实现,通过ConfigurationClassParser.parse()调用processDeferredImportSelectors(),执行DeferredImportSelector,来完成自动装配。
Spring Boot自动装配源码分析就到这里了,建议读者选择其中关键源码阅读,再加上调试源码加深理解。后面可能会不定期更新,有兴趣的朋友可以在评论区一起讨论研究。
最后有件很重要的事,那就是麻烦点赞关注赞赏,谢谢(๑•̀ㅂ•́)و✧