1、准备工作
启动一个简单的springboot应用
@SpringBootApplication
@RestController
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class);
}
@GetMapping("/a")
public String a(){
return "111";
}
}
2、分析
@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
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
SpringBootApplication组合注解,主要有SpringBootConfiguration、ComponentScan和EnableAutoConfiguration三个注解组成,ComponentScan我们都知道是往容器中添加指定的对象,SpringBootConfiguratio注解主要是标识的类可作为一个配置类,进一步分析EnableAutoConfiguration
进一步分析EnableAutoConfiguration又是由@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)组合,@Import注解中的AutoConfigurationImportSelector类实现了DeferredImportSelector接口,Spring会将ImportSelector接口中selectImports方法返回的迭代器遍历添加到spring容器中
AutoConfigurationImportSelector类
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
// 获取自动装配的类
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
//将自动装配的类添加到autoConfigurationEntries属性
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取@EnableAutoConfiguration的属性exclude,excludeName 自动装配要排除的类
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取所有需要自动装配的类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取@EnableAutoConfiguration属性要排除的类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查要排除的类,如果自动装配的类中没有要排除的类会抛异常
checkExcludedClasses(configurations, exclusions);
// 从自动装配的类中移除要排除的类
configurations.removeAll(exclusions);
// 对自动装配的类进行过滤,需要实现AutoConfigurationImportFilter接口,并且把该过滤类写进spring.factories文件中
configurations = getConfigurationClassFilter().filter(configurations);
// 发布自动装配事件,需要实现AutoConfigurationImportListener接口,并且把该监听类写进spring.factories文件中
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
// 进入getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 获取要自动装配的类
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
// 如果为空,抛出异常
Assert.notEmpty(configurations,
"No auto configuration classes found in "
+ "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
// 获取类加载器
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// LOCATION = "META-INF/spring/%s.imports" 拿AutoConfiguration注解全路径名进行拼接,拼接之后location=META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
String location = String.format(LOCATION, annotation.getName());
// 找location下资源
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 读取每一行的内容作为要自动装配的类
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
与springboot2的自动装配不同的是,springboot3的自动装配读取的要自动装配的类的文件是META-
INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,自动装配过程中也有使用spring.factories文件,获取一些自动装配过程中过滤、事件监听器等信息(本人没读过springboot2的自动装配源码,从网上了解到spirngboot2的自动装配的类是在spring.factories文件中)
那么,springboot在启动过程中是如何走到这一步的呢
1、SpringApplication.run方法
public ConfigurableApplicationContext run(String... args) {
if (this.registerShutdownHook) {
SpringApplication.shutdownHook.enableShutdowHookAddition();
}
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布事件 ApplicationStartingEvent 应用开启
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 输出banner
Banner printedBanner = printBanner(environment);
// 创建spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 启动spring容器(tomcat启动),就是AbstractApplicationContext的refresh方法了
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
if (context.isRunning()) {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
}
catch (Throwable ex) {
if (ex instanceof AbandonedRunException) {
throw ex;
}
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
2、ConfigurationClassPostProcessor
ConfigurationClassPostProcessor类实现了BeanDefinitionRegistryPostProcessor接口,可以在spring容器中添加bean定义
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
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);
}
3、processConfigBeanDefinitions方法片段
// 创建了ConfigurationClassParser对象parser
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 {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 将自动装配的类封装为parser的属性
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
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());
}
// 在spring容器中注册parser.getConfigurationClasses()方法返回的bean定义
this.reader.loadBeanDefinitions(configClasses);
至此,已发现自动装配的类被封装在ConfigurationClassParser的configurationClasses属性中,继续寻找该属性在何时被赋值
4、ConfigurationClassParser
parser.parse(candidates)方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
parse(abstractBeanDef.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);
}
}
this.deferredImportSelectorHandler.process();
}
DeferredImportSelectorGroupingHandler类processGroupImports方法
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
// 返回DeferredImportSelector接口getImports返回的迭代器
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
// 此处调用的就是AutoConfigurationImportSelector类的process方法
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
@Override
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
// 要排除的类
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions)
.flatMap(Collection::stream)
.collect(Collectors.toSet());
// autoConfigurationEntries属性
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations)
.flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.toList();
}
// 遍历所有要自动装配的类,执行该方法
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
}
if (checkForCircularImports && isChainedImportOnStack(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
}
else {
this.importStack.push(configClass);
try {
for (SourceClass candidate : importCandidates) {
// 是否实现了ImportSelector接口
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
// 反射创建该实例
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
Predicate<String> selectorFilter = selector.getExclusionFilter();
if (selectorFilter != null) {
exclusionFilter = exclusionFilter.or(selectorFilter);
}
// 是否是DeferredImportSelector
if (selector instanceof DeferredImportSelector deferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
}
else {
// 调用selectImports方法,获取到导入的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
// 循环调用processImports本方法
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
// Candidate class is an ImportBeanDefinitionRegistrar ->
// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
this.environment, this.resourceLoader, this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
this.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
processConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configClass.getMetadata().getClassName() + "]: " + ex.getMessage(), ex);
}
finally {
this.importStack.pop();
}
}
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
// 是否不满足自动装配条件 主要判断@Conditional
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
// configurationClasses属性中添加要自动装配的类
this.configurationClasses.put(configClass, configClass);
}
springboot自动装配先使用@Import注解导入的AutoConfigurationImportSelector类,利用AutoConfigurationImportSelector实现了DeferredImportSelector接口,进而要将自动装配的类bean定义注册到spring容器中
最后,为什么springBoot3的自动装配的类文件不再使用spirng.factories,使用META-INF/spring/%s.imports替代?下面是某AI工具的回复,也挺有道理,欢迎大家讨论指正
Spring Boot 3 放弃使用
spring.factories而使用org.springframework.boot.autoconfigure.AutoConfiguration.imports进行自动装配的类文件,主要有以下几个原因:
- 简化配置:使用
spring.factories需要维护一个中央的静态工厂文件,这在大型项目中可能会变得非常复杂。每个需要配置的类都需要在工厂文件中进行显式声明,这无疑增加了大量的维护成本。而使用AutoConfiguration.imports则可以将配置分散到各个独立的类中,使得项目结构更加清晰,也更容易进行版本控制。- 提高可读性和可维护性:使用 Java 注解(如
@Configuration和@Bean)来定义Bean,相比在spring.factories文件中使用静态工厂方法,使得代码更加直观和易于理解。这也使得开发者能够更清晰地看到哪些类被配置为Bean,以及它们是如何被配置的。- 错误检查和一致性:新的自动配置机制能够在启动时检查配置的一致性,例如检查是否存在两个相同类型的
Bean。这种检查机制在spring.factories中是不存在的。- 更好的支持 Java 配置:Spring Boot 的新机制更好地支持使用 Java 配置(而不是 XML 或注解)。这种配置方式使得开发者能够更清晰地看到哪些类被配置为
Bean,以及它们是如何被配置的。总的来说,Spring Boot 3 放弃使用
spring.factories而使用org.springframework.boot.autoconfigure.AutoConfiguration.imports是为了提高项目的可维护性、可读性和一致性,以及更好地支持 Java 配置。