Remove support for locating imports using spring.factories

With this commit, loading `@AutoConfiguration`,
`@ImportAutoConfiguration`, and `@ManagementContextConfiguration`
classes is supported with `.imports` files only. Support for loading
these classes with `spring.factories` is removed.

Closes gh-29699
This commit is contained in:
Scott Frederick 2022-09-21 15:56:35 -05:00
parent 6b8575b001
commit 08022ba86e
9 changed files with 35 additions and 100 deletions

View File

@ -23,15 +23,13 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
import org.gradle.api.DefaultTask;
import org.gradle.api.Task;
@ -43,13 +41,13 @@ import org.gradle.api.tasks.TaskAction;
import org.springframework.asm.ClassReader;
import org.springframework.asm.Opcodes;
import org.springframework.core.CollectionFactory;
import org.springframework.util.StringUtils;
/**
* A {@link Task} for generating metadata describing a project's auto-configuration
* classes.
*
* @author Andy Wilkinson
* @author Scott Frederick
*/
public class AutoConfigurationMetadata extends DefaultTask {
@ -60,10 +58,6 @@ public class AutoConfigurationMetadata extends DefaultTask {
private File outputFile;
public AutoConfigurationMetadata() {
getInputs()
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
"META-INF/spring.factories"))
.withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("spring.factories");
getInputs()
.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(),
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports"))
@ -99,9 +93,7 @@ public class AutoConfigurationMetadata extends DefaultTask {
private Properties readAutoConfiguration() throws IOException {
Properties autoConfiguration = CollectionFactory.createSortedProperties(true);
Set<String> classNames = new LinkedHashSet<>();
classNames.addAll(readSpringFactories());
classNames.addAll(readAutoConfigurationsFile());
List<String> classNames = readAutoConfigurationsFile();
Set<String> publicClassNames = new LinkedHashSet<>();
for (String className : classNames) {
File classFile = findClassFile(className);
@ -120,21 +112,6 @@ public class AutoConfigurationMetadata extends DefaultTask {
return autoConfiguration;
}
/**
* Reads auto-configurations from META-INF/spring.factories.
* @return auto-configurations
*/
private Set<String> readSpringFactories() throws IOException {
File file = new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories");
if (!file.exists()) {
return Collections.emptySet();
}
Properties springFactories = readSpringFactories(file);
String enableAutoConfiguration = springFactories
.getProperty("org.springframework.boot.autoconfigure.EnableAutoConfiguration");
return StringUtils.commaDelimitedListToSet(enableAutoConfiguration);
}
/**
* Reads auto-configurations from
* META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
@ -146,29 +123,18 @@ public class AutoConfigurationMetadata extends DefaultTask {
if (!file.exists()) {
return Collections.emptyList();
}
// Nearly identical copy of
// org.springframework.boot.context.annotation.ImportCandidates.load
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) {
List<String> autoConfigurations = new ArrayList<>();
String line;
while ((line = reader.readLine()) != null) {
line = stripComment(line);
line = line.trim();
if (line.isEmpty()) {
continue;
}
autoConfigurations.add(line);
}
return autoConfigurations;
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
return reader.lines().map(this::stripComment).filter((line) -> !line.isEmpty())
.collect(Collectors.toList());
}
}
private String stripComment(String line) {
int commentStart = line.indexOf(COMMENT_START);
if (commentStart == -1) {
return line;
return line.trim();
}
return line.substring(0, commentStart);
return line.substring(0, commentStart).trim();
}
private File findClassFile(String className) {
@ -182,12 +148,4 @@ public class AutoConfigurationMetadata extends DefaultTask {
return null;
}
private Properties readSpringFactories(File file) throws IOException {
Properties springFactories = new Properties();
try (Reader in = new FileReader(file)) {
springFactories.load(in);
}
return springFactories;
}
}

View File

@ -29,7 +29,6 @@ import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
@ -44,6 +43,7 @@ import org.springframework.util.StringUtils;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Moritz Halbritter
* @author Scott Frederick
* @see ManagementContextConfiguration
* @see ImportCandidates
*/
@ -90,11 +90,7 @@ class ManagementContextConfigurationImportSelector implements DeferredImportSele
}
protected List<String> loadFactoryNames() {
@SuppressWarnings("deprecation")
List<String> factoryNames = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(ManagementContextConfiguration.class, this.classLoader));
ImportCandidates.load(ManagementContextConfiguration.class, this.classLoader).forEach(factoryNames::add);
return factoryNames;
return ImportCandidates.load(ManagementContextConfiguration.class, this.classLoader).getCandidates();
}
@Override

View File

@ -30,16 +30,13 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Indicates that a class provides configuration that can be automatically applied by
* Spring Boot. Auto-configuration classes are regular
* {@link Configuration @Configuration} with the exception that
* {@literal Configuration#proxyBeanMethods() proxyBeanMethods} is always {@code false}.
* <p>
* They are located using {@link ImportCandidates} and the {@link SpringFactoriesLoader}
* mechanism (keyed against {@link EnableAutoConfiguration}).
* They are located using {@link ImportCandidates}.
* <p>
* Generally auto-configuration classes are marked as {@link Conditional @Conditional}
* (most often using {@link ConditionalOnClass @ConditionalOnClass} and

View File

@ -17,13 +17,11 @@
package org.springframework.boot.autoconfigure;
import java.io.IOException;
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;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
@ -32,6 +30,7 @@ import org.springframework.core.type.filter.TypeFilter;
* A {@link TypeFilter} implementation that matches registered auto-configuration classes.
*
* @author Stephane Nicoll
* @author Scott Frederick
* @since 1.5.0
*/
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
@ -64,11 +63,8 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad
protected List<String> getAutoConfigurations() {
if (this.autoConfigurations == null) {
@SuppressWarnings("deprecation")
List<String> autoConfigurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader));
ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader).forEach(autoConfigurations::add);
this.autoConfigurations = autoConfigurations;
this.autoConfigurations = ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader)
.getCandidates();
}
return this.autoConfigurations;
}

