【Spring】多环境切换

news/2024/5/20 5:08:06 标签: spring, java, 后端, gradle, spring boot

🎈博客主页:🌈我的主页🌈
🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏
🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩🚩🚩
🎈由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!🌠个人主页


目录

  • 🌟 一、Java配置
  • 🌟 二、XML配置
  • 🌟 三、原理分析
  • 🌟 三、自定义Profile


🌟 一、Java配置

首先创建一个 DataSource 类:

java">public class DataSource {
	private String username;
	private String password;
	private String url;

	@Override
	public String toString() {
		return "DataSource{" +
				"username='" + username + '\'' +
				", password='" + password + '\'' +
				", url='" + url + '\'' +
				'}';
	}
}

向 Spring 容器中注册多个 DataSource,并且在注册的时候设置 Profile,只有当条件满足的时候,才向 Spring 容器中注册相应的 Bean,否则不注册

java">public class JavaConfig {
	@Bean
	@Profile("dev")
	DataSource devDataSource(){
		DataSource dev = new DataSource();
		dev.setUsername("dev");
		dev.setPassword("dev");
		dev.setUrl("jdbc:mysql:3306/dev");
		return dev;
	}

	@Bean
	@Profile("prod")
	DataSource prodDataSource(){
		DataSource prod = new DataSource();
		prod.setUsername("prod");
		prod.setPassword("prod");
		prod.setUrl("jdbc:mysql:3306/prod");
		return prod;
	}

}

这里到底向 Spring 容器中注册多少个 DataSource,主要还是看条件是否满足,如果有多个条件满足,那么就注册多个 DataSource 实例到 Spring 容器中,否则就不注册。
最后,启动容器:

java">	public static void main(String[] args) {
		//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
		//此时,我们还没设置当前系统环境
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
		//此时,我们还没设置当前系统环境
		ConfigurableEnvironment evn = ctx.getEnvironment();
		evn.setActiveProfiles("prod");
		//此时再去设置配置类
		ctx.register(JavaConfig.class);
		//开始初始化容器
		ctx.refresh();
		DataSource dataSource = ctx.getBean(DataSource.class);
		System.out.println(dataSource);
	}

启动容器的时候,一开始不要传配置类进去,如果传了,则 refresh 方法会被自动调用,而此时我们还没设置容器的环境!所以,使用无参构造方法创建 AnnotationConfigApplicationContext 对象,然后设置系统环境,再去设置配置类,最后再去初始化容器

🌟 二、XML配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	<beans profile="dev">
		<bean class="com.dong.demo3.DataSource" >
			<property name="username" value="dev"/>
			<property name="password" value="dev"/>
			<property name="url" value="jdbc:mysql:3306/dev"/>
		</bean>
	</beans>
	<beans profile="prod">
		<bean class="com.dong.demo3.DataSource" >
			<property name="username" value="prod"/>
			<property name="password" value="prod"/>
			<property name="url" value="jdbc:mysql:3306/prod"/>
		</bean>
	</beans>
</beans>

这个是在 父标签 beans 中设置条件,满足对应的条件,父标签中的所有 Bean 才会被初始化

java">public class XMLDemo1 {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
		ctx.getEnvironment().addActiveProfile("prod");
		ctx.setConfigLocation("beans1.xml");
		ctx.refresh();
		DataSource bean = ctx.getBean(DataSource.class);
		System.out.println(bean);
	}
}

🌟 三、原理分析

@Profile注解的定义就是一个组合注解:

java">@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 从这里可以看到,本质上就是一个条件注解,条件则是 ProfileCondition
@Conditional(ProfileCondition.class)
public @interface Profile {

	/**
	 * The set of profiles for which the annotated component should be registered.
	 */
	String[] value();

}

那我们来看下 ProfileCondition:

java">class ProfileCondition implements Condition {

	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		// 这句话实际上就是查询到 Profile 注解的所有属性
		// 对于 Profile 注解来说,实际上就只有 value 属性
		// value->dev   value->prod
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
		if (attrs != null) {
			// attrs.get("value") 表示读取属性中的 value 属性,value 属性的值是一个数组,所以这里需要遍历
			for (Object value : attrs.get("value")) {
				//判断当前系统环境中是否包含这个 value
				if (context.getEnvironment().matchesProfiles((String[]) value)) {
					return true;
				}
			}
			return false;
		}
		return true;
	}

}

🌟 三、自定义Profile

首先,先自定义注解:

java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Conditional(MyCondition.class)
public @interface MyProfile {
	String[] value();
}

