专栏首页技术墨客Spring核心——Profile管理环境 原

Spring核心——Profile管理环境 原

抽象环境的概念

在介绍Spring核心模块为运行环境管理提供的功能之前,咱们先得解释清楚“运行环境”是什么。

码砖早年,对上下文(Context)、环境(Environment)一直都是傻傻分不清楚,感觉2者都是放了一堆参数在里面,貌似并没有多大区别。后来才慢慢摸清楚这2个词的套路。上下文(Context)是用来处理分层传递的,不清楚的可以看看上下文与IoC一文关于ApplicationContext的介绍。

而环境(Environment)是指当前运行程序之外的各种“全局变量”,这些变量反映了当前软件运行的各种外部情况。例如我们执行System.getenv()方法,就会获取到当前包括操作系统、全局路径配置、磁盘、jdk版本等等信息。这些信息实际上与当前运行的程序是无关的——无论你是否启动JVM,这些环境变量都是客观存在的。

既然环境的作用是体现当前运行的各种外部情况,那么除了JVM启动时提供的固定参数,也可以指定我们需要的环境变量。例如我们最常见的环境——开发环境、测试环境、集成QA环境、仿真环境、生产环境等。

Profile特性

对于软件开发而言经常要控制的就是当前程序是在开发环境运行还是在生产环境运行。除了后面要介绍的Spring Profile功能,还有各种各样的方法来进行控制,比如Maven的profile标签。Spring Profile只是一种环境控制的参考手段,他的好处是可以在代码级别去控制,具体使用什么根据项目的需要去考量。

Spring的Profile特性使用起来并不复杂,而且同时支持Java注解和XML配置。我们通过几段代码来说明如何使用Profile。

纯Java常规使用

(以下案例的可执行代码请到gitee下载,)

定义一个servuce接口和三个service的实现类:

package chkui.springcore.example.hybrid.profile.service;

public interface Blizzard {
	String getName();
}
package chkui.springcore.example.hybrid.profile.service.blizzard;
class Warcraft implements Blizzard {
	public String getName() {
		return "Warcraft";
	}

}
class WorldOfWarcraft implements Blizzard {
	public String getName() {
		return "World of Warcraft";
	}

}
class Overwatch implements Blizzard {
	public String getName() {
		return "Overwatch";
	}
}

 然后我们通过纯Java配置讲接口的每个实现添加到容器中:

@Configuration
public class EnvironmentApp {
	public static void main(String[] args) {
		//在启动容器之前,先指定环境中的profiles参数
		System.setProperty("spring.profiles.active", "wow");
		ApplicationContext ctx = new AnnotationConfigApplicationContext(EnvironmentApp.class);
        //当前的profile值是wow,所以获取的实现类是worldOfWarcraft
		Blizzard blizzard = ctx.getBean(Blizzard.class);
	}
	
	@Bean
	@Profile("war")
	public Blizzard warcraft() {
		return new Warcraft();
	}
	
	@Bean
	@Profile("wow")
	public Blizzard worldOfWarcraft() {
		return new WorldOfWarcraft();
	}
	
	@Bean
	@Profile("default")
	public Blizzard overwatch() {
		return new Overwatch();
	}
}

@Configuration类中每一个@Bean注解之后都有一个@Profile注解。@Profile中的字符串就标记了当前适配的环境变量,他配合System.setProperty("spring.profiles.active", "wow");这一行一起使用。当设定环境参数为wow时,标记了@Profile("wow")的方法会被启用,对应的Bean会添加到容器中。而其他标记的Bean不会被添加,当没有适配到任何Profile值时,@Profile("default")标记的Bean会被启用。

Spring Profile的功能就是根据在环境中指定参数的方法来控制@Bean的创建。

在@Configuration上配置Profile

@Profile注解除了在@Bean方法上使用,也可以用于@Configuration类上。这样使用可以一次性控制多个Bean的加载。例如下面的例子:

@Configuration
@Profile("cast")
class CastConfig {
	@Bean
	public Castlevania castlevania() {
		return new Castlevania();
	}
}

@Configuration
@Profile("pes")
class PESConfig {
	@Bean
	public ProEvolutionSoccer proEvolutionSoccer() {
		return new ProEvolutionSoccer();
	}
}

这样可以控制整个@Configuration类中的Bean是否加载。这个时候如果在@Configuration类上还标注了@Import注解,那么被@Import引入的类中的@Bean也不会添加到IoC容器中,那么这对统一配置环境是很有好处的。

需要注意的是,如果这个时候又在@Bean之上添加了@Profile注解,那么Spring最终会根据@Bean之上的标签来执行。例如:

@Configuration
@Profile("cast")
class CastConfig {
	@Bean
	public Castlevania castlevania() {
		return new Castlevania();
	}
	@Bean
    @Profile("pes")
	public ProEvolutionSoccer proEvolutionSoccer() {
		return new ProEvolutionSoccer();
	}
}

