写在前面

在实际的企业开发环境中,往往都会将环境分为:开发环境、测试环境和生产环境,而每个环境基本上都是互相隔离的,也就是说,开发环境、测试环境和生产环境是互不相通的。在以前的开发过程中,如果开发人员完成相应的功能模块并通过单元测试后,会通过手动修改配置文件的形式,将项目的配置修改成测试环境,发布到测试环境进行测试。测试通过后,再将配置修改为生产环境,发布到生产环境。这样手动修改配置的方式,一方面增加了开发和运维的工作量,而且总是手工修改各项配置文件很容易出问题。那么,有没有什么方式可以解决这些问题呢?答案是:有!通过@Profile注解就可以完全做到。

关注 冰河技术 微信公众号,回复 “ Spring注解 ” 关键字领取源码。

如果文章对你有所帮助,欢迎大家留言、点赞、在看和转发,大家的支持是我持续创作的动力!

@Profile注解

在容器中如果存在同一类型的多个组件,也可以使用@Profile注解标识要获取的是哪一个bean,这在不同的环境使用不同的变量的情景特别有用。例如,开发环境、测试环境、生产环境使用不同的数据源,在不改变代码的情况下,可以使用这个注解来切换要连接的数据库。

步骤如下:

  1. 在bean上加@Profile注解,其value属性值为环境标识,可以自定义
  2. 使用无参构造方法创建容器
  3. 设置容器环境,其值为第1步设置的环境标识
  4. 设置容器的配置类
  5. 刷新容器

注:2、4、5步其实是带参构造方法的步骤,相当于把带参构造方法拆开,在其中插入一条语句设置容器环境,这些我们可以在Spring的源码中可以看出,比如下面的代码。

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}

接下来,我们再来看下@Profile注解的源码,如下所示。

package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Profiles;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}

注意:@Profile不仅可以标注在方法上,也可以标注在配置类上。如果标注在配置类上,只有在指定的环境时,整个配置类里面的所有配置才会生效。如果一个bean上没有使用@Profile注解进行标注,那么这个bean在任何环境下都会被注册到IOC容器中

环境搭建

接下来,我们就一起来搭建使用@Profile注解实现开发、测试和生产环境的配置和切换的环境。这里,我们以不同的数据源为例。首先,我们在pom.xml文件中添加c3p0和MySQL驱动的依赖,如下所示。

<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>

添加完项目依赖之后,我们在项目中新建ProfileConfig配置类,并在ProfileConfig配置类中模拟开发、测试、生产环境的数据源,如下所示。

package io.mykit.spring.plugins.register.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; /**
* @author binghe
* @version 1.0.0
* @description 测试多数据源
*/
@Configuration
public class ProfileConfig {
@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean("testDataSource")
public DataSource dataSourceTest() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_test");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Bean("prodDataDource")
public DataSource dataSourceProd() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_prod");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
}

这个类相对来说比较简单,其中使用 @Bean("devDataSource")注解标注的是开发环境使用的数据源;使用 @Bean("testDataSource")注解标注的是测试环境使用的数据源;使用@Bean("prodDataDource")注解标注的是生产环境使用的数据源。

接下来,我们创建ProfileTest类,并在ProfileTest类中新建一个testProfile01()方法来进行测试,如下所示。

package io.mykit.spring.test;
import io.mykit.spring.plugins.register.config.ProfileConfig;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
import java.util.stream.Stream;
/**
* @author binghe
* @version 1.0.0
* @description 测试类
*/
public class ProfileTest {
@Test
public void testProfile01(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProfileConfig.class);
String[] names = context.getBeanNamesForType(DataSource.class);
Stream.of(names).forEach(System.out::println);
}
}

运行ProfileTest类的testProfile01()方法,输出的结果信息如下所示。

devDataSource
testDataSource
prodDataDource

可以看到三种不同的数据源成功注册到了IOC容器中,说明我们的环境搭建成功了。

根据环境注册bean

我们成功搭建环境后,接下来,就是要实现根据不同的环境来向IOC容器中注册相应的bean。也就是说,我们要实现在开发环境注册开发环境下使用的数据源;在测试环境注册测试环境下使用的数据源;在生产环境注册生产环境下使用的数据源。此时,@Profile注解就显示出其强大的特性了。

我们在ProfileConfig类中为每个数据源添加@Profile注解标识,如下所示。

