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及其屬性源來解析佔位符)。

相關文章
相關標籤/搜索