玩转SpringBoot:动态排除Starter配置,轻松部署
引言
在软件开发中,进行本地单元测试是一项常规且必要的任务。然而,在进行单元测试时,有时需要启动一些中间件服务,如Kafka、Elasticjob等。举例来说,我曾经遇到过一个问题:项目中使用了Redisson
锁,但由于Redisson
版本较低,在Mac环境下偶尔会报错# RedisConnectionException: Unable to init enough connections amount
。鉴于升级版本带来的风险,以及问题仅在本地启动时出现,我决定在本地环境中排除Redisson
的Starter,从而避免影响其他环境的配置。那么,我们应该如何做呢?
我们以上篇介绍如何自定义Starter中的文章中示例
CoderAcademyStarter
为例。我们引入了这个starter。
Starter自动配置类的排除
在《SpringBoot如何自定义Starter》中,我们介绍了如何在META-INF/spring.factories
文件中使用org.springframework.boot.autoconfigure.EnableAutoConfiguration
来指定Starter的自动配置类。Spring Boot启动时会扫描所有已引入jar包中的spring.factories
文件,并根据EnableAutoConfiguration
键下的类来加载和执行相应的自动配置逻辑。当我们不希望应用启动时使用该Starter的功能时,就需要排除自动配置类。
我们可以通过spring.autoconfigure.exclude
属性排除CoderAcademyStarter
的自动配置类:
spring.autoconfigure.exclude=com.springboot.starter.coderacademy.config.CoderAcademyAutoConfig
spring.autoconfigure.exclude
是Spring Boot中的一个属性,用于指定在自动配置过程中要排除的自动配置类。通过设置该属性,我们可以明确告知Spring Boot不要自动配置指定的类,即使它们满足自动配置的条件。当需要禁用特定的自动配置类时,可以在application.properties
或application.yml
中设置spring.autoconfigure.exclude
属性,并提供要排除的自动配置类的完全限定类名。这样,Spring Boot在自动配置过程中将不会考虑这些类。
此时,如果我们在使用CoderAcademyService
时会出现错误:
根据不同环境排除Starter自动配置类
在日常开发中,我们通常需要针对不同的环境指定不同的配置。我们可以通过spring.actice.profiles
属性来指定不同环境的配置文件的加载。例如,我们可以在本地指定spring.actice.profiles=local
,然后创建一个application-local.properties
的配置文件,在其中指定spring.autoconfigure.exclude
。
另外,我们还可以实现ApplicationListener<ApplicationContextInitializedEvent>
接口,通过监听上下文初始化事件来根据环境变量的标识排除Starter的自动配置类。当Spring应用程序的ApplicationContext
被初始化时,将触发ApplicationContextInitializedEvent
事件。通常,在应用程序的上下文初始化过程中会先加载bean定义、执行后处理器等操作。因此,通过监听ApplicationContextInitializedEvent
事件,我们可以在Spring容器初始化的早期阶段执行一些定制化的逻辑。
我们可以通过实现ApplicationListener<ApplicationContextInitializedEvent>
接口,根据一些环境变量的标识排除Starter的自动配置类。例如,我们可以定义一个coderacademy.enable
的标识来决定是否扫描Starter。以下是一个示例:
import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
@Configuration
public class EnvironmentHandler implements ApplicationListener<ApplicationContextInitializedEvent> {
@Override
public void onApplicationEvent(ApplicationContextInitializedEvent event) {
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
if (environment.getProperty("coderacademy.enable") != null && "false".equals(environment.getProperty("coderacademy.enable"))) {
System.setProperty("spring.autoconfigure.exclude", "com.springboot.starter.coderacademy.config.CoderAcademyAutoConfig");
}
}
}
然后,在启动应用时,通过-Dcoderacademy.enable=false
指定排除Starter的自动配置类。这种方式特别适用于本地启动应用时排除Starter或其他Bean的初始化。
当然本地启动也可以直接通过-Dspring.autoconfigure.exclude=com.springboot.starter.coderacademy.config.CoderAcademyAutoConfig
也可以满足排除Starter的配置。
自定义Starter Bean排除
在《SpringBoot如何自定义Starter》文中,我们还提到了一种调用方使用Starter的方式,我们可以不是用自动配置类的Starter,可以自定义配置的信息,在手动创建Starter对应的服务的Bean。例如:
@Data
@Configuration
public class StarterConfig {
@Value("${springboot.coderacademy.name}")
private String staterMsg;
@Bean
public CoderAcademyService coderAcademyService(){
CoderAcademyConfig coderAcademyConfig = new CoderAcademyConfig();
coderAcademyConfig.setUrl(staterMsg);
return new CoderAcademyService(coderAcademyConfig);
}
}
对于这种情况,我们可以使用Spring Boot的注解@ConditionalOnProperty
来控制是否创建Bean。@ConditionalOnProperty
是一个条件注解,根据配置属性的值来决定是否应该创建一个Bean或应用某个配置。具体来说,@ConditionalOnProperty
的name
属性表示配置属性的名称,havingValue
属性表示配置属性的期望值,默认为true
,matchIfMissing
属性表示当配置属性不存在时是否匹配条件,默认为false
。
因此,我们可以给CoderAcademyService
的Bean添加@ConditionalOnProperty
注解:
@Data
@Configuration
public class StarterConfig {
@Value("${springboot.coderacademy.name}")
private String staterMsg;
@ConditionalOnProperty(name = "coderacademy.enable", havingValue = "true", matchIfMissing = true)
@Bean
public CoderAcademyService coderAcademyService(){
CoderAcademyConfig coderAcademyConfig = new CoderAcademyConfig();
coderAcademyConfig.setUrl(staterMsg);
return new CoderAcademyService(coderAcademyConfig);
}
}
这样,我们在启动应用时可以通过-Dcoderacademy.enable=false
变量来控制是否创建CoderAcademyService
,而设置了matchIfMissing=true
,即使其他环境没有该环境变量也不受影响。
除了@ConditionalOnProperty
之外,Spring Boot还提供了其他一些条件注解,用于根据不同的条件来决定是否应该创建Bean或者是否应该应用某个配置。一些常见的条件注解包括:
@ConditionalOnClass
:当类路径中存在指定的类时,才会创建Bean或应用配置。@ConditionalOnMissingClass
:当类路径中不存在指定的类时,才会创建Bean或应用配置。@ConditionalOnBean
:当容器中存在指定的Bean时,才会创建Bean或应用配置。@ConditionalOnMissingBean
:当容器中不存在指定的Bean时,才会创建Bean或应用配置。@ConditionalOnExpression
:当满足SpEL表达式定义的条件时,才会创建Bean或应用配置。@ConditionalOnJava
:当JVM运行的Java版本符合指定条件时,才会创建Bean或应用配置。@ConditionalOnWebApplication
:当运行的环境是Web应用程序时,才会创建Bean或应用配置。@ConditionalOnNotWebApplication
:当运行的环境不是Web应用程序时,才会创建Bean或应用配置。
总结
本文介绍了在Spring Boot项目中如何排除Starter自动配置类,以及根据不同环境动态排除配置的方法。通过spring.autoconfigure.exclude
属性和条件注解如@ConditionalOnProperty
,我们可以灵活控制Bean的创建和配置的应用,从而更好地适应不同的部署环境和需求。
本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等