• 补习系列(22)-全面解读 Spring Profile 的用法


    一、简介

    Profile的意思是配置,对于应用程序来说,不同的环境需要不同的配置。
    比如:

    • 开发环境,应用需要连接一个可供调试的数据库单机进程
    • 生产环境,应用需要使用正式发布的数据库,通常是高可用的集群
    • 测试环境,应用只需要使用内存式的模拟数据库

    Spring框架提供了多profile的管理功能,我们可以使用profile功能来区分不同环境的配置。

    二、 区分Bean对象

    首先,我们先看看如何基于Profile来定义一个Bean。

    通过@Profile注解可以为一个Bean赋予对应的profile名称,如下:

    @Component
    @Profile("dev")
    public class DevDatasourceConfig
    
    

    上面的DevDatasourceConfig被定义为 profile=dev,于是该Bean只会在dev(开发环境)模式下被启用。
    如果需要定义为非dev环境,可以使用这样的形式:

    @Component
    @Profile("!dev")
    public class DevDatasourceConfig
    
    

    XML风格配置
    上面的例子也可以使用XML配置文件达到同样的目的,如下:

    <beans profile="dev">
        <bean id="devDatasourceConfig"
          class="org.baeldung.profiles.DevDatasourceConfig" />
    </beans>
    

    读取Profile
    通过ConfigurableEnvironment这个Bean 可以获得当前的Profile,如下:

    public class ProfileManager {
        @Autowired
        Environment environment;
     
        public void getActiveProfiles() {
            for (final String profileName : environment.getActiveProfiles()) {
                System.out.println("Currently active profile - " + profileName);
            }   
        }
    }
    

    三、 设置Profile

    接下来,为了让容器"仅仅注册那些所需要的Bean",我们需要通过一些手段来设置当前的profile。

    有很多方法可以达到这个目的,下面一一介绍。

    3.1 WebApplicationInitializer接口

    在Web应用程序中,通过WebApplicationInitializer可以对当前的ServletContext进行配置。

    如下,通过注入spring.profiles.active变量可以为Spring上下文指定当前的 profile:

    @Configuration
    public class MyWebApplicationInitializer 
      implements WebApplicationInitializer {
     
        @Override
        public void onStartup(ServletContext servletContext) throws ServletException {
      
            servletContext.setInitParameter(
              "spring.profiles.active", "dev");
        }
    }
    

    3.2 通过 web.xml定义

    与上面的方法类似,在web.xml中通过context-param元素也可以设置profile。

    但前提是当前应用程序使用了xml的配置文件风格,如下:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/app-config.xml</param-value>
    </context-param>
    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>dev</param-value>
    </context-param>
    

    3.3 JVM启动参数

    通过Java程序启动参数同样可以对profile进行设定,如下:

    java -jar application.jar -Dspring.profiles.active=dev
    
    

    spring-boot-maven-plugin插件也支持设定profile,其原理也是通过启动参数实现,可以参考这里

    https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-profiles.html

    3.4 环境变量

    在Unix/Linux环境中,可以通过环境变量注入profile的值:

    export spring_profiles_active=dev
    java -jar application.jar 
    
    

    3.5 application.properties

    可以在application.properties配置文件中指定spring.profiles.active属性:

    spring.profiles.active=dev
    

    SpringBoot默认会加载并读取该配置,当发现为profile=dev时,会同时关联加载application-dev.properties这个配置。
    这种方式非常简单,可以实现对不同环境采用单独的配置文件进行隔离。

    3.6 Maven Profile

    Maven本身也提供了Profile的功能,可以通过Maven的Profile配置来指定Spring的Profile。

    这种做法稍微有点复杂,需要先在pom.xml中设定不同的 maven profile,如下:

    <profiles>
        <profile>
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <properties>
                <spring.profiles.active>dev</spring.profiles.active>
            </properties>
        </profile>
        <profile>
            <id>prod</id>
            <properties>
                <spring.profiles.active>prod</spring.profiles.active>
            </properties>
        </profile>
    </profiles>
    

    这里,分别声明了dev和prod两个profile,每个profile都包含了一个spring.profiles.active属性,这个属性用来注入到 Spring中的profile入参。
    在SpringBoot的配置文件application.properties中,需要替换为这个maven传入的property:

    ## 使用Maven的属性进行替换
    spring.profiles.active=@spring.profiles.active@
    

    接下来,需要让Maven在打包时能将application.properties进行过滤处理,同时替换掉变量,需编辑pom.xml如下:

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
    

    这里定义了filtering=true,因此Resource打包插件会对配置文件执行过滤。

    如果你的项目pom定义继承自 spring-boot-starter-parent,那么可以不需要配置这个filter

    最后,在maven打包时指定参数如下:

    mvn clean package -Pprod
    

    3.7 使用 @ActiveProfiles

    @ActiveProfile 是用于单元测试场景的注解,可以为测试代码指定一个隔离的profile,如下:

    @ActiveProfiles("test")
    public void ApiTest{
      ...
    }
    

    3.8 使用 ConfigurableEnvironment

    ConfigurableEnvironment 这个Bean封装了当前环境的配置信息,你可以在启动应用前进行设定操作:

    SpringApplication application = new SpringApplication(MyApplication.class);
    
    //设置environment中的profiler
    ConfigurableEnvironment environment = new StandardEnvironment();
    environment.setActiveProfiles("dev","join_dev");
    
    application.setEnvironment(environment);
    application.run(args)
    

    3.9 SpringApplication.setAdditionalProfiles

    SpringApplication这个类还提供了setAdditionalProfiles方法,用来让我们实现"附加"式的profile。
    这些profile会同时被启用,而不是替换原来的active profile,如下:

    SpringApplication application = new SpringApplication(MyApplication.class);
    application.setAdditionalProfiles("new_dev");
    

    这种方式可以实现无条件的启用profile,优先级是最高的。
    当然,还可以通过设定spring.profiles.include来达到同样的目的。

    四、 优先级

    至此,我们已经提供了很多种方法来设定 Spring应用的profile,当它们同时存在时则会根据一定优先级来抉择,参考如下:

    1. SpringApplication.setAdditionalProfiles
    2. ConfigurableEnvironment、@ActiveProfiles
    3. Web.xml的 context-param
    4. WebApplicationInitializer
    5. JVM 启动参数
    6. 环境变量
    7. Maven profile、application.properties

    从上至下,优先级从高到低排列。
    其中,Maven profile与配置文件的方式相同,环境变量以及JVM启动参数会覆盖配置文件的内容。
    1和2则属于进程内的控制逻辑,优先级更高。
    如果在启动SpringBoot应用前对当前ConfigurableEnvironment对象注入了profile,则会优先使用这个参数, ActiveProfiles用于测试环境,其原理与此类似。
    SpringApplication.setAdditionalProfiles则是无论如何都会附加的profile,优先级最高。

    五、 案例

    最后,我们在SpringBoot中演示一个使用Profile的例子。

    一般,在开发环境和生产环境中的数据源配置是不同的,借助Profile我们可以定义出不同环境的数据源Bean。
    首先我们先创建一个接口:

    public interface DatasourceConfig {
        public void setup();
    }
    

    对于开发环境,DatasourceConfig实现如下:

    @Component
    @Profile("dev")
    public class DevDatasourceConfig implements DatasourceConfig {
        @Override
        public void setup() {
            System.out.println("Setting up datasource for DEV environment. ");
        }
    }
    

    同样,为生产环境也实现一个DatasourceConfig:

    @Component
    @Profile("production")
    public class ProductionDatasourceConfig implements DatasourceConfig {
        @Override
        public void setup() {
           System.out.println("Setting up datasource for PRODUCTION environment. ");
        }
    }
    

    接下来,我们声明一个Bean,对数据源执行初始化方法:

    @Component
    public class SpringProfilesTest {
        @Autowired
        DatasourceConfig datasourceConfig;
     
        @PostConstruct
        public void setupDatasource() {
            datasourceConfig.setup();
        }
    }
    
    

    之后,在application.properties的配置为:

    spring.profiles.active=dev
    

    启动SpringBoot 应用,发现输出如下:

    Setting up datasource for DEV environment.
    

    此时说明dev的profile被启用了!

    参考文档

    https://www.baeldung.com/spring-profiles
    https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
    http://dolszewski.com/spring/spring-boot-properties-per-maven-profile/
    https://www.concretepage.com/spring-5/activeprofiles-example-spring-test
    https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-profiles.html

    点击查看美码师的 SpringBoot 补习系列

  • 相关阅读:
    Schedular
    CronTrigger
    SimpleTrigger
    Quartz代码示例
    quartz
    gson and json
    json数据结构和gson的比较
    stackApp符号匹配
    Git命令
    org.apache.maven.archiver.mavenarchiver.getmanifest怎么解决
  • 原文地址:https://www.cnblogs.com/littleatp/p/11172602.html
Copyright © 2020-2023  润新知