@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_test");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}
@Profile("prod")
@Bean("prodDataDource")
public DataSource dataSourceProd() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_prod");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}

我们使用@Profile("dev")注解来标识在开发环境下注册devDataSource;使用@Profile("test")注解来标识在测试环境下注册testDataSource;使用@Profile("prod")注解来标识在生产环境下注册prodDataDource。

此时,我们运行ProfileTest类的testProfile01()方法,发现命令行并未输出结果信息。说明我们为不同的数据源添加@Profile注解后,默认是不会向IOC容器中注册bean的,需要我们根据环境显示指定向IOC容器中注册相应的bean。

换句话说:通过@Profile注解加了环境标识的bean,只有这个环境被激活的时候,相应的bean才会被注册到IOC容器中。

如果我们需要一个默认的环境怎么办呢?

此时,我们可以通过@Profile("default")注解来标识一个默认的环境,例如,我们将devDataSource环境标识为默认环境,如下所示。

@Profile("default")
@Bean("devDataSource")
public DataSource dataSourceDev() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test_dev");
dataSource.setUser("root");
dataSource.setPassword("root");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
return dataSource;
}

此时,我们运行ProfileTest类的testProfile01()方法,输出的结果信息如下所示。

devDataSource

可以看到,我们在devDataSource数据源上使用@Profile("default")注解将其设置为默认的数据源,运行测试方法时命令行会输出devDataSource。

接下来,我们将devDataSource数据源的@Profile("default")注解还原成@Profile("dev")注解,标识它为一个开发环境下注册的数据源。

那么,我们如何根据不同的环境来注册相应的bean呢?

第一种方式就是根据命令行参数来确定环境,我们在运行程序的时候可以添加相应的命令行参数,例如,我们现在的环境是测试环境,那可以在运行程序的时候添加如下命令行参数。

-Dspring.profiles.active=test

第二种方式就是通过AnnotationConfigApplicationContext类的无参构造方法来实现。我们在程序中调用AnnotationConfigApplicationContext的无参构造方法来生成IOC容器,在容器进行初始化之前,我们就为IOC容器设置相应的环境,然后再为IOC容器设置主配置类。例如,我们将IOC容器设置为生产环境,如下所示。

@Test
public void testProfile02(){
//创建IOC容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.getEnvironment().setActiveProfiles("prod");
context.register(ProfileConfig.class);
context.refresh();
String[] names = context.getBeanNamesForType(DataSource.class);
Stream.of(names).forEach(System.out::println);
}

此时,我们运行testProfile02()方法,输出的结果信息如下所示。

prodDataDource

可以看到,命令行输出了prodDataDource,说明我们成功将IOC环境设置为了生产环境。

@Profile不仅可以标注在方法上,也可以标注在配置类上。如果标注在配置类上,只有在指定的环境时,整个配置类里面的所有配置才会生效。例如,我们在ProfileConfig类上标注@Profile("dev")注解,如下所示。

@Profile("dev")
@Configuration
public class ProfileConfig {
/*********代码省略*********/
}

接下来,我们运行testProfile02()方法,发现命令行中未输出任何信息。

这是因为我们在testProfile02()方法中指定了当前的环境为生产环境,而ProfileConfig类上标注的注解为@Profile("dev"),说明ProfileConfig类中的所有配置只有在开发环境下才会生效。所以,此时没有任何数据源注册到IOC容器中,命令行不会打印任何信息。

重磅福利

关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!!

好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!!

写在最后

如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫!