当环境中的profile值包含"pes"时候,@Profile("pes")标注的这个Bean就会添加到IoC容器中。

Profile的XML配置

Profile特性也可以在XML配置。不过只能在<beans>标签上进行:

<beans ... >
	<beans profile="ff">
		<bean class="chkui.springcore.example.hybrid.profile.service.squareenix.FinalFantasy" />
	</beans>
	<beans profile="dog">
		<bean class="chkui.springcore.example.hybrid.profile.service.squareenix.SleepingDogs" />
	</beans>
</beans>

配置之后,<beans>中的多个<bean>都会被Profile控制。

环境变量的设置

Profile的环境变量可以包含多个值。例如:

System.setProperty("spring.profiles.active", "wow,pes");

这样环境中就包含了2个Profile的值。对用的@Profile或profile配置就会被启用。

除了例子中给出的System::setProperty方法,Spring还提供了多种方法来设置Profile的环境变量。

直接在Jvm启动参数中设置

-Dspring.profiles.active="wow,pes"

使用EnvironmentCapable接口来设置

ConfigurableApplicationContext继承了ConfigurableEnvironment接口我们可以通过ConfigurableEnvironment::getEnvironment方法获取到当前Spring中的环境对象——org.springframework.core.env.Environment,然后使用他来设置环境变量:

ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(EnvironmentApp.class);
ConfigurableEnvironment env = ctx.getEnvironment();
//通过setActiveProfiles来设置。
env.setActiveProfiles("wow","pes","ff"); 
//必须重建容器
ctx.refresh();

需要注意的是,在继承关系中ConfigurableApplicationContext之后才实现ConfigurableEnvironment,如果这里使用ApplicationContext::getEnvironment方法得到的是Environment,它不提供set相关的方法。所以上面的例子使用了ConfigurableApplicationContext。由于ApplicationContext的所有实现类都实现了Configurable的功能,我们也可以像下面这样进行转型:

ApplicationContext ctx = new AnnotationConfigApplicationContext(EnvironmentApp.class);
Environment _e =ctx.getEnvironment();
ConfigurableEnvironment env = ConfigurableEnvironment.class.cast(_e);

@Profile的实现

Profile特性的实现也不复杂,其实就是实现了Conditional功能(Conditional功能见@Configuration与混合使用一文中关于Conditionally的介绍)。

首先@Profile注解继承实现了@Conditional:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {}

然后他的处理类实现了Condition接口:

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

}

处理过程也很简单,实际上就检查@Profile注解中的值,如果和环境中的一致则添加。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • React中的模式对话框 转

    在16.x版本之后React提供了Protals功能来解决模式对话框不在Dom根节点导致的一些BUG。除了Protal还有更多的方法去解决这些问题,本文来自Da...

    随风溜达的向日葵
  • Spring核心——IOC处理器扩展 原

    Spring一直标注自己是一个非侵入式框架。非侵入式设计的概念并不新鲜,目标就是降低使用者和框架代码的耦合,毕竟框架的开发者和使用者几乎肯定不是同一个团队。Sp...

    随风溜达的向日葵
  • React 深入说明JSX语法与Props特性

    我们可以将JSX理解为React.createElement(component, props, ...children)方法的语法糖。JSX的代码:

    随风溜达的向日葵
  • [原创]web application中使用Profile应该注意的问题

    1.如何在web application中正确使用Profile web application与website的一个不同之处在于,web applicat...

    菩提树下的杨过
  • ASP.NET Core的配置(4):多样性的配置来源[中篇]

    我们在本篇文章中会介绍三种针对物理文件的ConfiguationProvider,它们分别是针对JSON文件的JsonConfiguationProvider,...

    蒋金楠
  • 什么是消息队列?

    消息队列不知道大家看到这个词的时候,会不会觉得它是一个比较高端的技术,反正我是觉得它好像是挺牛逼的。

    mafeifan
  • 什么是消息队列?

    公司用到的很多技术,自己之前都没学过(尬),于是只能慢慢补了。这次给大家写写我学习消息队列的笔记,希望对大家有帮助。

    用户4447430
  • Kotlin Array 创建、增、删、改、查、插入

    码脑
  • hadoop系列之深入优化

    五、MapReduce的优化 1、 操作系统调优 增大打开文件数据和网络连接上限,调整内核参数net.core.somaxconn,提高读写速度和网络带宽使用率...

    Spark学习技巧
  • centos7搭建hadoop3.*.*系列

     最近搭建这个hadoop踩过不少坑,先是配置JDK搞错路径(普通用户和root用户下的路径不同),再就是hadoop版本不同导致的启动错误,网上找到的是ha...

    爱学习的孙小白

扫码关注云+社区

领取腾讯云代金券