1、环境与 Profile
在开发中我们测试用一套数据库,开发用一套数据库,而且要将应用程序从一个环境迁移到另一个环境,Spring 允许我们定义多套配置,可以配置声明应用哪套配置的 Bean
##### 1.1 Profile
* **Spring中的Profile是什么?**
Spring中的Profile功能其实早在Spring 3.1的版本就已经出来,它可以理解为我们在Spring容器中所定义的Bean的逻辑组名称,只有当这些Profile被激活的时候,才会将Profile中所对应的Bean注册到Spring容器中。举个更具体的例子,我们以前所定义的Bean,当Spring容器一启动的时候,就会一股脑的全部加载这些信息完成对Bean的创建;而使用了Profile之后,它会将Bean的定义进行更细粒度的划分,将这些定义的Bean划分为几个不同的组,当Spring容器加载配置信息的时候,首先查找激活的Profile,然后只会去加载被激活的组中所定义的Bean信息,而不被激活的Profile中所定义的Bean定义信息是不会加载用于创建Bean的。
* **Profile有什么用?**
由于我们平时在开发中,通常会出现在开发的时候使用一个开发数据库,测试的时候使用一个测试的数据库,而实际部署的时候需要一个数据库。以前的做法是将这些信息写在一个配置文件中,当我把代码部署到测试的环境中,将配置文件改成测试环境;当测试完成,项目需要部署到现网了,又要将配置信息改成现网的,真的好烦。。。而使用了Profile之后,我们就可以分别定义3个配置文件,一个用于开发、一个用户测试、一个用户生产,其分别对应于3个Profile。当在实际运行的时候,只需给定一个参数来激活对应的Profile即可,那么容器就会只加载激活后的配置文件,这样就可以大大省去我们修改配置信息而带来的烦恼。
下面我将以两个例子介绍一下目前常用的两种配置方式下如何配置Spring profile 方式一:用xml配置profile
Application.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 id="dataSource" type="H2">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:dev-data.sql" />
</jdbc:embedded-database>
</beans>
<beans profile="prod">
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:schema.sql" />
<jdbc:script location="classpath:prod-data.sql" />
</jdbc:embedded-database>
</beans>
</beans>
方式二:用Annotation配置profile,这种方式配置和用xml配置是等价的 在同一个类的不同方法上使用@Profile注解与@Bean一起使用配置出不同的profile实例
@Configuration
public class DataSourceConfig {
//Spring 引入@Profile 制定某个bean属于哪个profile
//在方法级别上使用@Profile注解
@Bean
@Profile("dev") //开发环境
public DataSource embeddedDataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:dev-data.sql")
.build();
}
@Bean
@Profile("prod") //生产环境
public DataSource embeddedDataSourceDev() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:prod-data.sql")
.build();
}
}
激活profile Spring在确定哪个profile处于激活状态时,需要依赖两个独立属性:sping.profiles.active
和spring.profiles.default
。Spring提供了@ActiveProfiles
用来指定运行测试时要激活哪个profile,如果没有指定sping.profiles.active,会采用spring.profiles.default的默认值。
测试代码:
package com.xzy;
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("classpath:datasource-config.xml")
@ActiveProfiles("prod")
public static class ProductionDataSourceTest_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:B", results.get(0));
}
}
}
2、条件化Bean
Spring 4 引入了一个新的@Conditional
注解,它可以用到带@Bean 注解的方法上,如果条件计算结果为 true,就会创建个 Bean设置给@Conditional 的类可以是任意实现了 Condition 接口的类型,如果matches()方法返回true,那么就会创建带有@Conditional注解的bean。若返回false,将不会创建这些bean。 其中: ConditionContext :
举个栗子: 写个条件类,实现Condition接口的matches方法,简单的判断一下当前的系统是不是Windows 7的,如果是返回true,否则返回false
package com.xzy.utils;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class StudentCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment env = context.getEnvironment();
System.out.println(env.toString());
if("Windows 7".equals(env.getProperty("os.name"))){
return true;
}else{
return false;
}
}
}
测试代码:
package com.xzy.utils;
import com.xzy.bean.Student;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConditionTest {
//只用这个条件为true才能产生Student,否则spring压根就不会理他
@Bean
@Conditional(StudentCondition.class)
public Student appConfig(){
Student student=new Student();
student.setName("晓明");
student.setAge(34);
return student;
}
}
测试代码:

程序运行结果:

如果把“Windows 7”改成“Windows 10”就是条件Bean就会返回false,由于无法正常的注入就会出现以下的异常:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'com.xzy.AppTest': Unsatisfied dependency expressed through field 'student'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.xzy.bean.Student' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=appConfig)}