Spring自动装配之Profile应用

通常,在开发Spring项目时,可能存在着多个不同环境中的组件。例如:测试环境的数据源,预发环境数据源和生产环境数据源,对于这种需求,Spring提供了一个强大的功能,即:Profile功能,使用该功能,可以再开发过程中,对于项目中的组件进行快速灵活地切换,而不需要每次去修改大量的配置。

温馨提示:本博客已经发布小程序,可在微信小程序中搜索”百变码农”,手机上也能看!

1、Profile介绍及用法

Profile是Spring中提供的一个可以根据当前环境来动态切换和激活一些列组件的功能,例如:切换项目中的数据源,切换某一个具体的配置类等。使用示例如下:

假设条件:开发过程中,项目中使用的数据源中对应的数据库为db_dev,测试过程中,使用的数据库为db_test,生产环境中使用的数据库为db_prod,三个数据源配置如下面示例(1)中的ProfileConfig类所示:

(1)不使用环境激活

a.数据库配置文件:

db.user=root
db.password=root
db.driverClass=com.mysql.jdbc.Driver

b.配置类ProfileConfig:

/**
 * Created by wangbin33 on 2020/3/8
 */
@Configuration
@ComponentScan({"com.wb.spring.profile"})
@PropertySource({"classpath:db.properties"})
public class ProfileConfig {
   // 数据库用户名
   @Value("${db.user}")
   private String user;
   // 数据库密码
   @Value("${db.password}")
   private String password;
   // 数据库连接驱动
   @Value("${db.driverClass}")
   private String driverClass;
   // 开发环境数据源
   @Bean
   // @Profile("dev") // 标记当前数据源为开发环境
   public DataSource dataSourceDev() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_dev");
      return dataSource;
   }
   // 测试环境数据源
   @Bean
   // @Profile("test") // 标记当前数据源为测试环境
   public DataSource dataSourceTest() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_test");
      return dataSource;
   }
   // 生产环境数据源
   @Bean
   // @Profile("prod") // 标记当前数据源为生产环境
   public DataSource dataSourceProd() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_prod");
      return dataSource;
   }
   // 公共方法
   private void setParams(ComboPooledDataSource dataSource, 
       String jdbcUrl) throws Exception {
      dataSource.setJdbcUrl(jdbcUrl);
      dataSource.setUser(user);
      dataSource.setPassword(password);
      dataSource.setDriverClass(driverClass);
   }
}

c.测试类TestMain:

/**
 * Created by wangbin33 on 2020/3/8.
 */
public class TestMain {
   public static void main(String[] args) {

      ApplicationContext acx = 
       new AnnotationConfigApplicationContext(ProfileConfig.class);

      String[] dataSources = 
           acx.getBeanNamesForType(DataSource.class);
      for(String dataSource : dataSources) {
         System.out.println(dataSource);
      }
   }
}
// 运行结果输出:
dataSourceDev
dataSourceTest
dataSourceProd

可以看出,此时容器中存在着三个环境的数据源。

将上述红色标注的文字放开之后,再次运行测试类,会发现容器中没有任何数据源,因为@Profile如果标注在Bean上之后,只有当前profile的环境被激活之后,才会被注册到Spring容器中。而Profile默认激活的环境为default,即:@Profile(“default”)。而上述分别为”dev”,”test”,”prod”。

(2)通过环境标识激活某一个组件

方法1:使用VM参数的形式,通过指定”-Dspring.profiles.active=环境标识”的方式来激活指定的环境,例如激活dev环境:

在运行之前,先设置好VM参数,如下图:

将上述配置类中红色标注的环境标注信息放开之后,然后运行测试代码,输出结果如下:

dataSourceDev

可见,容器中DataSource类型的组件就只有dataSourceDev被激活了,而且注入到了Spring容器中。

方法2:通过在启动Spring上下文之前,通过设置需要激活的环境标识来激活特定的环境,如下:

/**
 * Created by wangbin33 on 2020/3/8.
 */
public class TestMain {
   public static void main(String[] args) {
      // 1.创建上下文对象,使用无参构造函数
      AnnotationConfigApplicationContext acx = 
        new AnnotationConfigApplicationContext();
      // 2.设置需要激活的环境表示,可以写多个
      //  此处表示同时激活开发环境和测试环境.
      acx.getEnvironment().setActiveProfiles("dev","test");
      // 3.注册配置类
      acx.register(ProfileConfig.class);
      // 4.执行刷新
      acx.refresh();

      String[] dataSources = 
          acx.getBeanNamesForType(DataSource.class);
      for(String dataSource : dataSources) {
         System.out.println(dataSource);
      }
   }
}

将上述配置类中红色标注的环境表示放开之后,然后运行测试代码,输出结果如下:

dataSourceDev
dataSourceTest

从结果中可以看出,dev和test标注的数据源被激活并且被注入到了Spring容器中。

(3)激活某一个配置下的所有组件

@Profile既可以放在@Bean标注的方法上,也可以放在某一个配置类上,当放在某一个配置类上时,表示是否激活当前配置类中的所有Bean组件。

例如,当运行环境为测试环境时,激活ProfileConfig中的所有Bean,配置如下:

a.ProfileConfig配置类:

/**
 * Created by wangbin33 on 2020/3/8.
 */
@Configuration
@ComponentScan({"com.wb.spring.profile"})
@PropertySource({"classpath:db.properties"})
@Profile("test") // 表示当运行环境为测试环境时,激活该配置中的所有Bean组件
public class ProfileConfig {

   @Value("${db.user}")
   private String user;

