@Profile使用场景

2023-11-07

前言

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

一、@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注解使用

1. 环境搭建

接下来,我们一起来搭建使用@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配置类中模拟开发、测试、生产环境的数据源,如下所示。

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

/**
 * @author 
 * @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()方法来进行测试,如下所示。

import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import javax.sql.DataSource;
import java.util.stream.Stream;

/**
 * @author 
 * @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容器中,说明我们的环境搭建成功了。

2. 根据环境注册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容器中,命令行不会打印任何信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@Profile使用场景 的相关文章

  • 如何在Java 8中实现Elvis运算符?

    我有一个经典的 Elvis 运算符 案例 其中我调用每个可能返回 null 的方法并将它们链接在一起 thing nullableMethod1 a nullableMethod2 b nullableMethod3 在 Java 8 中
  • Javadoc 1.5 和 1.6 中缺少 enum.valueOf(String name)

    这可能是一个愚蠢的问题 但我正在使用该方法enum valueOf String name 那里没问题 只是当我检查 javadoc 以了解有关此方法的更多信息时 我找不到它 有javadoc用于valueOf Class
  • 无法实例化接收器 com.parse.GcmBroadcastReceiver

    我正在编写一个使用 GCM 通知和解析推送的离子应用程序 这个应用程序正在使用这些插件 com ionic keyboard 1 0 3 Keyboard com phonegap plugins PushPlugin 2 4 0 Push
  • 使用除 SINGLE_TABLE 之外的任何其他 Hibernate 继承策略时 JVM 崩溃

    好吧 这可能不太可能 但还是这样吧 在Java JRE 1 6 0 26 b03 中我有两个类 SuperControl及其子类SubControl 它们都需要是持久对象 我正在使用 Hibernate Annotations 来实现这一点
  • 在 Hibernate 中创建 UPDATE RETURNING 查询

    在 Oracle 中 我们可以创建一个更新查询 该查询将使用 RETURNING 子句返回更新的记录 Hibernate中有类似的功能吗 除了数据库生成的值之外 Hibernate 显然不需要返回更新的实例 因为对象传递给Session s
  • 用 java 编写解释器时的 switch 或 if 语句

    当前的作业需要我编写一个程序 以一种非常微小且基本的编程语言 行为有点像 FORTRAN 来读取包含指令的文件并执行这些指令 基本上它是我猜的语言的简单解释器 它是完全线性的 所有语句都是按顺序定义的 并且只有字符串和整数变量 我需要查找和
  • Java 唤醒休眠线程

    我阅读了其他帖子 但没有找到我正在寻找的确切答案 所以我希望有人能给出一些澄清 我有一个将运行一段时间的程序 我有一些在后台运行的线程来执行各种任务 为了简单起见 让我们考虑 3 个线程 ThreadA每 10 秒执行一次任务 其中Thre
  • 支持通过 OAuth 进行 Facebook/Twitter 身份验证的 CAS 服务器

    我正在寻找一个支持 Facebook Twitter 通过 OAuth 进行单点登录身份验证的 CAS 服务器 我检查过 JASIG CAS 服务器 但它看起来不支持它们 我的 java web 应用程序基于 Spring Security
  • C# 中的协变和逆变

    首先我要说的是 我是一名正在学习 C 编程的 Java 开发人员 因此 我会将我所知道的与我正在学习的进行比较 我已经使用 C 泛型几个小时了 我已经能够在 C 中重现我在 Java 中知道的相同内容 除了几个使用协变和逆变的示例 我正在读
  • Java元数据读写

    是否可以以通用方式 对于所有图像类型 在 Java 中读取和写入元数据 我找到了一些示例 但它们总是特定的 例如 JPEG 或 PNG 我需要一些足够通用的东西 而不是到处都有 if else 语句 我不想重写源代码 但这是一个很好的例子
  • Java:使用 Java.util.concurrent 线程访问读取线程串行端口

    我正在尝试编写一个 Java 串行设备驱动程序并想使用 对我来说是新的 java util concurrent包裹 我有一种发送数据包然后等待 ACK 的方法 我打算有炭 接收在不同的线程中运行 如果接收线程收到 ACK 它应该使用发送数
  • 使用Java开发跨平台,不同平台字体缩放不同

    我正在为我的大学制作一些软件 需要一个 GUI 在它的第一个版本中 我让它使用系统外观 因此它看起来像 Linux Mac Windows 中的本机应用程序 我发现这很麻烦 因为我必须根据操作系统使所有 JLabel 具有不同的大小 无论分
  • 我们可以使用 for-each 循环来迭代 Iterator 类型的对象吗? [复制]

    这个问题在这里已经有答案了 如果我们执行以下操作 我们会收到错误 class FGH public static Iterator reverse List list Collections reverse list return list
  • 在服务器内部调用 Web 服务

    我有一个网络服务 getEmployee 当传递 id 时 它会获取单个员工的员工详细信息 同一服务器上的另一个 Web 服务 getEmployeeList 当传递一个部门时 它会获取整个员工列表 这将获取部门的 ID 然后调用 getE
  • 从命令行运行 Maven 插件的语法是什么。

    我看到这里已经有人问过这个问题 如何从命令行执行maven插件 https stackoverflow com questions 12930656 how to execute maven plugin from command line
  • 方法签名中带或不带synchronized关键字的方法具有相同的字节码

    对于以下 2 个类 获得相同的 Java 字节码 java版本 java 版本 1 8 0 181 Java TM SE 运行时环境 构建 1 8 0 181 b13 Java HotSpot TM 64 位服务器 VM 内部版本 25 1
  • 编写自定义 Eclipse 调试器

    EDIT 一定有某种方法可以解决这个问题 而无需编写全新的调试器 我目前正在研究在现有 java 调试器之上构建的方法 如果有人对如何获取 Java 调试器已有的信息 有关堆栈帧 变量 原始数据等 有任何想法 那将非常有帮助 我想要做的是我
  • Integer.parseInt 引发的 NumberFormatException

    嘿 我在学校上编码课 但老师没有很好地解释 所以我们必须在网上查找我所做的信息 但我无法找到代码中的错误 你能帮我吗 char end s do System out println Tipo de boleto char boleto c
  • 如何使用剪辑来减少绘画时间?

    我正在尝试使用 Clip 来减少 CPU 负载 但剪辑在屏幕上留下了一些我似乎无法摆脱的垃圾 另外 打开和关闭剪辑似乎对 CPU 负载没有影响 在任一情况下 大部分时间似乎都花在重绘管理器和绘制缓冲图像上 import static jav
  • 我找不到 IntelliJ 快捷方式

    我使用 vim 一段时间 我知道有一个 intellij vim 插件 我很好奇内置的 IntelliJ 文本导航存在什么 如何打开实时模板来创建模板 如何查看以 tr 开头的现有模板列表 如何进行全局搜索并在当前文档中进行搜索 然后转到下

随机推荐

  • windows凭据密码怎么查看_管理Windows访问凭证,快速访问局域网上的共享资源

    内部网访问其他电脑的共享资源 基本上需要输入访问对方电脑资源允许的账号和密码 在第一次的访问中选择保存凭据后 以后访问就不要输入相应的账号和密码了 但也会出现因修改相关的访问密码或者取消了访问账号的改变 这样就会出现凭据失效的情况 下面介绍
  • 类似-Xms、-Xmn这些参数的含义:

    类似 Xms Xmn这些参数的含义 答 堆内存分配 JVM初始分配的内存由 Xms指定 默认是物理内存的1 64 JVM最大分配的内存由 Xmx指定 默认是物理内存的1 4 默认空余堆内存小于40 时 JVM就会增大堆直到 Xmx的最大限制
  • Python通过ARIMA模型进行时间序列分析预测

    ARIMA模型预测 时间序列分析预测就是在已有的和时间有关的数据序列的基础上构建其数据模型并预测其未来的数据 例如航空公司的一年内每日乘客数量 某个地区的人流量 这些数据往往具有周期性的规律 如下图所示 有的数据呈现出简单的周期性循环 有的
  • Linux嵌入式学习---c语言之循环结构

    Linux嵌入式学习 c语言之循环结构 一 while语句循环 1 1一般形式 1 2累加求和 二 do while语句循环 2 1do while语句一般形式 2 2do while语句特点 三 for语句循环 3 1for语句的一般形式
  • vue-resource请求数据的使用方法

    vue resource vue js关于客户端请求数据的官方插件 使用vue resource请求数据的步骤 1 安装vue resource插件 记得添加 save 若安装淘宝镜像用cnpm npm install vue resour
  • [蓝桥杯2023初赛] 整数删除

    给定一个长度为 N 的整数数列 A1 A2 AN 你要重复以下操作 K 次 每次选择数列中最小的整数 如果最小值不止一个 选择最靠前的 将其删除 并把与它相邻的整数加上被删除的数值 输出 K 次操作后的序列 输入格式 第一行包含两个整数 N
  • vscode:visual studio code 调试php

    简介 php是动态语言没有调试器的话排错起来很是麻烦 vscode可以说是程序员的福音 启动速度快 插件越来越多 跨平台 现在说一下vscode上调试php文件 所需文件 xampp 集成服务器 vscode Xdebug php debu
  • Rightware的Kanzi界面很快你的全液晶汽车仪表盘

    锋影 e mail 174176320 qq com 这是一个屏幕在行动的Kanzi UI编辑器 这是说 汽车仪表板没有显著在过去的几十年里发展公平 不知怎么的 我觉得喜欢的东西是会改变的 但什么也没做 至少在一个大的方式 当日产GTR天际
  • 面试必问的Spring IoC与Spring AOP面试题,你能get到几问?

    Spring IoC Q1 IoC 是什么 Q2 IoC 容器初始化过程 Q3 依赖注入的实现方法有哪些 Q4 依赖注入的相关注解 Q5 依赖注入的过程 Q6 Bean 的生命周期 Q7 Bean 的作用范围 Q8 如何通过 XML 方式创
  • (含源码)「自然语言处理(NLP)」RoBERTa&&XLNet&&语言模型&&问答系统训练

    来源 AINLPer 微信公众号 每日更新 编辑 ShuYini 校稿 ShuYini 时间 2020 07 27 引言 本次内容主要包括 稳健优化Bert模型 RoBERTa 自回归预训练模型 XLNet 无监督多任务学习语言模型 生成预
  • 【BT 协议】HFP 协议

    原文链接 https blog csdn net shichaog article details 52123439 HFP Hands free Profile 让蓝牙设备可以控制电话 如接听 挂断 拒接 语音拨号等 拒接 语音拨号要视蓝
  • C++:智能指针及其实现原理

    更多C 知识点 C 目录索引 1 RAII思想 定义一个类来封装资源的分配与释放 构造函数中完成资源的分配及初始化 析构函数中完成资源的清理 可以保证资源的正确初始化和释放 如果对象是用声明的方式在栈上创建局部对象 那么RAII机制就会正常
  • 从 MySQL 到 OBOracle:如何处理自增列?

    业务需要将数据库转换为 OceanBase 数据库 但源端涉及到 Oracle 及 MySQL 两种不同数据库 需要合并为 OceanBase 中单一的 Oracle 模式 其中源端 MySQL 数据库需要改造为 OB Oracle 并做异
  • 天梯题集——复数四则运算(fabs)

    复数四则运算 include
  • K8S kube-proxy- iptable模式实现原理分析

    每台机器上都运行一个kube proxy服务 它监听api server 和endpoint变化情况 维护service和pod之间的一对多的关系 通过iptable或者ipvs为服务提供负载均衡的能力 通常kube proxy作为deem
  • mysql auto reconnect_Python mysql (using pymysql) auto reconnect

    I m not sure if this is possible but I m looking for a way to reconnect to mysql database when the connection is lost Al
  • 手把手教你解决二维数组旋转问题

    一 背景 最近做算法题发现有些题目都需要将一个数组顺时针或逆时针旋转 之前发的题解中也涉及到过这类题 但没有细讲 在这里讲一下思路 手把手带你找到对应关系 本文代码示例均使用java 但重要的是思路 思路 思路 与语言无关 如果大家不明白我
  • 大厂怎么做Code Review?

    发现坏味道的实践 就是Code Review 对计算机源代码系统化地审查 常用软件同行评审的方式进行 其目的是在找出及修正在软件开发初期未发现的错误 提升软件质量及开发者的技术 团队对 CR本身的理解有差异 有的团队 在一个完整的开发周期结
  • Java中的内存分配

    Java把内存划分成两种 一种是栈内存 一种是堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配 当在一段代码块定义一个变量时 Java就在栈中为这个变量分配内存空间 当超过变量的作用域后 Java会自动释放掉为
  • @Profile使用场景

    文章目录 前言 一 Profile注解介绍 二 Profile注解使用 1 环境搭建 2 根据环境注册bean 前言 在实际的企业开发环境中 往往都会将环境分为 开发环境 测试环境和生产环境 而每个环境基本上都是互相隔离的 也就是说 开发环