Spring入门实战之Profile详解
前言
spring中的profile功能其实早在spring 3.1的版本就已经出来,它可以理解为我们在spring容器中所定义的bean的逻辑组名称,只有当这些profile被激活的时候,才会将profile中所对应的bean注册到spring容器中。
看到profile这个关键字,或许你从来没有正眼瞧过他,又或者脑海中有些模糊的印象,比如除了这里springmvc中的profile,maven中也有profile的标签。
从字面意思来看,profile表示侧面,那什么情况下才会用到侧面这个功能呢,而侧面具体又有什么含义呢
打一个比方,对于数据库的配置问题,在开发的眼中可以使用嵌入的数据库,并且加载测试数据(后面会给出代码示例)。但是在测试的眼中,可能会配一个数据库连接池类似这样
@bean(destroymethod="close") public datasource datasource () { basicdatasource datasource = new basicdatasource(); datasource.seturl("jdbc:h2:tcp://dbserver/~/test"); datasource.setdriverclassname("org.h2.driver"); datasource.setusername("sa"); datasource.setpassword("password"); datasource.setinitialsize(20); datasource.setmaxactive(30); return datasource; }
当然还有产品环境下的配置等等。对于这种百花齐放的配置方式你还能说什么,默默的为这一套套的环境都部署相应的配置文件啊,没有profile这套我们一直都是这么做。
但是现在有了profile,我们就多了一种选择,一种更加智能省心的配置方式。通过profile配置,spring可以在根据环境在运行阶段来决定bean的创建与否,先举例如下,主要从profile bean的配置和激活来展开。
profile bean的配置
通过注解@profile配置
对于上面比方中的第一种情况,在开发环境中我们配置一个数据源可能是这样的
@bean(destroymethod = "shutdown") public datasource embeddeddatasource() { return new embeddeddatabasebuilder() .addscript("classpath:schema.sql") .addscript("classpath:test-data.sql") .build(); }
这里会使用embeddeddatabasebuilder创建一个嵌入式数据库,模式定义在类文件下的schema.sql文件中
schema.sql
create table things ( id identity, name varchar(100) );
这里定义了一张things表包含了两个字段
除了模式文件,还需要通过test-data.sql加载测试数据
test-data.sql
insert into things (name) values ('a')
对于这个@bean完全不知道是放在开发的环境下创建还是产品的环境下。所以我们这里可以使用注解@profile帮助我们为这个bean打上标识。
从spring 3.1版本中就引入了bean profile的功能,可以让你将不同的bean定义到一个或者多个profile里,然后在部署应用时告知要激活那个profile,则相应的bean就会被创建。
比如这里
@configuration @profile("dev") public class developmentprofileconfig { @bean(destroymethod = "shutdown") public datasource embeddeddatasource() { return new embeddeddatabasebuilder() .settype(embeddeddatabasetype.h2) .addscript("classpath:schema.sql") .addscript("classpath:test-data.sql") .build(); } }
通过@profile("dev")
为embedderdatasource bean标记为dev环境下要创建的bean。
注意:1. @profile被加载类级别上,如果dev profile没有被激活,那么类中对应的所有bean就不会被创建
2. 如果当前是dev环境被激活了,那么对于没有使用@profile的bean都会被创建,被标记为其他的profile如prod,则不会创建相应的bean
3. 从3.2开始@profile不仅仅可以加载类级别上,还可以加载方法上,具体代码如下
package com.myapp; import javax.sql.datasource; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.context.annotation.profile; import org.springframework.jdbc.datasource.embedded.embeddeddatabasebuilder; import org.springframework.jdbc.datasource.embedded.embeddeddatabasetype; import org.springframework.jndi.jndiobjectfactorybean; @configuration public class datasourceconfig { @bean(destroymethod = "shutdown") @profile("dev") public datasource embeddeddatasource() { return new embeddeddatabasebuilder() .settype(embeddeddatabasetype.h2) .addscript("classpath:schema.sql") .addscript("classpath:test-data.sql") .build(); } @bean @profile("prod") public datasource jndidatasource() { jndiobjectfactorybean jndiobjectfactorybean = new jndiobjectfactorybean(); jndiobjectfactorybean.setjndiname("jdbc/myds"); jndiobjectfactorybean.setresourceref(true); jndiobjectfactorybean.setproxyinterface(javax.sql.datasource.class); return (datasource) jndiobjectfactorybean.getobject(); } }
通过xml配置文件配置
除了简单的注解方式,我们哈可以通过在xml配置文件中声明的方式,具体配置如下
datasource-config.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" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:p="http://www.springframework.org/schema/p" xsi:schemalocation=" http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <beans profile="dev"> <jdbc:embedded-database type="h2"> <jdbc:script location="classpath:schema.sql" /> <jdbc:script location="classpath:test-data.sql" /> </jdbc:embedded-database> </beans> <beans profile="prod"> <jee:jndi-lookup lazy-init="true" jndi-name="jdbc/mydatabase" resource-ref="true" proxy-interface="javax.sql.datasource" /> </beans> </beans>
这里分别声明了两种环境以及对应的profile。
profile激活
虽然我们已经配置好了profile,但是如何激活相应的环境呢。这里我们需要两个属性spring.profile.active
以及spring.profile.default
。
如果spring.profile.active
被赋值了,则spring.profile.default
就不会起作用,如果spring.profie.active
没有赋值,则使用默认的spring.profile.default
设置的值。当然,如果两者都没有设置的话,则只会创建那些定义在相应的profile中的bean。
设置这两个属性的方式有很多:
作为dispactcherservlet的初始化参数
作为web应用上下文参数
作为jndi条目
作为环境变量
作为jvm的系统属性
在集成测试类上,使用@activeprofiles注解设置
比如我们在web.xml中可以声明代码如下
<?xml version="1.0" encoding="utf-8"?> <web -app version="2.5" ...> //为上下文设置默认的profile <context-param> <param-name>spring.profile.default</param-name> <param-value>dev</param-value> </context-param> ... <servlet> ... //为serlvet设置默认的profile <init-param> <param-name>spring-profiles.default</param-name> <param-value>dev</param-value> </init-prama> ... <web-app>
这样就可以指定需要启动那种环境,并准备相应的bean。
另外对于测试,spring为什么提供了一个简单的注解可以使用@activeprofiles,它可以指定运行测试的时候应该要激活那个profile。比如这里的测试类devdatasourcetest
package profiles; import static org.junit.assert.*; import java.sql.resultset; import java.sql.sqlexception; import java.util.list; import javax.sql.datasource; import org.junit.test; import org.junit.runner.runwith; import org.springframework.beans.factory.annotation.autowired; import org.springframework.jdbc.core.jdbctemplate; import org.springframework.jdbc.core.rowmapper; import org.springframework.test.context.activeprofiles; import org.springframework.test.context.contextconfiguration; import org.springframework.test.context.junit4.springjunit4classrunner; import com.myapp.datasourceconfig; public class datasourceconfigtest { @runwith(springjunit4classrunner.class) @contextconfiguration(classes=datasourceconfig.class) @activeprofiles("dev") public static class devdatasourcetest { @autowired private datasource datasource; @test public void shouldbeembeddeddatasource() { assertnotnull(datasource); jdbctemplate jdbc = new jdbctemplate(datasource); list<string> results = jdbc.query("select id, name from things", new rowmapper<string>() { @override public string maprow(resultset rs, int rownum) throws sqlexception { return rs.getlong("id") + ":" + rs.getstring("name"); } }); assertequals(1, results.size()); assertequals("1:a", results.get(0)); } } @runwith(springjunit4classrunner.class) @contextconfiguration(classes=datasourceconfig.class) @activeprofiles("prod") public static class productiondatasourcetest { @autowired private datasource datasource; @test public void shouldbeembeddeddatasource() { // should be null, because there isn't a datasource configured in jndi assertnull(datasource); } } @runwith(springjunit4classrunner.class) @contextconfiguration("classpath:datasource-config.xml") @activeprofiles("dev") public static class devdatasourcetest_xmlconfig { @autowired private datasource datasource; @test public void shouldbeembeddeddatasource() { assertnotnull(datasource); jdbctemplate jdbc = new jdbctemplate(datasource); list<string> results = jdbc.query("select id, name from things", new rowmapper<string>() { @override public string maprow(resultset rs, int rownum) throws sqlexception { return rs.getlong("id") + ":" + rs.getstring("name"); } }); assertequals(1, results.size()); assertequals("1:a", results.get(0)); } } @runwith(springjunit4classrunner.class) @contextconfiguration("classpath:datasource-config.xml") @activeprofiles("prod") public static class productiondatasourcetest_xmlconfig { @autowired(required=false) private datasource datasource; @test public void shouldbeembeddeddatasource() { // should be null, because there isn't a datasource configured in jndi assertnull(datasource); } } }
运行shouldbeembeddeddatasource方法,测试通过
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。