【Spring】使用@Profile注解实现开发、测试和生产环境的配置和切换,看完这篇我彻底会了!!的更多相关文章

  1. Spring.profile实现开发、测试和生产环境的配置和切换

    软件开发过程一般涉及“开发 -> 测试 -> 部署上线”多个阶段,每个阶段的环境的配置参数会有不同,如数据源,文件路径等.为避免每次切换环境时都要进行参数配置等繁琐的操作,可以通过spri ...

  2. Spring.profile配合Jenkins发布War包,实现开发、测试和生产环境的按需切换

    前两篇不错 Spring.profile实现开发.测试和生产环境的配置和切换 - Strugglion - 博客园https://www.cnblogs.com/strugglion/p/709102 ...

  3. Docker标准化开发测试和生产环境

    对于大部分企业来说,搭建 PaaS 既没有那个精力,也没那个必要,用 Docker 做个人的 sandbox 用处又小了点. 可以用 Docker 来标准化开发.测试.生产环境. Docker 占用资 ...

  4. IDEA开发、测试、生产环境pom配置及使用

    pom文件 一般放在最下面,project里 <!--开发环境.测试环境.生产环境--> <!--生产环境--> <profiles> <profile> ...

  5. Spring Boot (二)集成Jsp与生产环境部署

    一.简介 提起Java不得不说的一个开发场景就是Web开发,也是Java最热门的开发场景之一,说到Web开发绕不开的一个技术就是JSP,因为目前市面上仍有很多的公司在使用JSP,所以本文就来介绍一下S ...

  6. 分离Webpack开发环境与生产环境的配置

    这是Webpack+React系列配置过程记录的第五篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...

  7. Mysql快速入门(看完这篇能够满足80%的日常开发)

    这是一篇mysql的学习笔记,整理结合了网上搜索的教程以及自己看的视频教程,看完这篇能够满足80%的日常开发了. 菜鸟教程:https://www.runoob.com/mysql/mysql-tut ...

  8. maven项目打包部署到虚拟机测试和生产环境上及查看日志操作

    调试通过后提交代码到gitlab,打包部署到相应环境(测试或生产环境)步骤一样1.打包在要打包的项目上右键run as maven clean 清除原来的包,然后run as maven instal ...

  9. 【译】Spring 4 @Profile注解示例

    前言 译文链接:http://websystique.com/spring/spring-profile-example/ 本文将探索Spring中的@Profile注解,可以实现不同环境(开发.测试 ...

随机推荐

  1. __new__方法理解

    class Foo(object): def __init__(self, *args, **kwargs): pass def __new__(cls, *args, **kwargs): retu ...

  2. 因为不知道Java的CopyOnWriteArrayList,面试官让我回去等通知

    先看再点赞,给自己一点思考的时间,微信搜索[沉默王二]关注这个靠才华苟且的程序员.本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章. ...

  3. Python 字典(Dictionary) cmp()方法

    Python 字典(Dictionary) cmp()方法 描述 Python 字典的 cmp() 函数用于比较两个字典元素.高佣联盟 www.cgewang.com 语法 cmp()方法语法: cm ...

  4. Blob分析之bottle.hdev

    * 分割读取啤酒瓶子上的数字* bottle.hdev: Segment and read numbers on a beer bottle* 第0步* Step 0: Preparations*定义 ...

  5. react - 多层级嵌套路由支持

    routerCofing配置 { path: '/route1/someModel', children: [ { path: '/route2', component: RouteBase.Cps1 ...

  6. 构建自己的js库

    一.背景 web前端开发人员经常会用到一些现成的js库(框架).框架的使用增加了代码的模块化和可复用性,最主要的是屏蔽了浏览器之间差异性的实现,使得代码更加简洁,框架使用者只需要将注意力放在业务的实现 ...

  7. python 创建字典以及操作字典----这是基础知识

    当你编程久了,发现所有的东西都是建立在基础之上的,庞大的代码 你要识别出它的类型是什么 或者返回后类型是什么!? 根据返回的类型 或者需要操作的对象是什么类型  就可以选择相应的方法进行处理 #创建字 ...

  8. 面试官:怎么做JDK8的内存调优?

    面试官:怎么做JDK8的内存调优? 看着面试官真诚的眼神,心中暗想看起来年纪轻轻却提出如此直击灵魂的问题.擦了擦额头上汗,我稍微调整了一下紧张的情绪,对面试官说: 在内存调优之前,需要先了解JDK8的 ...

  9. Netty之旅:你想要的NIO知识点,这里都有!

    高清思维导图原件(xmind/pdf/jpg)可以关注公众号:一枝花算不算浪漫 回复nio即可.(文末有二维码) 前言 抱歉好久没更原创文章了,看了下上篇更新时间,已经拖更一个多月了. 这段时间也一直 ...

  10. 准确率、精确率、召回率、F1

    在搭建一个AI模型或者是机器学习模型的时候怎么去评估模型,比如我们前期讲的利用朴素贝叶斯算法做的垃圾邮件分类算法,我们如何取评估它.我们需要一套完整的评估方法对我们的模型进行正确的评估,如果模型效果比 ...