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常规使用

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

packagechkui.springcore.example.hybrid.profile.service;publicinterfaceBlizzard{StringgetName();}

packagechkui.springcore.example.hybrid.profile.service.blizzard;classWarcraftimplementsBlizzard{publicStringgetName(){return"Warcraft";}}classWorldOfWarcraftimplementsBlizzard{publicStringgetName(){return"World of Warcraft";}}classOverwatchimplementsBlizzard{publicStringgetName(){return"Overwatch";}}

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

@ConfigurationpublicclassEnvironmentApp{publicstaticvoidmain(String[] args){//在启动容器之前,先指定环境中的profiles参数System.setProperty("spring.profiles.active","wow");ApplicationContext ctx =newAnnotationConfigApplicationContext(EnvironmentApp.class);//当前的profile值是wow,所以获取的实现类是worldOfWarcraftBlizzard blizzard = ctx.getBean(Blizzard.class);}@Bean@Profile("war")publicBlizzardwarcraft(){returnnewWarcraft();}@Bean@Profile("wow")publicBlizzardworldOfWarcraft(){returnnewWorldOfWarcraft();}@Bean@Profile("default")publicBlizzardoverwatch(){returnnewOverwatch();}}

@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")classCastConfig{@BeanpublicCastlevaniacastlevania(){returnnewCastlevania();}}@Configuration@Profile("pes")classPESConfig{@BeanpublicProEvolutionSoccerproEvolutionSoccer(){returnnewProEvolutionSoccer();}}

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

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

@Configuration@Profile("cast")classCastConfig{@BeanpublicCastlevaniacastlevania(){returnnewCastlevania();}@Bean@Profile("pes")publicProEvolutionSoccerproEvolutionSoccer(){returnnewProEvolutionSoccer();}}

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

Profile的XML配置

Profile特性也可以在XML配置。不过只能在<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 =newAnnotationConfigApplicationContext(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 =newAnnotationConfigApplicationContext(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@interfaceProfile {}

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

classProfileConditionimplementsCondition{@Overridepublicbooleanmatches(ConditionContext context, AnnotatedTypeMetadata metadata){MultiValueMap attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());if(attrs !=null) {for(Object value : attrs.get("value")) {if(context.getEnvironment().acceptsProfiles((String[]) value)) {returntrue;}}returnfalse;}returntrue;}}

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容