   @Value("${db.password}")
   private String password;

   @Value("${db.driverClass}")
   private String driverClass;


   @Bean
   public DataSource dataSourceDev() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_dev");
      return dataSource;
   }

   @Bean
   public DataSource dataSourceTest() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_test");
      return dataSource;
   }

   @Bean
   public DataSource dataSourceProd() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_prod");
      return dataSource;
   }

   private void setParams(ComboPooledDataSource dataSource, 
     String jdbcUrl) throws Exception {
      dataSource.setJdbcUrl(jdbcUrl);
      dataSource.setUser(user);
      dataSource.setPassword(password);
      dataSource.setDriverClass(driverClass);
   }
}

b.TestMain测试类:

/**
 * Created by wangbin33 on 2020/3/8.
 */
public class TestMain {
   public static void main(String[] args) {
      // 1.创建上下文对象
      AnnotationConfigApplicationContext acx = 
         new AnnotationConfigApplicationContext();
      // 2.设置需要激活的环境,此处表示激活测试环境.
      acx.getEnvironment().setActiveProfiles("test");
      // 3.注册配置类
      acx.register(ProfileConfig.class);
      // 4.执行刷新
      acx.refresh();

      String[] dataSources = 
         acx.getBeanNamesForType(DataSource.class);
      for(String dataSource : dataSources) {
         System.out.println(dataSource);
      }
   }
}
// 运行结果输出:
dataSourceDev
dataSourceTest
dataSourceProd
(4)激活某一个配置下的所有组件,除了某个组件

当一个配置类下有多个Bean时,如果想激活该配置类下的大多数Bean,但是某些bean不希望被激活,可以通过在配置类上标注需要激活的环境,然后在配置类中的某个不希望被激活的Bean上标注其他的环境标识,例如:希望激活ProfileConfig中的dev和prod对应的dataSource,而忽略test对应的dataSource,可以通过如下的配置:

a.ProfileConfig配置类:

/**
 * Created by wangbin33 on 2020/3/8.
 */
@Configuration
@ComponentScan({"com.wb.spring.profile"})
@PropertySource({"classpath:db.properties"})
@Profile("dev") // 当dev被激活时,未标注Profile标识的bean将会被注入容器
public class ProfileConfig {

   @Value("${db.user}")
   private String user;

   @Value("${db.password}")
   private String password;

   @Value("${db.driverClass}")
   private String driverClass;

   @Bean
   public DataSource dataSourceDev() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_dev");
      return dataSource;
   }

   @Bean
   @Profile("test") // 只有当运行环境为test时,才会被激活
   public DataSource dataSourceTest() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_test");
      return dataSource;
   }

   @Bean
   public DataSource dataSourceProd() throws Exception {
      ComboPooledDataSource dataSource=new ComboPooledDataSource();
      setParams(dataSource, "jdbc:mysql://127.0.0.1:3306/db_prod");
      return dataSource;
   }

   private void setParams(ComboPooledDataSource dataSource, 
     String jdbcUrl) throws Exception {
      dataSource.setJdbcUrl(jdbcUrl);
      dataSource.setUser(user);
      dataSource.setPassword(password);
      dataSource.setDriverClass(driverClass);
   }
}

b.测试类:

/**
 * Created by wangbin33 on 2020/3/8. 
 */
public class TestMain {
   public static void main(String[] args) {
      // 1.创建上下文对象
      AnnotationConfigApplicationContext acx = 
        new AnnotationConfigApplicationContext();
      // 2.设置需要激活的环境表示,可以写多个.
      acx.getEnvironment().setActiveProfiles("dev");
      // 3.注册配置类
      acx.register(ProfileConfig.class);
      // 4.执行刷新
      acx.refresh();

      String[] dataSources = 
         acx.getBeanNamesForType(DataSource.class);
      for(String dataSource : dataSources) {
         System.out.println(dataSource);
      }
   }
}
// 输出结果如下:
dataSourceDev
dataSourceProd

从上述结果中可以看到,标注为test的bean会被自动忽略。

2、Profile使用总结
(1)生效的原则

a.当@Profile作用于某一个具体的Bean上时,只有该对应的环境被激活时,该Bean才会生效,@Profile的默认环境标识为default,即:@Profile(“default”);

b.当@Profile作用在某个配置类上时,当profile对应的环境标识被激活时,该配置类下的所有组件都将被注册到Spring容器中;

c.如果组件中不指定Profile,则任何环境下都可以注册这个组件;

(2)常用的激活方式

a.通过指定VM的启动参数”-Dspring.profiles.active=环境标识” 来生效某一个环境,例如:-Dspring.profiles.active=test,表示生效测试环境,即:标注@Profile(“test”)的Bean会被创建并加入到Spring容器中;

b.通过在创建Spring上下文环境时,指定需要激活的环境标识。通常使用的是无参的构造函数来创建Spring上下文,如下:

① xml方式使用的类:new ClasspathXmlApplicationContext()
② javaConfig方式使用的类:new AnnotationConfigApplicationContext()

至此,Spring中自动装配中的Profile功能介绍完毕,包括Profile的激活原则以及常见的Profile的激活方式。欢迎转发该文章!

温馨提示:如果小程序端代码显示混乱,是因为移动端兼容性导致,可移步至PC端站点查看!

文章属于原创,如果转发请标注文章来源:个人小站【www.jinnianshizhunian.vip

另外提供一些优秀的Java架构师及IT开发视频,书籍资料。无需注册,无需登录即可下载,免费下载地址:https://www.592xuexi.com