spring 高级装配

环境与profile

配置profile bean

在java配置中,能够使用@ProFile注解制定某个Bean属于哪个profile。例如:@Profile("dev")。java

在xml中配置profile 经过<beans>元素的profile属性,在XML中配置profile bean 。linux

<!-- 开发环境配置文件 -->
    <beans profile="test">
        <context:property-placeholder location="/WEB-INF/test-orm.properties" />
    </beans>

    <!-- 本地环境配置文件 -->
    <beans profile="local">
        <context:property-placeholder location="/WEB-INF/local-orm.properties" />
    </beans>

  profile的定义必定要在文档的最下边,不然会有异常。整个xml的结构大概是这样git

<beans xmlns="..." ...>  
  <bean id="dataSource" ... />  
  <bean ... />  
  <beans profile="...">  
    <bean ...>  
  </beans>  
</beans>

3.1.2 激活profile

Spring在肯定那个Profile处于激活状态时,依赖两个独立的属性:spring.profiles.active 和spring.profiles.default。若是设置了spring.profiles.active属性的话,那么他的值就会用来肯定那个profile是激活的,可是若是没有设置 spring.profiles.active属性的话,就会查找 spring.profiles.default的值,若是spring.profiles.active 和 spring.profiles.default 均没有设置的话,那就没有激活的profile,只会建立那些没有定义在profile中的bean。 设置激活属性的方式:web

  • 做为DispatcherServlet的初始化参数。
  • 做为web应用的上下问参数。
  • 做为JNDI条目。
  • 做为环境变量。
  • 做为JVM的系统属性。
  • 在集成测试类上,使用@ActiveProfiles注解设置。

好比咱们在web.xml中能够声明代码以下spring

<?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>

另外对于测试,spring为何提供了一个简单的注解能够使用@ActiveProfiles,它能够指定运行测试的时候应该要激活那个profile。好比这里的测试类DevDataSourceTestsql

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);
 }
 }
 
}

一、ENV方式:windows

ConfigurableEnvironment.setActiveProfiles("test")

二、JVM参数方式:   tomcat 中 catalina.bat(.sh中不用“set”) 添加JAVA_OPS。经过设置active选择不一样配置文件数组

set JAVA_OPTS="-Dspring.profiles.active=test"

  eclipse 中启动tomcat。项目右键 run as –> run configuration–>Arguments–> VM arguments中添加。local配置文件没必要上传git追踪管理tomcat

-Dspring.profiles.active="local"

三、web.xml方式:session

<init-param>
  <param-name>spring.profiles.active</param-name>
  <param-value>production</param-value>
</init-param>

四、标注方式(junit单元测试很是实用):

@ActiveProfiles({"unittest","productprofile"})

3.2 条件化的bean

spring4 引入一个新的@Conditional 注解它能够用到@Bean注解的方法上,若是给定的条件计算结果为true,就差建立这个bean,不然的话,这个bean就会被忽略。

import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Conditional;  
import org.springframework.context.annotation.Configuration;  
   
@Configuration  
public class MyConfiguration {  
   
  @Bean(name="emailerService")  
  @Conditional(WindowsCondition.class)  
  public EmailService windowsEmailerService(){  
      return new WindowsEmailService();  
  }  
   
  @Bean(name="emailerService")  
  @Conditional(LinuxCondition.class)  
  public EmailService linuxEmailerService(){  
    return new LinuxEmailService();  
  }  
}

处理自动装配的歧义性

仅有一个bean匹配所需的结果时,自动装配才是有效的。若是不只有一个bean可以匹配结果的话,这种歧义性会阻碍spring自动装配属性、构造器参数或方法参数。 当确实发生歧义性的时候,spring提供了多种可选方案来解决这样的问题:1.将可选bean中的某一个设为首选(Primary)的bean。或者使用限定符(qualifier)来帮助spring将可选bean的范围缩小到只有一个bean。

3.3.1 标识首选bean

经过@ Primary来表达最喜欢的方案。@primary可以与@component组合用在组建扫描的bean上。也能够与@Bean组合用在java配置的bean声明中。

@Primary  
@Component  
public class OperaSinger implements Singer{  
  
    @Override  
    public String sing(String lyrics) {  
        return "I am singing in Bocelli voice: "+lyrics;  
    }  
}
@Primary  
@Bean
public class OperaSinger implements Singer{  
  
    @Override  
    public String sing(String lyrics) {  
        return "I am singing in Bocelli voice: "+lyrics;  
    }  
}
<bean id ="iceCream" class = "com.dess.IceCream" primary = "true">

3.3.2 限定自动装配的bean

@Qualifier 注解是使用限定符的主要方式,能够与@Autowired 和@Inject协同使用,在注入的时候制定想要注入进去的是那个bean。@qualifier注解所设置的参数就是想要注入的bean的id。

@Autowired 
@Qualifier("office") 
private Office office;

3.3.2 建立自定义的限定符

建立本身的限定符,所须要作的就是在bean声明上添加@Qualifier注解。

//
@Component 
@Qualifier("code") 
private Office office;

//显示定义bean,声明限定符
@Bean
@Qualifier("code") 
private Office office;

当使用自定义的@qualifier值时,最佳实践为bean选择特征或描述性的术语,而不是使用随意的名字

3.3.3 使用自定义的限定符注解

面向特性的限定符要比基于beanID的限定符更好一些。

3.4 bean的做用域

