高級裝配

本節的主要內容是:

  • Spring profile
  • 條件化的bean聲明
  • 自動裝配與歧義性
  • bean的作用域
  • 運行時值注入
    本節內容,并不會全部講的非常細,因為有的開發中,由于開發進度的限制,可能并不會采用,這里只提供一引子,起到拋磚迎玉的作用。在遇到的時候,可以參考往正確的方向實現。

一、環境與profile

開發階段中,某些環境相關做法可能并不適合遷移到生產環境中,甚至即便遷移過去也無法正常工作。數據庫配置、加密算法以及與外部系統的集成是跨環境部署時會發生變化。比如,在不同的開發環境,會使用嵌入式數據庫,這時候可能就需要用到配置profile bean來解決不同環境下運行不同嵌入式數據庫的問題。
可以使用@Profile注解,在Spring 3.1中,只能在類級別上使用@Profile注解。不過,從Spring 3.2開始,你也可以在方法級別上使用@Profile注解,與@Bean注解一同使用。

二、條件化的bean

假設你希望一個或多個bean只有在應用的類路徑下包含特定的庫時才創建。Spring 4引入了一個新的@Conditional注解,它可以用到帶有@Bean注解的方法上。如果給定的條件計算結果為true,就會創建這個bean,否則的話,這個bean會被忽略。

三、處理自動裝配@Autowired 的歧義性

僅有一個bean匹配所需的結果時,自動裝配才是有效的。如果不僅有一個bean能夠匹配結果的話,這種歧義性會阻礙Spring自動裝配屬性、構造器參數或方法參數。

假設我們使用@Autowired注解標注了構造函數CDPlayer 如下:

    @Autowired 
    public CDPlayer cd(ICompactDisc cd2) {
        this.cd2 = cd2;
    }

這時候有好幾個類都實現了ICompactDisc接口,并將他們創建為Spring應用上下文里面的bean

@Component("myComponentName01")
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}

@Component("myComponentName02")
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}

@Component("myComponentName03")
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}

這種情況下CDPlayer 類里面的自動裝配,就會拋錯了。那么怎么解決呢?

  • 標示首選的bean,使用@Primary注解,@Primary能夠與@Component組合用在組件掃描的bean上,也可以與@Bean組合用在Java配置的bean聲明中。xml配置文件可以在<bean>元素有一個primary屬性用來指定首選的bean,boolean類型
@Component("myComponentName01")
@Primary
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}
<bean id="customerDAO" class="com.sanxin.org.jdbc.JdbcCustomerDAO" primary = "true">
        <property name="dataSource" ref="dataSource" />
</bean>

如果有兩個繼承類,都同時用了@Primary注解呢,這時候又會拋錯了,因為程序又不知道該使用哪個了。接下來看下面的限定自動裝配的bean來解決這個問題。

  • 限定自動裝配的bean,@Qualifier注解是使用限定符的主要方式。它可以與@Autowired和@Inject協同使用,在注入的時候指定想要注入進去的是哪個bean。也可以與@Component組合使用,為bean設置自己的限定符。
@Component("myComponentName01")
@Qualifier("CompactDiscImpl01 ")
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}
    @Autowired 
    @Qualifier("CompactDiscImpl01 ")
    public CDPlayer cd(ICompactDisc cd2) {
        this.cd2 = cd2;
    }

四、bean的作用域

在默認情況下,Spring應用上下文中所有bean都是作為以單例(singleton)的形式創建的。也就是說,不管給定的一個bean被注入到其他bean多少次,每次所注入的都是同一個實例。

Spring定義了多種作用域,可以基于這些作用域創建bean,包括:

  • 單例(Singleton):在整個應用中,只創建bean的一個實例。
  • 原型(Prototype):每次注入或者通過Spring應用上下文獲取的時候,都會創建一個新的bean實例。
  • 會話(Session):在Web應用中,為每個會話創建一個bean實例。
  • 請求(Rquest):在Web應用中,為每個請求創建一個bean實例。

1.使用@Scope注解,它可以與@Component或@Bean一起使用。

@Component("myComponentName03")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  //ConfigurableBeanFactory.SCOPE_PROTOTYPE ="prototype"
public class CompactDiscImpl01 implements ICompactDisc{
    ...
}

也可以在xml文件中配置:

<bean id="customerDAO" class="com.sanxin.org.jdbc.JdbcCustomerDAO" primary = "true" scope="prototype" />

2.以上是使用單例或是原型作用域的方式,那么,怎么實現使用會話和請求作用域
會話和請求作用域直到某個用戶進入系統,創建了會話之后,才會出現bean實例。

    @Bean("myComponentName03")
    @Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode=ScopedProxyMode.INTERFACES) 
    public CDPlayer cd(ICompactDisc cd2) {
        this.cd2 = cd2;
    }
<bean id="customerDAO" class="com.sanxin.org.jdbc.JdbcCustomerDAO" primary = "true" scope="prototype" >
      <!-- 默認情況下,它會使用CGLib創建目標類的代理。 但是我們也可以將proxy-target-class屬性設置為false,進而要求它生成基于接口的代理-->
      <aop:scoped-proxy  proxy-target-class = "false"/>
</bean>

當ICompactDisc 時一個借口時,proxyMode屬性被設置成了ScopedProxyMode.INTERFACES,這表明這個代理要實現ShoppingCart接口,并將調用委托給實現bean。但如果ShoppingCart是一個具體的類的話,Spring就沒有辦法創建基于接口的代理了。此時,它必須使用CGLib
來生成基于類的代理。所以,如果bean類型是具體類的話,我們必須要將proxyMode屬性設置為ScopedProxyMode.TARGET_CLASS,以此來表明要以生成目標類擴展的方式創建代理。

五、運行時值注入

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

  • Spring表達式語言(SpEL)。
  • 屬性占位符(Property placeholder)。

1.先看占位符,下面一個例子,將屬性文件jdbc.properties中的值讀取出來,并設置到DataSource這個類當中。如下代碼:

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;

import com.df.test.pojo.DataSource;
import com.df.test.service.StudentService;

@Configuration
@PropertySource("classpath:jdbc.properties")
public class StudentServiceImpl implements StudentService {
    
    @Autowired
    Environment ev;     

    @Bean
    public DataSource ds(){
        return new DataSource(ev.getProperty("url"),
                ev.getProperty("username"),ev.getProperty("password"));
    }
}

如果我們依賴于組件掃描和自動裝配來創建和初始化應用組件的話,那么就沒有指定占位符的配置文件或類了。在這種情況下,我們可以使用@Value注解。

    @Bean("myComponentName03")
    public CDPlayer  cd(@Value("url") url,@Value("username") username,@Value("password") password) {
        return new DataSource(url,username,password));
    }

2.Spring表達式語言(SpEL)。
SpEL擁有很多特性:

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

這里就不在細細分析了,可以移步Spring表達式語言(SpEL)學習。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容