Bean Profile
对于一份代码,我们可能需要它在不同的环境都进行不同的调用或者配置,为了不对代码进行多次修改,我们可以预设多个条件,然后在代码执行的时候根据环境的不同来使用不同的配置,这种做法就是使用Bean Profile进行配置,在Spring中,我们使用@Profile注解进行表示,看下面一个例子
这是我们在开发时所需要的一个数据库的操作源
@Configuration
@Profile("dev")
public class DevelopmentProfileConfig {
@Bean(destroyMethod = "shutdown")
public DataSource dataSource(){
return (DataSource) new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
}
假如我们生产中同样也需要一个操作源,那么我们可以这样写
@Configuration
@Profile("prod")
public class ProductionProfileConfig {
@Bean
public DataSource dataSource(){
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
这两个类的意思是只有分别在profile文件是dev或者prod被激活的时候,才会创建相应的bean
不过Spring3.2之后可以在方法级别上使用这个profile文件,如下所示
@Configuration
public class DataSourceConfig {
@Bean(destroyMethod = "shutdown")
public DataSource embeddeddataSource(){
return (DataSource) new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
@Bean
public DataSource jndidataSource(){
JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiName("jdbc/myDS");
jndiObjectFactoryBean.setResourceRef(true);
jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
return (DataSource) jndiObjectFactoryBean.getObject();
}
}
bean file不仅可以使用JavaConfig进行配置,而且还可以使用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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
</beans>
也可以为了多个环境把配置写在同一个文件里面而不是为每一个环境写一个xml文件,写的时候需要用beans进行包裹,如下
<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:schema.sql"/>
<jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="prod">
<jee:jndi-lookup jndi-name="jdbc/myDatabase"
id="jndiDataSource"
resource-ref="true"
proxy-interface="javax.sql.DataSource"/>
</beans>
<beans profile="qa">
<bean id="qaDataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:url="jdbc:h2:tcp://dbserver/~/test"
p:driverClassName="org.h2.Driver"
p:username="sa"
p:password="password"
p:initialSize="20"
p:maxActive="30"/>
</beans>
</beans>
激活Bean profile的方式有两种,分别是spring.profiles.active和spring.profile.default,一般以active为主,没有这个属性则使用default属性,如果二者都不存在,则不激活带profile参数的文件,可以有多种方式来设置这两个属性
1、作为DispatcherServlet的初始化参数
2、作为web的应用上下文参数
3、做为JNDI条目
4、作为环境变量
5、作为JVM的系统属性
6、在集成的环境上,使用@ActiveProfile注解设置
条件化的Bean
有时候我们需要根据某些条件是否满足来决定是否创建bean,我们在这时候就需要使用@Conditional注解来表示我们的条件,注解中的内容是实现Condition的类,举例如下
@Bean
@Conditional(MagicExistsCondition.class)
public CompactDisc randomBeatlesCD(){
int choice = (int) Math.floor(Math.random() * 4);
if(choice == 0){
return new SgtPeppers();
}else {
return new Revolver();
}
}
对Condition接口实现的类如下
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment env = conditionContext.getEnvironment();
return env.containsProperty("magic");
}
}
当matches返回值为true的时候,我们创建这个bean,当返回值为false的时候,则不创建
处理自动装配的歧义性
当对应一种接口有多种实现的时候,假如此时我们使用自动装配,那么就会出现自动装配的歧义,在这种情况下,我们有3种方式来消除歧义
1、对默认的bean使用@Primary标识,但是假如多个bean都使用@Primary标识,那么就没有办法进行区分
2、使用@Qualifier加限定符的方式来进行标识
3、使用自定义的限定标识符
Bean的作用域
默认的情况下,bean是单例的形式创建的,也就是说在整个应用程序的生命周期内,bean的实例只有一个,但是有时候单例的bean并不能解决所有问题,所以我们又添加了几种作用域的bean,分别是
1、单例:整个应用中只存在一个bean的实例
2、原型:每次注入或通过应用上下文获取的时候都会创建一个新的bean实例
3、会话:每个会话创建一个bean实例
4、请求:每次请求时创建一个bean实例
运行时注入
有时候我们对创建的bean的值是在运行时确定的而不是在编译时就确定的,我们有两种方式来做到这一点,分别是
1、属性占位符
2、Spring表达式语言(SpEL)