Spring高級裝配

本節主要講:java

  • Spring profile
  • 自動裝配與歧義性
  • Spring表達式語言

Spring profile

當須要根據環境決定該建立哪一個 bean 和不建立哪一個 bean,可使用bean profile 的功能,確保是等到運行時再來肯定,這樣的結果就是同一個部署單元(可能會是 WAR 文件)可以適用於全部的環境,沒有必要進行從新構建。正則表達式

配置profile bean

在 Java 配置中,可使用 @Profile 註解指定某個 bean 屬於哪個 profile,例如,在配置類中,嵌入式數據庫的 DataSource 可能會配置成以下所示:spring

package com.myapp;  
  
import javax.sql.DataSource;  
  
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 {  
  
    @Bean(destroyMethod = "shutdown")  
    @Profile("dev")  
    public DataSource embeddedDataSource() {  
        return new EmbeddedDatabaseBuilder()  
                .setType(EmbeddedDatabaseType.H2)  
                .addScript("classpath:schema.sql")  
                .addScript("classpath:test-data.sql")  
                .build();  
    }  
  
    @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();  
    }  
  
}

激活 profile

有多種方式來設置這兩個屬性:sql

  • 做爲 DispatcherServlet 的初始化參數;
  • 做爲 Web 應用的上下文參數;
  • 做爲 JNDI 條目;
  • 做爲環境變量;
  • 做爲 JVM 的系統屬性;
  • 在集成測試類上,使用 @ActiveProfiles 註解設置。

處理自動裝配的歧義性

僅有一個 bean 匹配所需的結果時,自動裝配纔是有效的。若是不只有一個 bean 可以匹配結果的話,這種歧義性會阻礙 Spring 自動裝配屬性、構造器參數或方法參數。數據庫

標示首選的 bean

採用@Primary只能標記一個首選bean,若是你標示了兩個或更多的首選 bean,那麼它就沒法正常工做了。app

package com.food;  
  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.context.annotation.Primary;  
import org.springframework.stereotype.Component;  
  
@Component  
@Primary  
public class Cake implements Dessert {  
}

限定自動裝配的 bean

設置首選 bean 的侷限性在於 @Primary 沒法將可選方案的範圍限定到惟一一個無歧義性的選項中。它只能標示一個優先的可選方案。當首選 bean 的數量超過一個時,咱們並無其餘的方法進一步縮小可選範圍。dom

咱們這裏使用自定義的限定符註解。當使用自定義的 @Qualifier 值時,最佳實踐是爲 bean 選擇特徵性或描述性的術語,而不是使用隨意的名字。測試

建立自定義的限定符註解,這裏要使用@Qualifier來標註一下:ui

package com.food;  
  
import javax.inject.Qualifier;  
import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
  
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD,  
        ElementType.METHOD, ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
@Qualifier 
public @interface Cold {  
}

爲組件添加@Cold註解:this

package com.food;  
  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.stereotype.Component;  
  
@Component  
@Cold  
public class IceCream implements Dessert {  
}

爲了獲得 IceCream bean,DiningTable() 方法能夠這樣使用註解:

package com.food;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.stereotype.Component;  
  
@Component  
public class DiningTable {  
    private Dessert dessert;  
    
    @Autowired  
    @Creamy  
    public DiningTable(Dessert dessert) {  
        this.dessert = dessert;  
    }  
}

運行時值注入

當討論依賴注入的時候,咱們一般所討論的是將一個 bean 引用注入到另外一個 bean 的屬性或構造器參數中,可是 bean 裝配的另一個方面指的是將一個注入到 bean 的屬性或者構造器參數中。
例如:

@Bean  
public CompactDisc sgtPeppers()  {  
    return new BlankDisc(  
            "Sgt. Pepper's Lonely Hearts Club Band",  
            "The Beatles"  
  );  
}

BlankDisc bean 設置 title 和 artist,但它在實現的時候是將值硬編碼在配置類中的,有時候硬編碼是能夠的,但有的時候,咱們可能會但願避免硬編碼值,而是想讓這些值在運行時再肯定。

Spring提供了兩種在運行時求值的方式:

  • 屬性佔位符。
  • Spring 表達式語言(SpEL)。

屬性佔位符

屬性佔位符須要放到 ${ ... } 之中。
在本例中,@PropertySource 引用了類路徑中一個名爲 app.properties 的文件。它大體會以下所示:

disc.title=Sgt. Peppers Lonely Hearts Club
disc.artisc=The Beatles

若是咱們依賴於組件掃描和自動裝配來建立和初始化應用組件的話,那麼就沒有指定佔位符的配置文件或類了。好比,在 BlankDisc 類中,構造器能夠改爲以下所示:

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

@Value的使用方式與 @Autowired 註解很是類似。

使用 Spring 表達式語言進行裝配

SpEL 擁有不少特性,包括:

  • 表示字面值;
  • 使用 bean 的 ID 來引用 bean;
  • 調用方法和訪問對象的屬性;
  • 對值進行算術、關係和邏輯運算;
  • 正則表達式匹配;
  • 集合操做。

SpEL樣例

須要瞭解的第一件事情就是 SpEL 表達式要放到 #{ ... } 之中。

若是經過組件掃描建立 bean 的話,在注入屬性和構造器參數時,咱們可使用 @Value 註解,例如:

public BlankDisc(@Value("#{systemProperties['disc.title']}") String title, @Value("#{systemProperties['disc.artist']}") String artist) {  
    this.title = title;  
    this.artist = artist;  
}

表示字面值

#{3.14159}
#{false}
#{'3.14159'}
#{9.78E4}//科學計數法

引用 bean、屬性和方法

#{sgtPeppers}//經過 ID 引用其餘的 bean
#{sgtPeppers.artist}//引用sgtPeppers的artist屬性
#{sgtPeppers.selectArtist()}//引用sgtPeppers的方法
#{sgtPeppers.selectArtist()?.toUpperCase()}//?.這個運算符可以在訪問它右邊的內容以前,確保它所對應的元素不是 null。因此,若是 selectArtist() 的返回值是 null 的話,那麼 SpEL 將不會調用 toUpperCase() 方法。表達式的返回值會是 null。

在表達式中使用類型

T(java.lang.Math)//這裏所示的 T() 運算符的結果會是一個 Class 對象,表明了 java.lang.Math。

T() 運算符的真正價值在於它可以訪問目標類型的靜態方法和常量。

T(java.lang.Math).PI
T(java.lang.Math).random()

計算正則表達式

matches 返回的是一個boolean類型的值

#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}

計算集合

//引用一個jukebox bean的一個songs集合的title屬性
#{jukebox.songs[4].title} 

//.?[] 運算符用來對集合進行過濾,獲得集合的一個子集
//獲得jukebox 中 artist 屬性爲 Aerosmith 的全部歌曲
#{jukebox.songs.?[artist eq 'Aerosmith']}

//.^[] 用在集合中查詢第一個匹配項
#{jukebox.songs.^[artist eq 'Aerosmith']}

//.$[] 用在集合中查詢最後一個匹配項
#{jukebox.songs.$[artist eq 'Aerosmith']}

//.![]從集合的每一個成員中選擇特定的屬性放到另一個集合中
#{jukebox.songs.![title]}
相關文章
相關標籤/搜索