Spring Boot 提供了一种条件化配置机制,通过 @Conditional 注解及其派生注解,可以根据特定条件来决定是否加载某些配置类或 Bean。这种机制在微服务架构中非常有用,可以根据环境、属性、类的存在性等条件来灵活地配置应用程序。
com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration
你可以使用 # 字符在imports文件中添加注释。
自动配置类不应该通过 @ComponentScan 注解或其他方式让 Spring 自动扫描和加载,因为这可能会导致不必要或意外的对象被创建,从而干扰应用程序的正常运行
自动配置类中不应该包含 @ComponentScan 注解来扫描其他组件。如果你需要导入其他配置或类,应该使用 @Import 注解显式地导入。
@AutoConfiguration
// Some conditions ...
public class MyAutoConfiguration {
// Auto-configured beans ...
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(SomeService.class)
public static class SomeServiceConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
}
如果你将 @ConditionalOnClass 或 @ConditionalOnMissingClass 作为元注解的一部分来组合自定义的注解,在这种情况下,你必须使用 name 属性来引用类,因为使用 value 属性的方式不会被正确处理。
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public SomeService someService() {
return new SomeService();
}
}
在前面的例子中,如果 ApplicationContext 中还没有包含 SomeService 类型的 Bean,那么将创建 SomeService Bean。
一般来说,自动化配置 Bean 的创建在用户应用 Bean 之后,建议只在自动化配置对象中使用 @ConditionalOnBean 和 @ConditionalOnMissingBean 注解。以避免自动配置 Bean 干扰用户的应用 Bean。
在声明 @Bean 方法时,尽可能提供详细的类型信息。例如,如果你的 Bean 的具体实现类实现了一个接口,那么 @Bean 方法的返回类型应该是具体的实现类,而不是接口。因为在使用 Bean 条件(如 @ConditionalOnBean 或 @ConditionalOnMissingBean)时,条件的评估只能依赖于方法签名中可用的类型信息。
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserServiceImpl(); // 实际返回的是 UserServiceImpl
}
}
@Configuration
public class AppConfig {
@Bean
public UserServiceImpl userService() {
return new UserServiceImpl(); // 返回具体的实现类
}
}
@ConditionalOnProperty 注解允许根据 Spring Environment 的属性来决定是否包含某些配置。使用 prefix 和 name 属性可以指定需要检查的属性。默认情况下,任何存在且不等于 false 的属性都会匹配。你还可以通过使用 havingValue 和 matchIfMissing 属性来创建更复杂的检查。
@Configuration
@ConditionalOnProperty(prefix = "app", name = {"prop1", "prop2"}, havingValue = "true")
public class MultiPropertyConfig {
@Bean
public MultiPropertyService multiPropertyService() {
return new MultiPropertyService();
}
}
只有当 app.prop1 和 app.prop2 都存在且其值为 “true” 时,MultiPropertyConfig 类才会被加载,并注册 multiPropertyService Bean。
@ConditionalOnResource 注解允许仅在特定资源存在时包含某些配置。你可以使用 Spring 的常规资源表示方法来指定这些资源。
@Configuration
@ConditionalOnResource(resources = "file:/home/user/test.dat")
public class ResourceConfig {
@Bean
public MyService myService() {
return new MyService();
}
}
@ConditionalOnResource 注解检查是否存在位于 /home/user/test.dat 的文件资源。如果该资源存在,ResourceConfig 配置类会被加载,并且 myService Bean 会被创建和注册。
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication 注解用于根据应用程序是否是一个 Web 应用程序来决定是否包含某些配置。@ConditionalOnWarDeployment 和 @ConditionalOnNotWarDeployment 注解用于根据应用程序是否是一个传统的 WAR 部署应用程序来决定是否包含某些配置。
@ConditionalOnExpression 注解允许根据 SpEL(Spring Expression Language)表达式的结果来决定是否包含某些配置。使用这个注解可以让你基于更复杂的条件来决定配置是否生效。
@Configuration
@ConditionalOnExpression("#{systemProperties['os.name'].contains('Windows') and environment['app.mode'] == 'production'}")
public class WindowsProductionConfig {
@Bean
public WindowsProductionService windowsProductionService() {
return new WindowsProductionService();
}
}
#{systemProperties[‘os.name’].contains(‘Windows’) and environment[‘app.mode’] == ‘production’} 用于检查系统属性 os.name 是否包含 ‘Windows’ 以及环境属性 app.mode 是否等于 ‘production’。只有当这两个条件都满足时,WindowsProductionConfig 配置类才会被加载,并且 windowsProductionService Bean 将被创建和注册。
在使用 @ConditionalOnExpression 注解时,如果 SpEL 表达式中引用了某个 Bean,这个 Bean 可能会在 Spring 上下文的刷新过程中被过早初始化。
主程序上一般加有 @SpringBootApplication 注解,@SpringBootApplication 注解带有 @EnableAutoConfiguration 注解,@EnableAutoConfiguration 注解带有 @Import(AutoConfigurationImportSelector.class) 注解,Spring Boot 会加载 AutoConfigurationImportSelector 类并执行其中的 selectImports 方法获得其中的自动化配置类。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(
beanFactory, getBeanFactoryPostProcessors());
}
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory,
List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// 获取所有实现 BeanDefinitionRegistryPostProcessor 接口的 Bean 名称
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 对实现了 PriorityOrdered 的 BeanDefinitionRegistryPostProcessor 进行实例化并执行相应的方法
// currentRegistryProcessors 保存当前需要执行的 BeanDefinitionRegistryPostProcessor 对象
// processedBeans 所有的 BeanDefinitionRegistryPostProcessor 对象
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(
beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
// ConfigurationClassPostProcessor 会在这里执行
invokeBeanDefinitionRegistryPostProcessors(
currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
// 清空 currentRegistryProcessors
currentRegistryProcessors.clear();
// 接下来,对实现了 Ordered 的 BeanDefinitionRegistryPostProcessor 进行实例化并执行相应的方法
postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(
beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(
currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
// 最后,调用所有其他 BeanDefinitionRegistryPostProcessors,
// 直到没有其他 BeanDefinitionRegistryPostProcessors 出现为止。
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(
beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(
currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
// 后面还会对实现了 BeanFactoryPostProcessor 的对象进行实例化并执行相应的方法,这里省略
}
/** Invoke the given BeanDefinitionRegistryPostProcessor beans. */
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors,
BeanDefinitionRegistry registry,
ApplicationStartup applicationStartup) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
@Override
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
// checkConfigurationClassCandidate 用于检查一个 BeanDefinition
// 是否是一个配置类候选者(即是否具有 @Configuration、@Component、@Import 等注解)
if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果类被 @Order 修饰,进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
// 解析被 @Configuration 修饰的类,第一次进入此类只有被 @SpringBootApplication 修饰的主类
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
// 对配置类进行解析
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// 将解析后新增的配置类解析为 BeanDefinition 并添加到上下文中
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
// 如果解析后新增了配置类,对新增的配置类进行解析
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = Set.of(candidateNames);
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
}
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
parse(annotatedBeanDef.getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition abstractBeanDef
&& abstractBeanDef.hasBeanClass()) {
parse(abstractBeanDef.getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 这里处理延迟导入的配置类 (实现了 ImportSelector 和 DeferredImportSelector 接口的配置类)
// 比如 AutoConfigurationImportSelector 类
this.deferredImportSelectorHandler.process();
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter)
throws IOException {
// 递归处理配置类
SourceClass sourceClass = null;
try {
sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
} while (sourceClass != null);
} catch (IOException ex) {
throw new BeanDefinitionStoreException(
"I/O failure while processing configuration class [" + sourceClass + "]", ex);
}
}
@Nullable
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 递归处理带有 @Component 注解的配置类
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// 处理配置类上的 @PropertySource 注解 略
// 处理配置类上的 @ComponentScan 注解,略
// 处理配置类上的 @Import 注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理配置类上的 @ImportResource 注解 略
// 处理配置类上单个 @Bean 方法 略
// 处理接口上的默认方法 略
// 处理超类 略
return null;
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,
boolean checkForCircularImports) {
try {
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
this.environment, this.resourceLoader, this.registry);
// 如果实现了 DeferredImportSelector,添加该类到 deferredImportSelectorHandler 中
// DeferredImportSelector 为延迟导入接口,该类在解析的最后会被执行
if (selector instanceof DeferredImportSelector deferredImportSelector) {
this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
}
else {
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);
processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);
}
}
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
}
}
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
// 对配置类进行解析 略
// 这里处理延迟导入的配置类 (实现了 ImportSelector 和 DeferredImportSelector 接口的配置类)
// 比如 AutoConfigurationImportSelector 类
this.deferredImportSelectorHandler.process();
}
private class DeferredImportSelectorHandler {
@Nullable
private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>();
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.processGroupImports();
}
}
}
private class DeferredImportSelectorGroupingHandler {
private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>();
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses =
new HashMap<>();
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
grouping
.getImports()
.forEach(
entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false);
} catch (BeanDefinitionStoreException ex) {
throw ex;
}
});
}
}
}
private static class DeferredImportSelectorGrouping {
private final DeferredImportSelector.Group group;
private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>();
/**
* Return the imports defined by the group.
* @return each import with its associated configuration class
*/
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 从 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中获取自动化配置类的全类名
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复的类名
configurations = removeDuplicates(configurations);
// 获取明确排除的自动配置类,如在 @EnableAutoConfiguration 注解中使用 exclude 或者 excludeName 属性
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 执行过滤器对自动化配置类进行进一步过滤,比如使用了 @ConditionalOnClass 的注解会在这里进行过滤
configurations = getConfigurationClassFilter().filter(configurations);
// 执行一些 AutoConfigurationImportListener 监听器的
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader())
.getCandidates();
return configurations;
}
private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware,
ResourceLoaderAware {
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
AutoConfigurationEntry autoConfigurationEntry =
((AutoConfigurationImportSelector) deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
}
private static final String LOCATION = "META-INF/spring/%s.imports";
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
Assert.notNull(annotation, "'annotation' must not be null");
ClassLoader classLoaderToUse = decideClassloader(classLoader);
// 这里的 location = META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
String location = String.format(LOCATION, annotation.getName());
Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
// importCandidates 保存读取到的自动化配置的全类名
List<String> importCandidates = new ArrayList<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
importCandidates.addAll(readCandidateConfigurations(url));
}
return new ImportCandidates(importCandidates);
}