大家在面试中问到springboot如何实现的自动配置
诶,不要傻背错八股了,以往 入口类上的@SpringBootApplication注解引入@EnableAutoConfiguration,再 @Import({AutoConfigurationImportSelector.class}) ,其中代码实现根据META-INF/spring.factories文件读取需要自动配置的类了
从springboot 2.7之后的版本中,改为了META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports,老的方式还能兼容,而 Springboot3之后完全放弃之前的方式,因此升级springboot版本后,引入的任何三方依赖,如nacos,dubbo,mybatis-plus等等依赖则需要升级相应的版本,否则无法实现自动配置并报错
如图如示
@SpringBootApplication
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration //引入EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
...
}
@EnableAutoConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class}) //自动配置的引入选择器实现
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
查看AutoConfigurationImportSelector 的getAutoConfigurationEntry方法
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
private static final AutoConfigurationEntry EMPTY_ENTRY = new AutoConfigurationEntry();
private static final String[] NO_IMPORTS = new String[0];
private static final Log logger = LogFactory.getLog(AutoConfigurationImportSelector.class);
private static final String PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE = "spring.autoconfigure.exclude";
private ConfigurableListableBeanFactory beanFactory;
private Environment environment;
private ClassLoader beanClassLoader;
private ResourceLoader resourceLoader;
private ConfigurationClassFilter configurationClassFilter;
public AutoConfigurationImportSelector() {
}
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
...省略
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); //这行查找配置候选列表
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
...省略
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates(); //这里是实现,注意这里第一个参数是AutoConfiguration类信息
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;
}
可以发现ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates()是最终实现,进去看源码
public final class ImportCandidates implements Iterable<String> {
private static final String LOCATION = "META-INF/spring/%s.imports";
private static final String COMMENT_START = "#";
private final List<String> candidates;
...省略
//classLoader转入的是org.springframework.boot.autoconfigure.AutoConfiguration
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
String location = String.format("META-INF/spring/%s.imports", annotation.getName()); //明显看到,查找地址已经发生了变化 ,最终拼接为META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
List<String> importCandidates = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}
从以上源码可以发现,在springboot3之后,已完全放弃是读META-INF/spring.factories的读取。而是从传的AutoConfiguration.class取的类名称。即org.springframework.boot.autoconfigure.AutoConfiguration。拼接之后 ,META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
nacos的自动配置为例