SpringBoot的多环境配置详解(下)——注解版

  • 3、profile激活的优先级
  • 4、profile的书写
  • 二、案例
  • 一、 @Profile注解使用说明

    使用@profile注解的目的是未了多环境开发,比如开发环境使用dev, 生产环境使用prod,就可以使用@Profile注解实现不同的开发环境使用不同的数据源;

    @profile注解 使用说明:

    spring3.2之前 @Profile注解用在类上
    spring3.2 之后 @Profile注解用在 方法上

    另外一种说法

    在项目运行中,包括多种环境,例如线上环境prod(product)、开发环境dev(development)、测试环境test、提测环境qa、单元测试unitest等等。不同的环境需要进行不同的配置,从而在不同的场景中跑我们的程序。例如prod环境和dev环境通常需要连接不同的数据库、需要配置不同的日志输出配置。还有一些类和方法,在不同的环境下有不同的实现方式。

    Spring Boot 对此提供了支持,一方面是注解@Profile,另一方面还有多资源配置文件。

    @profile注解的作用是指定类、方法、注解上,在特定的 Profile 环境生效,任何@Component@Configuration注解的类都可以使用@Profile注解。在使用DI来依赖注入的时候,能够根据@profile标明的环境,将注入符合当前运行环境的相应的bean。

    使用要求:

    • 可以在任何直接或间接使用@Component和@Configuration的类上作为一个类型注解使用@profile
    • @Profile中需要指定一个字符串,约定生效的环境
    • @Profile作为元注解,用于组成自定义构造型注解
    • @Profile作为任何@Bean方法的方法级注解

    1、@Profile的使用位置

    (1) @Prifile修饰类

    @Configuration
    @Profile("prod")
    public class JndiDataConfig {
          
          
    
        @Bean(destroyMethod="")
        public DataSource dataSource() throws Exception {
          
          
            Context ctx = new InitialContext();
            return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
        }
    }
    

    (2) @Profile修饰方法

    @Configuration
    public class AppConfig {
          
          
    
        @Bean("dataSource")
        @Profile("dev")
        public DataSource standaloneDataSource() {
          
          
            return new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.HSQL)
                .addScript("classpath:com/bank/config/sql/schema.sql")
                .addScript("classpath:com/bank/config/sql/test-data.sql")
                .build();
        }
    
        @Bean("dataSource")
        @Profile("prod")
        public DataSource jndiDataSource() throws Exception {
          
          
            Context ctx = new InitialContext();
            return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
        }
    }
    

    (3)@Profile修饰注解

    @Profile注解支持定义在其他注解之上,以创建自定义场景注解。这样就创建了一个@Dev注解,该注解可以标识bean使用于@Dev这个场景。后续就不再需要使用@Profile("dev")的方式,这样即可以简化代码。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Profile("dev")
    public @interface Dev{
          
          
    }
    

    2、profile激活

    实际使用中,注解中标示了prod、test、dev等多个环境,运行时使用哪个profile由spring.profiles.active控制,以下说明常见的几种方式。

    (1)配置文件方式激活profile

    确定当前使用的是哪个环境,这边环境的值与application-prod.properties中-后面的值对应,这是SpringBoot约定好的。
    在resources/application.properties中添加下面的配置。需要注意的是,spring.profiles.active的取值应该与@Profile注解中的标示保持一致。

    spring.profiles.active=dev 
    

    除此之外,同理还可以在resources/application.yml中配置,效果是一样的:

    spring:
      profiles:
        active: dev
    

    注意:

    spring.profiles.active 激活方式 ,如果 spring.profiles.active 未配置,则使用spring.profiles.default激活方式。如果前面都没有配置,就会加载没有定义在profile中的bean

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

    (2)命令行方式激活profile

    在打包后运行的时候,添加参数:

    1)java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar   --spring.profiles.active=dev
    (2)java -jar application.jar -Dspring.profiles.active=dev
    (3)java -jar -Dspring.profiles.active=prod *.jar
    

    (3)在web.xml 中配置

    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>dev</param-value>
    </context-param>
    <servlet>
            <servlet-name>zszxzServlet</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <param-name>spring.profiles.default</param-name>
                <param-value>dev</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>zszxzServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    (4)在类上使用@ActiveProfiles注解

    @RunWith(SpringJUnit4ClassRunner.class)//创建spring应用上下文
    @ContextConfiguration(classes= DataSourceConfig.class)//加载配置类
    @ActiveProfiles("dev")
    public class ProfileTest {
          
          
    
        @Autowired
        private DataSource dataSource;
    
        @Test
        public void sheetTest(){
          
          
            JdbcTemplate jdbc = new JdbcTemplate(dataSource);
            List<String> query = jdbc.query("select * from customer", new RowMapper<String>() {
          
          
                @Override
                public String mapRow(ResultSet rs, int rowNum) throws SQLException {
          
          
                    return rs.getLong("id") + ":" + rs.getString("customer_name");
                }
            });
            // [19:知识追寻者, 20:知识追寻者, 21:知识追寻者, 22:知识追寻者, 23:知识追寻者]
            System.out.println(query);
        }
    }
    

    (5)在 Java 代码中激活 profile

    方式1:直接指定环境变量来激活 profile:

    System.setProperty("spring.profiles.active", "test");
    

    方式2:在 Spring 容器中激活 profile:

    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.getEnvironment().setActiveProfiles("development");
    ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
    ctx.refresh();
    

    方式3:实现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");
        }
    }
    

    方式4:使用 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)
    

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

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

    注意:

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

    (6)通过环境变量的方式激活

    在Unix/Linux环境中,可以通过环境变量注入profile的值:
    export spring_profiles_active=dev
    java -jar application.jar 
    

    (7)通过Maven的方式激活

    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 -P prod
    

    3、profile激活的优先级

    至此,我们已经提供了很多种方法来设定 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,优先级最高。

    3、profile的书写

    (1)单个环境名称

    profile字符串可以包含简单的profile名称(例如"p1") 或**profile表达式。 ** Profile表达式允许表达更复杂的profile逻辑, 例如"p1&p2" 。

    另外profile表达式支持以下运算符:

    (1)、 ! - 逻辑非
    (2) 、& - 逻辑并
    (3).、| - 逻辑或

    (2)组合环境名称

    当@Profile注解里配置多个环境时,记住需要使用大括号扩住, 例如:@Profile({"prod", "dev","test"}) 这种用法等同于使用逻辑或。相当于在prod、dev、test三种环境里均可以产生效果。

    3、案例:

    (1)、通过@Profile注解匹配active参数,动态加载内部配置

    @Profile可接受一个或者多个参数,例如:

    @Profile({
          
          "test","prod"})
    @Configuration
    public class WebConfig{
          
          
    	@Bean
    	public Queue hello(){
          
          
    		return new Queue("hello");
    	}
    	@Profile("receiver")
    	@Bean
    	public Receiver receiver(){
          
          
    		return new Receiver();
    	}
    	@Profile("dev")
    	@Bean
    	public DevDemo dev(){
          
          
    		return new DevDemo ();
    	}	
    

    当 spring.profiles.active=prod,dev 时,该配置类生效,且第一个@Bean和第三个@Bean生效
    如果spring.profiles.active=prod ,则该配置文件生效,第一个@Bean生效
    如果spring.profiles.active=dev ,该配置文件未生效,所以下面的@Bean都不会生效
    如此,当我们的项目需要运行在不同环境,特异化配置又比较多,该注解的优势是相当明显的!

    (2) 配置不同环境的数据源

    /**
     * @Author lsc
     * <p>spring3.2之前 @Profile注解用在类上
     * spring3.2 之后 @Profile注解用在 方法上
     * </p>
     */
    @Configuration
    public class DataSourceConfig {
          
          
    
        @Bean
        @Profile("dev")
        public DataSource devDataSource() {
          
          
            System.out.println(" dev DataSource !!");
            BasicDataSource basicDataSource = new BasicDataSource();
            basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            basicDataSource.setUrl("jdbc:mysql://localhost:3308/zszxz");
            basicDataSource.setUsername("root");
            basicDataSource.setPassword("1234");
            return basicDataSource;
        }
    
        @Bean
        @Profile("prod")
        public DataSource prodDataSource() {
          
          
            System.out.println(" prod DataSource !!");
            BasicDataSource basicDataSource = new BasicDataSource();
            basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
            basicDataSource.setUrl("jdbc:mysql://localhost:3306/zszxz");
            basicDataSource.setUsername("root");
            basicDataSource.setPassword("1234");
            return basicDataSource;
        }
    }
    

    如果你是在xml中配置,示例如下

    <beans profile="dev">
            <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
                  p:driverClassName="com.mysql.jdbc.Driver"
                  p:url="jdbc:mysql://localhost:3306/zszxzb"
                  p:username="root"
                  p:password="1234"/>
    </beans>
    <beans profile="prod">
            <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
                  p:driverClassName="com.mysql.jdbc.Driver"
                  p:url="jdbc:mysql://localhost:3306/zszxzb"
                  p:username="root"
                  p:password="1234"/>
    </beans>
    

    鸣谢:
    https://blog.csdn.net/loongkingwhat/article/details/105745303
    https://www.jianshu.com/p/75de79fba705
    https://www.cnblogs.com/huahua-test/p/11576907.html

猜你喜欢

转载自blog.csdn.net/CNCDXX_88/article/details/129750666