- 环境与profile
1.配置profile bean
如下代码所示:
在开发环境中会创建一个javax.sql.DataSource的bean。用EmbeddedDatabaseBuilder会搭建一个嵌入式的HyperSonic数据库。他的模式(schema)定义在schema.sql中,测试数据则是通过test-data.sql加载的。
在生产环境中通过JNDI容器中获取一个DataSource,
在Qa环境中则配置Commons DBCP连接池。
package test;
import javax.activation.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
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 {
/**
* 开发环境
* @return
*/
@Bean(destroyMethod="shutdown")
@Profile("dev")
public DataSource dataSource() {
return (DataSource) new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:test-data.sql")
.build();
}
/**
* QA环境
* @return
*/
@Bean
@Profile("qa")
public DataSource qaDataSource(){
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) dataSource;
}
/**
* 生产环境
* @return
*/
@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();
}
}
注:尽管每个DataSource bean 都背声明在一个profile中,并且只有当规定的profile被激活时,相应的bean才会被创建。而没有用指定profile的bean始终都会被创建,与激活哪个profile没有关系。
- 在xml中配置profile
<?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-4.3.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.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>
<!-- QA环境 -->
<beans profile="qa">
<bean id="dataSource" 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 profile="prod">
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDataBase"
resource-ref="true" proxy-interface="javax.sql.DataSource" />
</beans>
</beans>
三个bean,但是在运行的时候只会创建一个bean,这取决于哪个bean被激活。
2. 激活profile
Spring在确定哪个profile处于激活状态时,需要两个独立的属性:spring.profiles.active和spring.profiles.default。如果设置了spring.profiles.active属性,那么它的值就用来确定哪个profile被激活。否则就查找spring.profile.default的值。如果两个均没有设置,那就没有激活的profile,因此只会创建那些没有定义在profile的bean。
通过以下几种方式设置这两个属性:
作为DispatcherServlet的初始化参数;
作为Web应用的上下文参数;
作为JNDI条目;
作为环境变量;
作为JVM的系统属性;
在继承测试类上,使用@ActiveProfile注解设置
在web.xml文件中设置默认的profile
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 为上下文设置默认的profile -->
<context-param>
<param-name>spring.profiles.default</param-name>
<param-value>dev</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 为Servlet设置默认的profile -->
<init-param>
<param-name>spring.profile.default</param-name>
<param-value>dev</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
按照这种方式设置的spring.profile.default,所有的开发人员都能从版本控制软件中获得应用程序源码,并使用开发环境设置运行代码,不需要额外的配置。
当系统使用spring.profile.active以后,spring.profile.default将没有任何作用,系统会有限使用spring.profile.active的值。
- 条件化的bean
假设有一个名为MagicBean的类,我们希望在只有设置了magic环境属性的时候,Spring才会实例化这个类。
当matches()方法的返回值为真的时候则会创建MagicBean,否则不会创建。
package restfun;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
public class MagicConfig {
/**
* 条件化的创建bean
* @return
*/
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
}
MagicExistsCondition需要实现Condition接口并且实现其中的matches()方法。
ConditionContext中的getRegistry()方法返回的BeanDefinitionRegistry检查bean定义。
ConditionContext中的getBEanFactory()方法返回的ConfigurableListBeanFactory检查bean是否存在,甚至探查bean的属性。
ConditionContext中的getEnvironment()方法返回的Environment检查环境变量是否存在以及它的值是什么。
ConditionContext中的getResourceLoader()方法获取返回的ResourceLoader所加载的资源。
ConditionContext中的getClassLoader()方法获取返回的ClassLoader所加载的资源是否存在。
package restfun;
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 MagicExistsCondition implements Condition{
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
//检查magic属性
return env.containsProperty("magic");
}
}
- 处理自动装配的歧义性
当有多个bean能够匹配结果的话,这种歧义性会阻碍Springz自动装配属性、构造器参数或方法参数。
处理这个方式有两个:标示首选的bean和限定自动装配的bean.
1.标示首选的bean.
通过在@Component和@Primary结合来使用
package soundsystem;
import javax.inject.Named;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component("lonelyHearsClub")
@Primary
public class SgtPeppers implements CompactDisc{
.....
}
<bean id="cdPlayer" class="xmlCongfig.CDPlayer" primary="true" />
2.限定自动装配的bean
(1)@Qualifier注解是使用限定符的主要方式。为@Qualifier注解所设置的参数就是想要注入的bean的ID。
package test;
import org.springframework.stereotype.Component;
@Component
public class IceCream implements Dessert{
@Override
public void display() {
// TODO Auto-generated method stub
}
}
@Autowired
@Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
(2)使用自定义的限定符
为IceCraem类定义其限定符为cold。则在Java配置bean的时候就必须如下配置@Qualifier("cold")。
package test;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("cold")
public class IceCream implements Dessert{
@Override
public void display() {
}
}
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class Test {
private Dessert dessert;
@Autowired
@Qualifier("cold")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
public void fn() {
dessert.display();
}
}
(3)使用自定义的限定符注解
可以自定义一个限定符注解,如定义了一个@Cold的限定符,该限定符必须实现Qualifier注解。然后在IceCream与bean配置中使用该限定符即可
package test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.annotation.Qualifier;
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Cold {
}
package test;
import org.springframework.stereotype.Component;
@Component
@Cold
public class IceCream implements Dessert{
@Override
public void display() {
}
}
package test;
import org.springframework.beans.factory.annotation.Autowired;
public class Test {
private Dessert dessert;
@Autowired
@Cold
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
public void fn() {
dessert.display();
}
}
- bean的作用域
spring中bean的作用域有:
1.单例(Singleton):在整个应用中,只创建bean的一个实例
2.原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例
3.会话(session):在Web应用中,为每个会话请求创建一个bean实例
4.请求(request):在Web应用中,为每个请求创建一个bean实例
package scoped;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
@Component
/**
* 原型模式
@Scope("prototype")
或者
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
在xml文件中的配置形式
<bean id="notepad" class="scoped.Notepad" scope="protype" />
会话模式
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
*/
@Scope(value=WebApplicationContext.SCOPE_SESSION, proxyMode=ScopedProxyMode.INTERFACES)
public class Notepad {
}
在XML中声明作用域代理
<bean id="notepad" class="scoped.Notepad" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
</bean>
<aop:scoped-proxy>是与@Scope注解的proxyMode属性功能相同的SpringXML配置元素。它会告诉Spring为bean创建一个作用域代理。默认情况下,它会使用CGLib创建目标代理。但是将proxy-target-class属性设置为false,进而要求它生成基于接口的代理。
- 运行时注入
1.注入外部的值
在Spring中,处理外部值得最简单的方式是声明属性源并通过Spring的Environment来检索属性。
package externals;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
@Configuration
//声明属性源
@PropertySource("classpath:app.properties")
public class ExpressiveConfig {
@Autowired
Environment env;
@Bean
public BlankDisc disc() {
return new BlankDisc(env.getProperty("disc.title"),env.getProperty("disc.artist"));
}
}
2 使用Spring表达式语言进行装配
Spring表达式语言(Spring ExpressionLanguage, SpEL), 它能够以一种强大和简洁的方式将值装配到bean属性和构造器参数中,在这个过程中所使用的表达式会在运行时计算得到的值。“#{.......}”
SpEL的特性:
使用bean的ID来引用bean;
使用方法和访问对象的属性;
对值进行算术、关系和逻辑运算;
正则表达式匹配;
集合操作。
引用bean、属性和方法
#{sgtPeppers.artist}其中sgtPeppers为一个bean的ID,并且使用了该bean的artist属性。