mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-07-05 00:56:58 +08:00
Rename AutoConfigurationLoader to ImportCandidates
Move the class to a more suitable package, and load the files from META-INF/spring/<fqn>.imports See gh-29872
This commit is contained in:
parent
14c9147621
commit
d7b229d3c7
@ -66,7 +66,7 @@ public class AutoConfigurationMetadata extends DefaultTask {
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("spring.factories");
|
||||
getInputs()
|
||||
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration"))
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"))
|
||||
.withPathSensitivity(PathSensitivity.RELATIVE)
|
||||
.withPropertyName("org.springframework.boot.autoconfigure.AutoConfiguration");
|
||||
|
||||
@ -137,17 +137,17 @@ public class AutoConfigurationMetadata extends DefaultTask {
|
||||
|
||||
/**
|
||||
* Reads auto-configurations from
|
||||
* META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration.
|
||||
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
|
||||
* @return auto-configurations
|
||||
*/
|
||||
private List<String> readAutoConfigurationsFile() throws IOException {
|
||||
File file = new File(this.sourceSet.getOutput().getResourcesDir(),
|
||||
"META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration");
|
||||
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports");
|
||||
if (!file.exists()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// Nearly identical copy of
|
||||
// org.springframework.boot.autoconfigure.AutoConfigurationLoader.readAutoConfigurations
|
||||
// org.springframework.boot.context.annotation.ImportCandidates.load
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
|
||||
List<String> autoConfigurations = new ArrayList<>();
|
||||
String line;
|
||||
|
@ -106,7 +106,7 @@ public class TestSliceMetadata extends DefaultTask {
|
||||
Properties springFactories = readSpringFactories(
|
||||
new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||
readTestSlicesDirectory(springFactories,
|
||||
new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring-boot/"));
|
||||
new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring/"));
|
||||
for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) {
|
||||
addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
|
||||
}
|
||||
@ -123,14 +123,17 @@ public class TestSliceMetadata extends DefaultTask {
|
||||
* @param directory directory to scan
|
||||
*/
|
||||
private void readTestSlicesDirectory(Properties springFactories, File directory) {
|
||||
File[] files = directory.listFiles();
|
||||
File[] files = directory.listFiles((dir, name) -> name.endsWith(".imports"));
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
try {
|
||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||
springFactories.setProperty(file.getName(), StringUtils.collectionToCommaDelimitedString(lines));
|
||||
String fileNameWithoutExtension = file.getName().substring(0,
|
||||
file.getName().length() - ".imports".length());
|
||||
springFactories.setProperty(fileNameWithoutExtension,
|
||||
StringUtils.collectionToCommaDelimitedString(lines));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||
|
@ -24,6 +24,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
@ -34,9 +35,8 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
* {@link Configuration @Configuration} with the exception that
|
||||
* {@literal Configuration#proxyBeanMethods() proxyBeanMethods} is always {@code false}.
|
||||
* <p>
|
||||
* They are located using the {@link AutoConfigurationLoader} and the
|
||||
* {@link SpringFactoriesLoader} mechanism (keyed against
|
||||
* {@link EnableAutoConfiguration}).
|
||||
* They are located using {@link ImportCandidates} and the {@link SpringFactoriesLoader}
|
||||
* mechanism (keyed against {@link EnableAutoConfiguration}).
|
||||
* <p>
|
||||
* Generally auto-configuration classes are marked as {@link Conditional @Conditional}
|
||||
* (most often using {@link ConditionalOnClass @ConditionalOnClass} and
|
||||
|
@ -21,6 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
@ -63,11 +64,9 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad
|
||||
|
||||
protected List<String> getAutoConfigurations() {
|
||||
if (this.autoConfigurations == null) {
|
||||
List<String> autoConfigurations = new ArrayList<>();
|
||||
autoConfigurations.addAll(
|
||||
List<String> autoConfigurations = new ArrayList<>(
|
||||
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader));
|
||||
autoConfigurations
|
||||
.addAll(new AutoConfigurationLoader().loadNames(AutoConfiguration.class, this.beanClassLoader));
|
||||
ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader).forEach(autoConfigurations::add);
|
||||
this.autoConfigurations = autoConfigurations;
|
||||
}
|
||||
return this.autoConfigurations;
|
||||
|
@ -40,6 +40,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
@ -168,7 +169,7 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
|
||||
/**
|
||||
* Return the auto-configuration class names that should be considered. By default
|
||||
* this method will load candidates using {@link AutoConfigurationLoader} with
|
||||
* this method will load candidates using {@link ImportCandidates} with
|
||||
* {@link #getSpringFactoriesLoaderFactoryClass()}. For backward compatible reasons it
|
||||
* will also consider {@link SpringFactoriesLoader} with
|
||||
* {@link #getSpringFactoriesLoaderFactoryClass()}.
|
||||
@ -178,12 +179,11 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
|
||||
* @return a list of candidate configurations
|
||||
*/
|
||||
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
|
||||
List<String> configurations = new ArrayList<>();
|
||||
configurations.addAll(
|
||||
List<String> configurations = new ArrayList<>(
|
||||
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
|
||||
configurations.addAll(new AutoConfigurationLoader().loadNames(AutoConfiguration.class, getBeanClassLoader()));
|
||||
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
|
||||
Assert.notEmpty(configurations,
|
||||
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration. If you "
|
||||
"No auto configuration classes found in META-INF/spring.factories nor 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;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import java.lang.annotation.Target;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
@ -60,7 +61,7 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
* and classes can be searched.
|
||||
* <p>
|
||||
* Auto-configuration classes are regular Spring {@link Configuration @Configuration}
|
||||
* beans. They are located using the {@link AutoConfigurationLoader} and the
|
||||
* beans. They are located using {@link ImportCandidates} and the
|
||||
* {@link SpringFactoriesLoader} mechanism (keyed against this class). Generally
|
||||
* auto-configuration beans are {@link Conditional @Conditional} beans (most often using
|
||||
* {@link ConditionalOnClass @ConditionalOnClass} and
|
||||
|
@ -59,8 +59,8 @@ public @interface ImportAutoConfiguration {
|
||||
|
||||
/**
|
||||
* The auto-configuration classes that should be imported. When empty, the classes are
|
||||
* specified using a file in {@code META-INF/spring-boot} where the file name is the
|
||||
* fully-qualified name of the annotated class.
|
||||
* specified using a file in {@code META-INF/spring} where the file name is the
|
||||
* fully-qualified name of the annotated class, suffixed with '.imports'.
|
||||
* @return the classes to import
|
||||
*/
|
||||
@AliasFor("value")
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -28,6 +28,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.context.annotation.DeterminableImports;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
@ -95,9 +96,9 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec
|
||||
}
|
||||
|
||||
protected Collection<String> loadFactoryNames(Class<?> source) {
|
||||
List<String> factoryNames = new ArrayList<>();
|
||||
factoryNames.addAll(SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader()));
|
||||
factoryNames.addAll(new AutoConfigurationLoader().loadNames(source, getBeanClassLoader()));
|
||||
List<String> factoryNames = new ArrayList<>(
|
||||
SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader()));
|
||||
ImportCandidates.load(source, getBeanClassLoader()).forEach(factoryNames::add);
|
||||
return factoryNames;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
|
||||
import org.springframework.boot.context.annotation.ImportCandidates;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@ -212,7 +213,10 @@ class AutoConfigurationImportSelectorTests {
|
||||
}
|
||||
|
||||
private List<String> getAutoConfigurationClassNames() {
|
||||
return new AutoConfigurationLoader().loadNames(AutoConfiguration.class, getClass().getClassLoader());
|
||||
List<String> autoConfigurationClassNames = new ArrayList<>();
|
||||
ImportCandidates.load(AutoConfiguration.class, getClass().getClassLoader())
|
||||
.forEach(autoConfigurationClassNames::add);
|
||||
return autoConfigurationClassNames;
|
||||
}
|
||||
|
||||
private class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
|
||||
|
@ -18,14 +18,14 @@ Additional `@Conditional` annotations are used to constrain when the auto-config
|
||||
Usually, auto-configuration classes use `@ConditionalOnClass` and `@ConditionalOnMissingBean` annotations.
|
||||
This ensures that auto-configuration applies only when relevant classes are found and when you have not declared your own `@Configuration`.
|
||||
|
||||
You can browse the source code of {spring-boot-autoconfigure-module-code}[`spring-boot-autoconfigure`] to see the `@Configuration` classes that Spring provides (see the {spring-boot-code}/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration[`META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration`] file).
|
||||
You can browse the source code of {spring-boot-autoconfigure-module-code}[`spring-boot-autoconfigure`] to see the `@Configuration` classes that Spring provides (see the {spring-boot-code}/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports[`META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`] file).
|
||||
|
||||
|
||||
|
||||
[[features.developing-auto-configuration.locating-auto-configuration-candidates]]
|
||||
=== Locating Auto-configuration Candidates
|
||||
|
||||
Spring Boot checks for the presence of a `META-INF/spring-boot/org.springframework.boot.autoconfigure.AutoConfiguration` file within your published jar.
|
||||
Spring Boot checks for the presence of a `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` file within your published jar.
|
||||
The file should list your configuration classes, as shown in the following example:
|
||||
|
||||
[indent=0]
|
||||
|
@ -769,9 +769,9 @@ include::code:MyJdbcTests[]
|
||||
|
||||
NOTE: Make sure to not use the regular `@Import` annotation to import auto-configurations as they are handled in a specific way by Spring Boot.
|
||||
|
||||
Alternatively, additional auto-configurations can be added for any use of a slice annotation by registering them in a file stored in `META-INF/spring-boot` as shown in the following example:
|
||||
Alternatively, additional auto-configurations can be added for any use of a slice annotation by registering them in a file stored in `META-INF/spring` as shown in the following example:
|
||||
|
||||
.META-INF/spring-boot/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest
|
||||
.META-INF/spring/org.springframework.boot.test.autoconfigure.jdbc.JdbcTest.imports
|
||||
[indent=0]
|
||||
----
|
||||
com.example.IntegrationAutoConfiguration
|
||||
|
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure;
|
||||
package org.springframework.boot.context.annotation;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
@ -22,57 +22,76 @@ import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Loads the names of annotated classes, usually @{@link AutoConfiguration}.
|
||||
* Contains import candidates, usually auto-configurations.
|
||||
*
|
||||
* The names of the classes are stored in files named META-INF/spring-boot/{full qualified
|
||||
* name of the annotation}. Every line contains the full qualified class name of the
|
||||
* annotated class. Comments are supported using the # character.
|
||||
* The {@link #load(Class, ClassLoader)} method can be used to discover the import
|
||||
* candidates.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
* @see AutoConfiguration
|
||||
* @see SpringFactoriesLoader
|
||||
* @since 2.7.0
|
||||
*/
|
||||
class AutoConfigurationLoader {
|
||||
public final class ImportCandidates implements Iterable<String> {
|
||||
|
||||
private static final String LOCATION = "META-INF/spring-boot/";
|
||||
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);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<String> iterator() {
|
||||
return this.candidates.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the names of annotated classes.
|
||||
* 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.import} 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
|
||||
*/
|
||||
List<String> loadNames(Class<?> annotation, ClassLoader classLoader) {
|
||||
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
|
||||
Assert.notNull(annotation, "'annotation' must not be null");
|
||||
ClassLoader classLoaderToUse = decideClassloader(classLoader);
|
||||
String location = LOCATION + annotation.getName();
|
||||
String location = String.format(LOCATION, annotation.getName());
|
||||
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
|
||||
List<String> autoConfigurations = new ArrayList<>();
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
autoConfigurations.addAll(readAutoConfigurations(url));
|
||||
}
|
||||
return autoConfigurations;
|
||||
return new ImportCandidates(autoConfigurations);
|
||||
}
|
||||
|
||||
private ClassLoader decideClassloader(ClassLoader classLoader) {
|
||||
private static ClassLoader decideClassloader(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
return AutoConfigurationLoader.class.getClassLoader();
|
||||
return ImportCandidates.class.getClassLoader();
|
||||
}
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
private Enumeration<URL> findUrlsInClasspath(ClassLoader classLoader, String location) {
|
||||
private static Enumeration<URL> findUrlsInClasspath(ClassLoader classLoader, String location) {
|
||||
try {
|
||||
return classLoader.getResources(location);
|
||||
}
|
||||
@ -82,7 +101,7 @@ class AutoConfigurationLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> readAutoConfigurations(URL url) {
|
||||
private static List<String> readAutoConfigurations(URL url) {
|
||||
try (BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) {
|
||||
List<String> autoConfigurations = new ArrayList<>();
|
||||
@ -102,7 +121,7 @@ class AutoConfigurationLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private String stripComment(String line) {
|
||||
private static String stripComment(String line) {
|
||||
int commentStart = line.indexOf(COMMENT_START);
|
||||
if (commentStart == -1) {
|
||||
return line;
|
@ -3,4 +3,4 @@ class1
|
||||
|
||||
class2 # with comment at the end
|
||||
# Comment with some whitespace in front
|
||||
class3
|
||||
class3
|
@ -14,39 +14,28 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure;
|
||||
package org.springframework.boot.context.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class AutoConfigurationLoaderTests {
|
||||
|
||||
private AutoConfigurationLoader sut;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.sut = new AutoConfigurationLoader();
|
||||
}
|
||||
class ImportCandidatesTest {
|
||||
|
||||
@Test
|
||||
void loadNames() {
|
||||
List<String> classNames = this.sut.loadNames(TestAutoConfiguration.class, null);
|
||||
|
||||
assertThat(classNames).containsExactly("class1", "class2", "class3");
|
||||
void loadReadsFromClasspathFile() {
|
||||
ImportCandidates candidates = ImportCandidates.load(TestAnnotation.class, null);
|
||||
assertThat(candidates).containsExactly("class1", "class2", "class3");
|
||||
}
|
||||
|
||||
@AutoConfiguration
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface TestAutoConfiguration {
|
||||
public @interface TestAnnotation {
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user