Spring Boot自动装配是Spring Boot框架的一个关键特性,它的目标是让开发者能够快速构建Spring应用程序,减少繁琐的配置工作。
从启动类@SpringApplication注解入手,@SpringBootApplication是一个组合注解,它是Spring Boot框架中常用的一个主要注解之一。它结合了多个注解,简化了Spring Boot应用程序的配置。
java复制代码@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
@SpringBootApplication 包含以下三个核心注解:
java复制代码@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration //标识为配置类
@EnableAutoConfiguration //开启自动配置
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...省略
}
@EnableAutoConfiguration 是自动装配的核心,下面我们对该注解展开分析。
java复制代码@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
...省略
}
在@EnableAutoConfiguration的源码中,可以看到它使用@Import注解引入了AutoConfigurationImportSelector类。当Spring bean后置处理器扫描解析启动类下所有包创建beanDefinition时,AutoConfigurationImportSelector会触发自动装配的加载过程。
@Import是一个用于导入其他配置类的扩展点。通过使用@Import注解,我们可以在一个配置类中引入其他的配置类,从而将它们的Bean定义和配置合并到当前配置中。
@Import导入配置类分为两种:
通过上述可知AutoConfigurationImportSelector 实现ImportSelector接口。该自动装配核心类逻辑在接下的分析中展开说
java复制代码@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
//第一步
SpringApplication.run(MyApp.class, args);
}
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//第二步
return new SpringApplication(primarySources).run(args);
}
//第三步
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//资源加载器
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置启动引导类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//web应用类型:
// 1jnty有限公司. NONE 非web应用程序
// 2. SERVLET web应用程序
// 3. REACTIVE 响应式web引用程序
//通过web应用类路径选择web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//Spring SPI机制获取key为 ApplicationContextInitializer的类,并将它们作为应用程序上下文初始化器(ApplicationContextInitializer)注册
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//Spring SPI机制获取key为 ApplicationListener,并将它们作为应用程序事件监听器(ApplicationListener)注册
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
这块关注第一次调用Spring SPI机制的setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
java复制代码private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//Spring SPI机制加载 META-INF/spring.factories 文件以key,value形式的自动装配类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通过 SpringFactoriesLoader.loadFactoryNames(type, classLoader) 方法,从 META-INF/spring.factories 文件中加载指定接口类型的实现类的名称集合,并将它们保存在一个 Set 中。
一些八股文认为在AutoConfigurationImportSelector使用Spring SPI加载META-INF/spring.factories获取自动装配类 ,其实不然,Spring SPI 只加载一次META-INF/spring.factories,以key-value的形式进行本地缓存,后续触发自动装配时,从本地缓存中获取自动装配类集合
java复制代码public ConfigurableApplicationContext run(String... args) {
.
.省略
.
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//初始化并配置应用程序的环境,包括加载配置文件、解析命令行参数、设置系统属性等。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印Banner
Banner printedBanner = printBanner(environment);
//根据web类型创建ApplicationContext
context = createApplicationContext();
//SpringBootExceptionReporter用于报告Spring Boot应用程序启动过程中的异常
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备ApplicationContext,包括设置环境、配置监听器、初始化Bean等操作。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//这里会完成整个Spring IoC容器的初始化,包括解析Bean的定义,创建Bean实例,以及处理依赖注入等。
refreshContext(context);
.
.省略
.
return context;
}
上述代码中,除了createApplicationContext()、refreshContext(context),其他部分与自动装配无关。在后续的文章中,会进一步介绍这部分内容。
createApplicationContext()做了什么?
java复制代码protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
//根据webApplicationType枚举类型,创建应用
switch (this.webApplicationType) {
case SERVLET: //SERVLET web应用程序
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE: //REACTIVE 响应式web引用程序
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default: //NONE 非web应用程序
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
public AnnotationConfigServletWebServerApplicationContext() {
//用于读取基于注解的Bean定义的类
this.reader = new AnnotatedBeanDefinitionReader(this);
//用于扫描类路径中的组件,并将其注册为Spring管理的Bean定义。
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
//注册BeanFactory后置处理器
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
//注册BeanFactory后置处理器
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
.
.省略
.
Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
if (JNTY!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//自动装配重点:创建BeanFactory后置处理器 ConfigurationClassPostProcessor,用于扫描解析启动类当前包以及子包下的配置类
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
//注册 ConfigurationClassPostProcessor
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}
// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
//创建BeanFactory后置处理器 CommonAnnotationBeanPostProcessor,处理依赖注入@Resource、WebServiceRef标注的字段以及方法
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
//注册CommonAnnotationBeanPostProcessor
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
.
.省略
.
return beanDefs;
}
上述这段代码整体来说非常简单,根据 web应用类型创建ApplicationContext, 例如web应用类型为 SERVLET,创建AnnotationConfigServletWebServerApplicationContext。通过AnnotationConfigServletWebServerApplicationContext构造方法创建 AnnotatedBeanDefinitionReader以及ClassPathBeanDefinitionScanner,我们需要注意的是AnnotatedBeanDefinitionReader涉及BeanFactory后置处理器注册的内容,可以看到这一步注册了ConfigurationClassPostProcessor
refreshContext(context)做了什么?JNTY体育
java复制代码@Override
public void refresh() throws BeansException, IllegalStateException {
...省略
/**
* 共注册了2个后置处理器
* ApplicationContextAwareProcessor,用于执行xxxAware接口中的方法
* ApplicationListenerDetector,保证监听器被添加到容器中
*/
prepareBeanFactory(beanFactory);
try {
//为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
//触发BeanFactory后置处理器
//定位注册加载BeanDefinition, 能让我们对容器中扫描出来的BeanDefinition做出修改以达到扩展的目的
invokeBeanFactoryPostProcessors(beanFactory);
//说白了就是beanFactory.addBeanPostProcessor(postProcessor);添加bean后置处理器
registerBeanPostProcessors(beanFactory);
////初始化容器事件传播器,添加事件传播器到IOC容器
initApplicationEventMulticaster();
//启动容器
onRefresh();
//为事件传播器注册事件监听器.
registerListeners();
//将解析的BeanDefinition注册到IOC容器中
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事
finishRefresh();
}
...省略
}
在refreshContext方法中,完成了整个Spring IoC容器的初始化,包括解析Bean的定义、创建Bean实例以及处理依赖注入等。我们主要关注invokeBeanFactoryPostProcessors(beanFactory)对配置类进行定位解析注册BeanDefinition
invokeBeanFactoryPostProcessors(beanFactory)做了什么?
java复制代码
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//遍历内部所有BeanDefinitionRegistry后置处理器 (继承 BeanFactoryPostProcessor),
//调用 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 解析配置类成benaDefinition并注册
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
//第二步:
在invokeBeanFactoryPostProcessors方法中,会触发所有BeanFactory后置处理器的执行,其中ConfigurationClassPostProcessor后置处理器负责扫描解析启动类当前包以及子包下的配置类,并将其注册为Spring管理的BeanDefinition,接下来我们就看看它的源码
ConfigurationClassPostProcessor源码分析
java复制代码 //调用 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
//获取传入的 BeanDefinitionRegistry 对象 registry 的哈希码,作为标识
int registryId = System.identityHashCode(registry);
//通过检查 registriesPostProcessed 集合和 factoriesPostProcessed 集合,防止了在同一个 BeanDefinitionRegistry 上重复执行逻辑
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);
}
//处理一些自定义的BeanDefinition
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
//存放加了注解@Configuration @Component @ComponentScan @Import @ImportResource的类
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//判断是否已经处理
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
} else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
//存放加了注解@Configuration @Component @ComponentScan @Import @ImportResource的类
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
...省略
// 创建配置类解析器,构造方法会创建ComponentScanAnnotationParser扫描器
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
//1.解析
parser.parse(candidates);
//验证
parser.validate();
// 已经解析过的配置类
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
// 移除已经解析过的配置类,防止重复加载了配置类中的bd
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// 2. 将所有ConfigurationClass解析成BeanDefinition进行 @Condition... 匹配,然后注册到beanDefinition容器中
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 如果大于,说明容器中新增了一些BeanDefinition,那么需要重新判断新增的bd是否是配置类,如果是配置类,需要再次解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
// 注册ImportRegistry到容器中
// 当通过@Import注解导入一个全配置类A(被@Configuration注解修饰的类),A可以实现ImportAware接口
// 通过这个Aware可以感知到是哪个类导入的
if (sbr != null) {
if (!sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
//解析
public void parse(Set<BeanDefinitionHolder> configCandidates) {
this.deferredImportSelectors = new LinkedList<>();
// 在 new SpringApplication时候,会将 启动类 生成为AnnotatedBeanDefinition,然后注入到beanDefinition容器中
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 (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
//处理启动类的自动装配注解上@Import,对于 实现ImportSelector接口的执行selectImport
processDeferredImportSelectors();
}
private void processDeferredImportSelectors() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
if (deferredImports == null) {
return;
}
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);
for (DeferredImportSelectorHolder deferredImport : deferredImports) {
ConfigurationClass configClass = deferredImport.getConfigurationClass();
try {
// 获取启动类上的所有@Import的实现ImportSelector类,执行selectImports
// 自动装配就在此处触发,获取类处境
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
// 处理selectImports返回的可导入自动装配类
//1. 实现imporselect接口,则创建为实例调用selectImprot方法,然后递归调用处理import
//2. 实现importRegistry接口,则缓存到importBeanDefinitionRegistrars集合
//3. 非上述接口,则当做配置类处理。解析@PropertySources、@ComponentScans、@Import、@Bean
processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]", ex);
}
}
}
上述代码主要流程:
@bean 等价于 factory-method
@bean 所在的类 等价于 factory-bean
自动装配核心逻辑 - AutoConfigurationImportSelector
java复制代码public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//加载组件自动配置类元信息spring-autoconfigure-metadata.properties
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//获取key为EnableAutoConfiuration对应的value集合
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//自动装配类去重
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
//根据maven导入的启动器,过滤出需要导入的自动装配类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
上面这段代码主要做了四件事
通过本文前部分的注解分析已知AutoConfigurationImportSelector实现ImportSelector接口。当ConfigurationClassPostProcessor后置处理器处理@Import时,就会触发自动装配核心类AutoConfigurationImportSelector#selectImprot() 获取Spring SPI 本地缓存key为EnableAutoConfiuration的自动装配类集合,然后去重过滤返回需要导入的自动装配类数组。
简单的来说,Spring Boot的自动装配原理主要依赖于Spring SPI机制+@Import扩展点+ ConfigurationClassPostProcessor实现。ConfigurationClassPostProcessor后置处理器会在扫描解析启动类当前包以及子包下的配置类,并将其注册为Spring管理的BeanDefinition,从而实现自动装配。而AutoConfigurationImportSelector作为自动装配核心类,在处理@Import逻辑中触发自动装配的过程,最终获取需要导入的自动装配类集合,实现Spring Boot的自动装配功能。
电话:400-123-4567
传 真:+86-123-4567
手 机:13800000000
邮 箱:admin@qq.com
地 址:JNTY体育广东省广州市天河区88号