然后自定义条件,在自定义条件中解析注解:

java">public class MyCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(MyProfile.class.getName());
		if(attrs != null){
			List<Object> list = attrs.get("value");
			for (Object value : list) {
				boolean b = context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value));
				if(b){
					return true;
				}
			}
		}
		return false;
	}
}

最后使用自定义注解:

java">public class JavaConfig {
	@Bean
	@MyProfile("dev")
    DataSource devDataSource(){
		DataSource dev = new DataSource();
		dev.setUsername("dev");
		dev.setPassword("dev");
		dev.setUrl("jdbc:mysql:3306/dev");
		return dev;
	}

	@Bean
	@MyProfile("prod")
    DataSource prodDataSource(){
		DataSource prod = new DataSource();
		prod.setUsername("prod");
		prod.setPassword("prod");
		prod.setUrl("jdbc:mysql:3306/prod");
		return prod;
	}

}

启动容器:

java">	public static void main(String[] args) {
		//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
		//此时,我们还没设置当前系统环境
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
		//此时,我们还没设置当前系统环境
		ConfigurableEnvironment evn = ctx.getEnvironment();
		evn.setActiveProfiles("prod");
		//此时再去设置配置类
		ctx.register(JavaConfig.class);
		//开始初始化容器
		ctx.refresh();
		DataSource dataSource = ctx.getBean(DataSource.class);
		System.out.println(dataSource);
	}

http://www.niftyadmin.cn/n/5045997.html

相关文章

oCPC实践录 | oCPC转化的设计、选择、归因与成本设置(3)

上周抽时间参加了某培训机构的公开课《搜索oCPC拓量与线索质量优化》&#xff0c;想了解真正一线的优化师们是怎么使用oCPC的。课程中有两点值得学习&#xff1a;一是计算驱动&#xff0c;分析全链路营销漏斗各个环节&#xff0c;明确关键卡点&#xff0c;再调整投放动作&#…

基于MUSIC算法的二维超声波成像matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、基本原理 4.2、数学公式 4.3、实现过程 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..........................................…

【虚拟化】虚拟机vcpu绑核物理机

文章目录 一、NUMA二、虚拟机xml配置&#xff08;重点&#xff09; 参考文章 第一篇&#xff1a;KVM虚拟化CPU技术总结 第二篇&#xff1a;虚机cpu和mem的配置&#xff08;cputune和numatune&#xff09; 第三篇&#xff1a;libvirt 中cpu, numa 的配置 第四篇&#xff1a;如何…

Oracle分区的使用详解:创建、修改和删除分区,处理分区已满或不存在的插入数据,以及分区历史数据与近期数据的操作指南

一、前言 什么是表分区: Oracle的分区是一种将表或索引数据分割为更小、更易管理的部分的技术。它可以提高查询性能、简化维护操作,并提供更好的数据组织和管理。 表分区和表空间的区别和联系: 在Oracle数据库中,表空间(Tablespace)是用于存储表、索引和其他数据库对…

有名管道及其应用

创建FIFO文件 1.通过命令&#xff1a; mkfifo 文件名 2.通过函数: mkfifo #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数&#xff1a; -pathname&#xff1a;管道名称的路径 -mode&#xff1a;文件的权限&a…

NodeRed拖拉拽实现OPCUA数据订阅,发布至MQTT并落库MySQL

背景 几年前曾根据 Node-Red 官网示例进行了简单的体验&#xff0c;当时觉得这东西就是个玩具&#xff0c;拿过来玩一玩可以&#xff0c;不实用&#xff1b;但是如今发现有不少产品对其进行了集成&#xff0c;并做出了复杂的商业应用&#xff0c;这确实是极大的震撼。 使用看似…

CSDN热榜分析3:实现领域热榜的爬取

文章目录 领域热榜爬取函数领域热榜按钮功能实现 热榜分析系列&#xff1a; CSDN热榜分析&#x1f525; UI界面&#x1f525; 领域热榜 领域热榜爬取函数 CSDN热榜共有21个子领域&#xff0c;包括C、云原生、人工智能、前沿技术、软件工程、后端、Java、JavaScript、PHP、P…

19异常的学习笔记

异常 很重要&#xff0c;有利于我们平时处理问题 异常就是代表程序出现了问题 常见的异常比如说 数组越界除法除0 异常的体系是什么 java.lang.Throwable Error Exception RuntimeException 其他异常 Error 代表的是系统级别的错误&#xff0c;也就是一旦系统出现问题&…