玩转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.propertiesapplication.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或应用某个配置。具体来说,@ConditionalOnPropertyname属性表示配置属性的名称,havingValue属性表示配置属性的期望值,默认为truematchIfMissing属性表示当配置属性不存在时是否匹配条件,默认为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或者是否应该应用某个配置。一些常见的条件注解包括:

  1. @ConditionalOnClass:当类路径中存在指定的类时,才会创建Bean或应用配置。
  2. @ConditionalOnMissingClass:当类路径中不存在指定的类时,才会创建Bean或应用配置。
  3. @ConditionalOnBean:当容器中存在指定的Bean时,才会创建Bean或应用配置。
  4. @ConditionalOnMissingBean:当容器中不存在指定的Bean时,才会创建Bean或应用配置。
  5. @ConditionalOnExpression:当满足SpEL表达式定义的条件时,才会创建Bean或应用配置。
  6. @ConditionalOnJava:当JVM运行的Java版本符合指定条件时,才会创建Bean或应用配置。
  7. @ConditionalOnWebApplication:当运行的环境是Web应用程序时,才会创建Bean或应用配置。
  8. @ConditionalOnNotWebApplication:当运行的环境不是Web应用程序时,才会创建Bean或应用配置。

总结

本文介绍了在Spring Boot项目中如何排除Starter自动配置类,以及根据不同环境动态排除配置的方法。通过spring.autoconfigure.exclude属性和条件注解如@ConditionalOnProperty,我们可以灵活控制Bean的创建和配置的应用,从而更好地适应不同的部署环境和需求。

本文已收录于我的个人博客:码农Academy的博客,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等

热门相关:士子风流   榴绽朱门   北宋大表哥   庶子风流   艳遇传说