View File

@ -69,6 +69,7 @@ import org.springframework.util.StringUtils;
* @author Stephane Nicoll
* @author Madhura Bhave
* @author Moritz Halbritter
* @author Scott Frederick
* @since 1.3.0
* @see EnableAutoConfiguration
*/
@ -168,36 +169,23 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector,
}
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link ImportCandidates} with
* {@link #getSpringFactoriesLoaderFactoryClass()}. For backward compatible reasons it
* will also consider {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* Return the auto-configuration class names that should be considered. By default,
* this method will load candidates using {@link ImportCandidates}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
@SuppressWarnings("deprecation")
List<String> configurations = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
"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;
}
/**
* Return the class used by {@link SpringFactoriesLoader} to load configuration
* candidates.
* @return the factory class
*/
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
for (String exclusion : exclusions) {

View File

@ -32,7 +32,6 @@ import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.SpringFactoriesLoader;
/**
* Enable auto-configuration of the Spring Application Context, attempting to guess and
@ -61,9 +60,8 @@ 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 {@link ImportCandidates} and the
* {@link SpringFactoriesLoader} mechanism (keyed against this class). Generally
* auto-configuration beans are {@link Conditional @Conditional} beans (most often using
* beans. They are located using {@link ImportCandidates}. Generally auto-configuration
* beans are {@link Conditional @Conditional} beans (most often using
* {@link ConditionalOnClass @ConditionalOnClass} and
* {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
*

View File

@ -32,7 +32,6 @@ 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;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap;
@ -46,6 +45,7 @@ import org.springframework.util.ObjectUtils;
* @author Phillip Webb
* @author Andy Wilkinson
* @author Moritz Halbritter
* @author Scott Frederick
*/
class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelector implements DeterminableImports {
@ -96,11 +96,7 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec
}
protected Collection<String> loadFactoryNames(Class<?> source) {
@SuppressWarnings("deprecation")
List<String> factoryNames = new ArrayList<>(
SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader()));
ImportCandidates.load(source, getBeanClassLoader()).forEach(factoryNames::add);
return factoryNames;
return ImportCandidates.load(source, getBeanClassLoader()).getCandidates();
}
@Override

View File

@ -213,10 +213,7 @@ class AutoConfigurationImportSelectorTests {
}
private List<String> getAutoConfigurationClassNames() {
List<String> autoConfigurationClassNames = new ArrayList<>();
ImportCandidates.load(AutoConfiguration.class, getClass().getClassLoader())
.forEach(autoConfigurationClassNames::add);
return autoConfigurationClassNames;
return ImportCandidates.load(AutoConfiguration.class, getClass().getClassLoader()).getCandidates();
}
private class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {

View File

@ -37,6 +37,7 @@ import org.springframework.util.Assert;
* candidates.
*
* @author Moritz Halbritter
* @author Scott Frederick
* @since 2.7.0
*/
public final class ImportCandidates implements Iterable<String> {
@ -57,6 +58,14 @@ public final class ImportCandidates implements Iterable<String> {
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.
*