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"); | 				.withPathSensitivity(PathSensitivity.RELATIVE).withPropertyName("spring.factories"); | ||||||
| 		getInputs() | 		getInputs() | ||||||
| 				.file((Callable<File>) () -> new File(this.sourceSet.getOutput().getResourcesDir(), | 				.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) | 				.withPathSensitivity(PathSensitivity.RELATIVE) | ||||||
| 				.withPropertyName("org.springframework.boot.autoconfigure.AutoConfiguration"); | 				.withPropertyName("org.springframework.boot.autoconfigure.AutoConfiguration"); | ||||||
| 
 | 
 | ||||||
|  | @ -137,17 +137,17 @@ public class AutoConfigurationMetadata extends DefaultTask { | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Reads auto-configurations from | 	 * 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 | 	 * @return auto-configurations | ||||||
| 	 */ | 	 */ | ||||||
| 	private List<String> readAutoConfigurationsFile() throws IOException { | 	private List<String> readAutoConfigurationsFile() throws IOException { | ||||||
| 		File file = new File(this.sourceSet.getOutput().getResourcesDir(), | 		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()) { | 		if (!file.exists()) { | ||||||
| 			return Collections.emptyList(); | 			return Collections.emptyList(); | ||||||
| 		} | 		} | ||||||
| 		// Nearly identical copy of | 		// 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)))) { | 		try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)))) { | ||||||
| 			List<String> autoConfigurations = new ArrayList<>(); | 			List<String> autoConfigurations = new ArrayList<>(); | ||||||
| 			String line; | 			String line; | ||||||
|  |  | ||||||
|  | @ -106,7 +106,7 @@ public class TestSliceMetadata extends DefaultTask { | ||||||
| 			Properties springFactories = readSpringFactories( | 			Properties springFactories = readSpringFactories( | ||||||
| 					new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories")); | 					new File(this.sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories")); | ||||||
| 			readTestSlicesDirectory(springFactories, | 			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()) { | 			for (File classesDir : this.sourceSet.getOutput().getClassesDirs()) { | ||||||
| 				addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories); | 				addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories); | ||||||
| 			} | 			} | ||||||
|  | @ -123,14 +123,17 @@ public class TestSliceMetadata extends DefaultTask { | ||||||
| 	 * @param directory directory to scan | 	 * @param directory directory to scan | ||||||
| 	 */ | 	 */ | ||||||
| 	private void readTestSlicesDirectory(Properties springFactories, File directory) { | 	private void readTestSlicesDirectory(Properties springFactories, File directory) { | ||||||
| 		File[] files = directory.listFiles(); | 		File[] files = directory.listFiles((dir, name) -> name.endsWith(".imports")); | ||||||
| 		if (files == null) { | 		if (files == null) { | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 		for (File file : files) { | 		for (File file : files) { | ||||||
| 			try { | 			try { | ||||||
| 				List<String> lines = removeComments(Files.readAllLines(file.toPath())); | 				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) { | 			catch (IOException ex) { | ||||||
| 				throw new UncheckedIOException("Failed to read file " + file, 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.ConditionalOnClass; | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 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.Conditional; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import org.springframework.core.io.support.SpringFactoriesLoader; | 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 |  * {@link Configuration @Configuration} with the exception that | ||||||
|  * {@literal Configuration#proxyBeanMethods() proxyBeanMethods} is always {@code false}. |  * {@literal Configuration#proxyBeanMethods() proxyBeanMethods} is always {@code false}. | ||||||
|  * <p> |  * <p> | ||||||
|  * They are located using the {@link AutoConfigurationLoader} and the |  * They are located using {@link ImportCandidates} and the {@link SpringFactoriesLoader} | ||||||
|  * {@link SpringFactoriesLoader} mechanism (keyed against |  * mechanism (keyed against {@link EnableAutoConfiguration}). | ||||||
|  * {@link EnableAutoConfiguration}). |  | ||||||
|  * <p> |  * <p> | ||||||
|  * Generally auto-configuration classes are marked as {@link Conditional @Conditional} |  * Generally auto-configuration classes are marked as {@link Conditional @Conditional} | ||||||
|  * (most often using {@link ConditionalOnClass @ConditionalOnClass} and |  * (most often using {@link ConditionalOnClass @ConditionalOnClass} and | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ import java.util.ArrayList; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import org.springframework.beans.factory.BeanClassLoaderAware; | import org.springframework.beans.factory.BeanClassLoaderAware; | ||||||
|  | import org.springframework.boot.context.annotation.ImportCandidates; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import org.springframework.core.io.support.SpringFactoriesLoader; | import org.springframework.core.io.support.SpringFactoriesLoader; | ||||||
| import org.springframework.core.type.classreading.MetadataReader; | import org.springframework.core.type.classreading.MetadataReader; | ||||||
|  | @ -63,11 +64,9 @@ public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoad | ||||||
| 
 | 
 | ||||||
| 	protected List<String> getAutoConfigurations() { | 	protected List<String> getAutoConfigurations() { | ||||||
| 		if (this.autoConfigurations == null) { | 		if (this.autoConfigurations == null) { | ||||||
| 			List<String> autoConfigurations = new ArrayList<>(); | 			List<String> autoConfigurations = new ArrayList<>( | ||||||
| 			autoConfigurations.addAll( |  | ||||||
| 					SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader)); | 					SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, this.beanClassLoader)); | ||||||
| 			autoConfigurations | 			ImportCandidates.load(AutoConfiguration.class, this.beanClassLoader).forEach(autoConfigurations::add); | ||||||
| 					.addAll(new AutoConfigurationLoader().loadNames(AutoConfiguration.class, this.beanClassLoader)); |  | ||||||
| 			this.autoConfigurations = autoConfigurations; | 			this.autoConfigurations = autoConfigurations; | ||||||
| 		} | 		} | ||||||
| 		return this.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.BeanFactoryAware; | ||||||
| import org.springframework.beans.factory.NoSuchBeanDefinitionException; | import org.springframework.beans.factory.NoSuchBeanDefinitionException; | ||||||
| import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | 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.boot.context.properties.bind.Binder; | ||||||
| import org.springframework.context.EnvironmentAware; | import org.springframework.context.EnvironmentAware; | ||||||
| import org.springframework.context.ResourceLoaderAware; | 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 | 	 * 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 | 	 * {@link #getSpringFactoriesLoaderFactoryClass()}. For backward compatible reasons it | ||||||
| 	 * will also consider {@link SpringFactoriesLoader} with | 	 * will also consider {@link SpringFactoriesLoader} with | ||||||
| 	 * {@link #getSpringFactoriesLoaderFactoryClass()}. | 	 * {@link #getSpringFactoriesLoaderFactoryClass()}. | ||||||
|  | @ -178,12 +179,11 @@ public class AutoConfigurationImportSelector implements DeferredImportSelector, | ||||||
| 	 * @return a list of candidate configurations | 	 * @return a list of candidate configurations | ||||||
| 	 */ | 	 */ | ||||||
| 	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { | 	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { | ||||||
| 		List<String> configurations = new ArrayList<>(); | 		List<String> configurations = new ArrayList<>( | ||||||
| 		configurations.addAll( |  | ||||||
| 				SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())); | 				SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader())); | ||||||
| 		configurations.addAll(new AutoConfigurationLoader().loadNames(AutoConfiguration.class, getBeanClassLoader())); | 		ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add); | ||||||
| 		Assert.notEmpty(configurations, | 		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."); | 						+ "are using a custom packaging, make sure that file is correct."); | ||||||
| 		return configurations; | 		return configurations; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ import java.lang.annotation.Target; | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; | ||||||
| import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | 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.embedded.tomcat.TomcatServletWebServerFactory; | ||||||
| import org.springframework.boot.web.servlet.server.ServletWebServerFactory; | import org.springframework.boot.web.servlet.server.ServletWebServerFactory; | ||||||
| import org.springframework.context.annotation.Conditional; | import org.springframework.context.annotation.Conditional; | ||||||
|  | @ -60,7 +61,7 @@ import org.springframework.core.io.support.SpringFactoriesLoader; | ||||||
|  * and classes can be searched. |  * and classes can be searched. | ||||||
|  * <p> |  * <p> | ||||||
|  * Auto-configuration classes are regular Spring {@link Configuration @Configuration} |  * 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 |  * {@link SpringFactoriesLoader} mechanism (keyed against this class). Generally | ||||||
|  * auto-configuration beans are {@link Conditional @Conditional} beans (most often using |  * auto-configuration beans are {@link Conditional @Conditional} beans (most often using | ||||||
|  * {@link ConditionalOnClass @ConditionalOnClass} and |  * {@link ConditionalOnClass @ConditionalOnClass} and | ||||||
|  |  | ||||||
|  | @ -59,8 +59,8 @@ public @interface ImportAutoConfiguration { | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * The auto-configuration classes that should be imported. When empty, the classes are | 	 * 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 | 	 * specified using a file in {@code META-INF/spring} where the file name is the | ||||||
| 	 * fully-qualified name of the annotated class. | 	 * fully-qualified name of the annotated class, suffixed with '.imports'. | ||||||
| 	 * @return the classes to import | 	 * @return the classes to import | ||||||
| 	 */ | 	 */ | ||||||
| 	@AliasFor("value") | 	@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"); |  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  * you may not use this file except in compliance with 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 java.util.Set; | ||||||
| 
 | 
 | ||||||
| import org.springframework.boot.context.annotation.DeterminableImports; | 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.AnnotatedElementUtils; | ||||||
| import org.springframework.core.annotation.AnnotationAttributes; | import org.springframework.core.annotation.AnnotationAttributes; | ||||||
| import org.springframework.core.annotation.AnnotationUtils; | import org.springframework.core.annotation.AnnotationUtils; | ||||||
|  | @ -95,9 +96,9 @@ class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelec | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	protected Collection<String> loadFactoryNames(Class<?> source) { | 	protected Collection<String> loadFactoryNames(Class<?> source) { | ||||||
| 		List<String> factoryNames = new ArrayList<>(); | 		List<String> factoryNames = new ArrayList<>( | ||||||
| 		factoryNames.addAll(SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader())); | 				SpringFactoriesLoader.loadFactoryNames(source, getBeanClassLoader())); | ||||||
| 		factoryNames.addAll(new AutoConfigurationLoader().loadNames(source, getBeanClassLoader())); | 		ImportCandidates.load(source, getBeanClassLoader()).forEach(factoryNames::add); | ||||||
| 		return factoryNames; | 		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.freemarker.FreeMarkerAutoConfiguration; | ||||||
| import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; | import org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration; | ||||||
| import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; | import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration; | ||||||
|  | import org.springframework.boot.context.annotation.ImportCandidates; | ||||||
| import org.springframework.context.annotation.Configuration; | import org.springframework.context.annotation.Configuration; | ||||||
| import org.springframework.core.io.DefaultResourceLoader; | import org.springframework.core.io.DefaultResourceLoader; | ||||||
| import org.springframework.core.type.AnnotationMetadata; | import org.springframework.core.type.AnnotationMetadata; | ||||||
|  | @ -212,7 +213,10 @@ class AutoConfigurationImportSelectorTests { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private List<String> getAutoConfigurationClassNames() { | 	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 { | 	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. | 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`. | 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]] | [[features.developing-auto-configuration.locating-auto-configuration-candidates]] | ||||||
| === 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: | The file should list your configuration classes, as shown in the following example: | ||||||
| 
 | 
 | ||||||
| [indent=0] | [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. | 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] | [indent=0] | ||||||
| ---- | ---- | ||||||
| 	com.example.IntegrationAutoConfiguration | 	com.example.IntegrationAutoConfiguration | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.springframework.boot.autoconfigure; | package org.springframework.boot.context.annotation; | ||||||
| 
 | 
 | ||||||
| import java.io.BufferedReader; | import java.io.BufferedReader; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | @ -22,57 +22,76 @@ import java.io.InputStreamReader; | ||||||
| import java.net.URL; | import java.net.URL; | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.Enumeration; | import java.util.Enumeration; | ||||||
|  | import java.util.Iterator; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| 
 | 
 | ||||||
|  | import org.jetbrains.annotations.NotNull; | ||||||
|  | 
 | ||||||
| import org.springframework.core.io.UrlResource; | import org.springframework.core.io.UrlResource; | ||||||
| import org.springframework.core.io.support.SpringFactoriesLoader; |  | ||||||
| import org.springframework.util.Assert; | 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 |  * The {@link #load(Class, ClassLoader)} method can be used to discover the import | ||||||
|  * name of the annotation}. Every line contains the full qualified class name of the |  * candidates. | ||||||
|  * annotated class. Comments are supported using the # character. |  | ||||||
|  * |  * | ||||||
|  * @author Moritz Halbritter |  * @author Moritz Halbritter | ||||||
|  * @see AutoConfiguration |  * @since 2.7.0 | ||||||
|  * @see SpringFactoriesLoader |  | ||||||
|  */ |  */ | ||||||
| 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 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 annotation annotation to load | ||||||
| 	 * @param classLoader class loader to use for loading | 	 * @param classLoader class loader to use for loading | ||||||
| 	 * @return list of names of annotated classes | 	 * @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"); | 		Assert.notNull(annotation, "'annotation' must not be null"); | ||||||
| 		ClassLoader classLoaderToUse = decideClassloader(classLoader); | 		ClassLoader classLoaderToUse = decideClassloader(classLoader); | ||||||
| 		String location = LOCATION + annotation.getName(); | 		String location = String.format(LOCATION, annotation.getName()); | ||||||
| 		Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location); | 		Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location); | ||||||
| 		List<String> autoConfigurations = new ArrayList<>(); | 		List<String> autoConfigurations = new ArrayList<>(); | ||||||
| 		while (urls.hasMoreElements()) { | 		while (urls.hasMoreElements()) { | ||||||
| 			URL url = urls.nextElement(); | 			URL url = urls.nextElement(); | ||||||
| 			autoConfigurations.addAll(readAutoConfigurations(url)); | 			autoConfigurations.addAll(readAutoConfigurations(url)); | ||||||
| 		} | 		} | ||||||
| 		return autoConfigurations; | 		return new ImportCandidates(autoConfigurations); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private ClassLoader decideClassloader(ClassLoader classLoader) { | 	private static ClassLoader decideClassloader(ClassLoader classLoader) { | ||||||
| 		if (classLoader == null) { | 		if (classLoader == null) { | ||||||
| 			return AutoConfigurationLoader.class.getClassLoader(); | 			return ImportCandidates.class.getClassLoader(); | ||||||
| 		} | 		} | ||||||
| 		return classLoader; | 		return classLoader; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private Enumeration<URL> findUrlsInClasspath(ClassLoader classLoader, String location) { | 	private static Enumeration<URL> findUrlsInClasspath(ClassLoader classLoader, String location) { | ||||||
| 		try { | 		try { | ||||||
| 			return classLoader.getResources(location); | 			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( | 		try (BufferedReader reader = new BufferedReader( | ||||||
| 				new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) { | 				new InputStreamReader(new UrlResource(url).getInputStream(), StandardCharsets.UTF_8))) { | ||||||
| 			List<String> autoConfigurations = new ArrayList<>(); | 			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); | 		int commentStart = line.indexOf(COMMENT_START); | ||||||
| 		if (commentStart == -1) { | 		if (commentStart == -1) { | ||||||
| 			return line; | 			return line; | ||||||
|  | @ -14,39 +14,28 @@ | ||||||
|  * limitations under the License. |  * limitations under the License. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| package org.springframework.boot.autoconfigure; | package org.springframework.boot.context.annotation; | ||||||
| 
 | 
 | ||||||
| import java.lang.annotation.ElementType; | import java.lang.annotation.ElementType; | ||||||
| import java.lang.annotation.Retention; | import java.lang.annotation.Retention; | ||||||
| import java.lang.annotation.RetentionPolicy; | import java.lang.annotation.RetentionPolicy; | ||||||
| import java.lang.annotation.Target; | import java.lang.annotation.Target; | ||||||
| import java.util.List; |  | ||||||
| 
 | 
 | ||||||
| import org.junit.jupiter.api.BeforeEach; |  | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| 
 | 
 | ||||||
| import static org.assertj.core.api.Assertions.assertThat; | import static org.assertj.core.api.Assertions.assertThat; | ||||||
| 
 | 
 | ||||||
| class AutoConfigurationLoaderTests { | class ImportCandidatesTest { | ||||||
| 
 |  | ||||||
| 	private AutoConfigurationLoader sut; |  | ||||||
| 
 |  | ||||||
| 	@BeforeEach |  | ||||||
| 	void setUp() { |  | ||||||
| 		this.sut = new AutoConfigurationLoader(); |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
| 	void loadNames() { | 	void loadReadsFromClasspathFile() { | ||||||
| 		List<String> classNames = this.sut.loadNames(TestAutoConfiguration.class, null); | 		ImportCandidates candidates = ImportCandidates.load(TestAnnotation.class, null); | ||||||
| 
 | 		assertThat(candidates).containsExactly("class1", "class2", "class3"); | ||||||
| 		assertThat(classNames).containsExactly("class1", "class2", "class3"); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@AutoConfiguration |  | ||||||
| 	@Target(ElementType.TYPE) | 	@Target(ElementType.TYPE) | ||||||
| 	@Retention(RetentionPolicy.RUNTIME) | 	@Retention(RetentionPolicy.RUNTIME) | ||||||
| 	public @interface TestAutoConfiguration { | 	public @interface TestAnnotation { | ||||||
| 
 | 
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
		Loading…
	
		Reference in New Issue