Spring上下文中全部bean都是做为单例(singleton)的形式建立的。注解:scope。做用域包括

  • 单例(singleton):在整个应用中,只建立bean的一个实例。
  • 原型(prototype):每次注入或者经过Spring应用上下文获取的时候,都会建立一个新的bean实例。
  • 会话(Session):在web应用中,为每一个回话建立一个bean实例。
  • 请求(Request):在web应用中,为每一个请求建立一个bean实例。

3.4.1 使用会话和请求做用域

应用场景:购物车场景。

@Component  
@Scope (  
    value=WebApplicationContext.SCOPE_SESSION,  
    proxyMode=ScopedProxyMode.INTERFACES)  
public ShoppingCart cart() {...}

@Scope的ProxyMode属性,它被设置成了ScopedProxyMode.INTERFACES.这个属性解决了会话或请求做用域的bean注入到单例bean中所遇到的问题。

Spring引入了做用域代理的方法,来解决该问题。代理会暴露与ShoppingCart相同的方法,当StoreService调用ShoppingCart的方法时,代理会对其进行懒解析并将调用委托给会话做用域内的真正的ShoppingCart bean。(即做用域代理可以延迟注入请求和会话做用域的bean)

在XML中声明做用域代理

若是使用XML来声明会话或请求做用域的bean,除了须要使用<bean>元素的scope属性设置bean的做用域外,还要使用Spring aop命名空间的aop:scoped-proxy元素。

aop:scoped-proxy是与@Scope注解的proxyMode属性功能相同的SpringXML配置元素。它会告诉Spring为bean建立一个做用域代理。默认状况下,它会使用CGLib建立目标类的代理。(能够经过将proxy-target-class属性设置为false,进而要求生成基于接口的代理),例如

<bean id="cart"  
    class="com.myapp.ShoppingCart"  
    scope="session">  
    <aop:scoped-proxy/>  
</bean>

注:上述代码,声明做用域为会话的bean,同时指定代理模式为建立目标类的方式。

<bean id="cart"  
    class="com.myapp.ShoppingCart"  
    scope="session">  
    <aop:scoped-proxy proxy-target-class="false" />  
</bean>

注:上述代码,声明做用域为会话的bean,同时指定代理模式为基于接口的方式。

另外,为了使用aop:scoped-proxy元素,必须在XML配置中声明Spring的aop命名空间:

<?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:aop="http://www.springframework.org/schema/aop"  
  xsi:schemaLocation="  
    http://www.springframework.org/schema/aop  
    http://www.springframework.org/schema/aop/spring-aop.xsd  
    http://www.springframework.org/schema/beans  
    http://www.springframework.org/schema/beans/spring-beans.xsd">  
    ...  
</bean>

3.5 运行时注入

运行时注入:是指将一个值注入到bean的属性或者构造器的参数中。为了实现这些功能,spring提供了两种在运行时求值的方式:

  • 属性占位符
  • Spring表达式语言(SPEl)

3.5.1 注入外部的值

处理外部值的最简单方法就是声明属性源并经过Spring的Environment来检索属性。
@Configuration  
//声明属性源
@PropertySource("classpath:/com/soundsystem/app.properties")  
public class ExpressiveConfig {  
      
    @Autowired  
    Environment env;  
      
    @Bean  
    public BlankDisc disc() {  
        return new BlankDisc(  
             //检索属性值
            env.getProperty("disc.title");  
            env.getProperty("disc.artist"));  
    }  
}

上述代码,@PropertySource 引用了类路径中一个名为app.properties的文件。该文件内容以下:

disc.title=Sgt. Peppers Lonely Hearts Club Band  
disc.artist=The Beatles

深刻学习Spring的Environment

  • String getProperty(String key) // 返回String类型的值

  • String getProperty(String key, String defaultValue) // 指定默认值的版本(当指定的属性不存在时,会使用一个默认值)

  • T getProperty(String key, Class<T> type) // 返回指定类型的值

  • T getProperty(String key, Class<T> type, T defaultValue)

方法: containsProperty()方法:检查某个属性是否存在。 getPropertyAsClass()方法:将属性解析为类。例如:

Class<CompactDisc> cdClass = env.getPropertyAsClass("disc.class", CompactDisc.class);

除了属性相关的功能外,Environment还提供了一些方法来检查哪些profile处于激活状态:

【】String[] getActiveProfiles() : 返回激活profile名称的数组

【】String[] getDefaultProfiles() : 返回默认profile名称的数组

【】boolean acceptsProfiles(String... profiles) : 若是environmet支持给定profile的话,返回true;

(2)、解析属性占位符

在Spring装配中,占位符的形式为使用“${...}”包装的属性名称。

若是要在XML中解析构造参数,能够以下所示:

<bean id="sgtPeppers"  
    class="soundsystem.BlankDisc"  
    c:_title="${disc.title}"  
    c:_title="${disc.artist}" />

若是是依赖组件扫描和自动装配来建立和初始化组件的话,能够使用@Value注解,例如:在BlankDisc类中,能够以下初始化:

public BlankDisc(  
    @Value("${disc.title}") String title,  
    @Value("${disc.artist}")  String artist) {  
    this.title = title;  
    this.artist = artist;  
}

为了使用占位符,必须配置一个PropertyPlaceholderConfigurer bean 或PropertySourcesPlaceholderConfigurer bean。(从Spring 3.1开始,推荐使用PropertySourcesPlaceholderConfigurer, 由于它可以基于Spring Environment及其属性源来解析占位符)。

相关文章
相关标签/搜索