在SpringBoot3中spring.factories配置的自动装配不生效
- 在Spring Boot 2.7还是可以兼容使用spring.factories
- 到了SpringBoot3 spring.factories就不兼容使用了。
spring.factories其实是SpringBoot提供的SPI机制,底层实现是基于SpringFactoriesLoader检索ClassLoader中所有jar(包括ClassPath下的所有模块)引入的META-INF/spring.factories文件,基于文件中的接口(或者注解)加载对应的实现类并且注册到IOC容器。这种方式对于@ComponentScan不能扫描到的并且想自动注册到IOC容器的使用场景十分合适,基本上绝大多数第三方组件甚至部分spring-projects中编写的组件都是使用这种方案。
原文链接:blog.csdn.net/BASK2311/ar…
如何扫描到的呢 springboot默认只能扫描到自己主程序所在的包及其子包,扫描不到springboot-autoconfigure包中官方提供好的配置类
@EnableAutoConfiguration
springboot开启自动配置的核心注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
/**
* Environment property that can be used to override when auto-configuration is
* enabled.
*/
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@Import(AutoConfigurationImportSelector.class) 批量给容器导入组件
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 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;
private ImportCandidates(List<String> candidates) {
Assert.notNull(candidates, "'candidates' must not be null");
this.candidates = Collections.unmodifiableList(candidates);
}
@Override
public Iterator<String> iterator() {
return this.candidates.iterator();
}
/**
* Returns the list of loaded import candidates.
* @return the list of import candidates
*/
public List<String> getCandidates() {
return this.candidates;
}
/**
* Loads the names of import candidates from the classpath.
*
* The names of the import candidates are stored in files named
* {@code META-INF/spring/full-qualified-annotation-name.imports} on the classpath.
* Every line contains the full qualified name of the candidate class. Comments are
* supported using the # character.
* @param annotation annotation to load
* @param classLoader class loader to use for loading
* @return list of names of annotated classes
*/
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
String location = String.format(LOCATION, annotation.getName());
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);
}
private static ClassLoader decideClassloader(ClassLoader classLoader) {
if (classLoader == null) {
return ImportCandidates.class.getClassLoader();
}
return classLoader;
}
private static Enumeration<URL> findUrlsInClasspath(ClassLoader classLoader, String location) {
try {
return classLoader.getResources(location);
}
catch (IOException ex) {
throw new IllegalArgumentException("Failed to load configurations from location [" + location + "]", ex);
}
}
private static List<String> readCandidateConfigurations(URL url) {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) {
List<String> candidates = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
line = stripComment(line);
line = line.trim();
if (line.isEmpty()) {
continue;
}
candidates.add(line);
}
return candidates;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load configurations from location [" + url + "]", ex);
}
}
private static String stripComment(String line) {
int commentStart = line.indexOf(COMMENT_START);
if (commentStart == -1) {
return line;
}
return line.substring(0, commentStart);
}
}
总结 通过@Import(AutoConfigurationImportSelector.class) 将META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中定义的xxxAutoConfiguration类导入容器中
springboot3 由于 Java EE 已经变更为 Jakarta EE,包名以 javax开头的需要相应地变更为jakarta