• 【Spring】多环境切换


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



    🌟 一、Java配置

    首先创建一个 DataSource 类:

    public class DataSource {
    	private String username;
    	private String password;
    	private String url;
    
    	@Override
    	public String toString() {
    		return "DataSource{" +
    				"username='" + username + '\'' +
    				", password='" + password + '\'' +
    				", url='" + url + '\'' +
    				'}';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    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;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

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

    	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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

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

    🌟 二、XML配置

    
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

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

    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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    🌟 三、原理分析

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

    @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();
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    那我们来看下 ProfileCondition:

    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;
    	}
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    🌟 三、自定义Profile

    首先,先自定义注解:

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    @Conditional(MyCondition.class)
    public @interface MyProfile {
    	String[] value();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

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

    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;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    最后使用自定义注解:

    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;
    	}
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    启动容器:

    	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);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    Java 将HTML转为Word
    计算机组成原理学习笔记:奇偶校验码
    数据结构 Map&Set(搜索)
    WPF 控件专题 Slider控件详解
    在微信小程序中使用 echarts 图片-例 折线图
    Java解决new date出现的时区问题(差8小时)
    IP协议从0到1
    Elasticsearch 查询分析器简介
    24 行为型模式-访问者模式
    [数据结构+算法]关于动态规划dp入门--01背包问题
  • 原文地址:https://blog.csdn.net/m0_46635265/article/details/133082255