天天看點

再學習之Spring(依賴注入).

一、概述

    Spring架構是以 簡化Java EE應用程式的開發 為目标而建立的。Spring可以實作很多功能,但是這些功能的底層都依賴于它的兩個核心特性,也就是依賴注入和面向切面程式設計。幾乎Spring所做的任何事情都可以追溯到下述的一條或多條政策:

基于POJO的輕量級和最小侵入性程式設計;

通過依賴注入和面向接口實作松耦合;

基于切面和慣例進行聲明式程式設計;

通過切面和模闆減少樣闆式代碼。 

    Spring的三個基本願景:

使用DI來實作低耦合

使用AOP切面實作高内聚

使用模闆消除樣闆式代碼,比如jdbcTemplate

二、Bean

    容器是Spring架構的核心。Spring容器使用DI管理構成應用的元件(Bean),它會建立互相協作的元件之間的關聯。毫無疑問,這些對象更簡單幹淨,更易于了解和重用,更易于單元測試。

    Spring為每個Bean定義了多種作用域,預設都是以單例的模式建立的:

單例(Singleton):在整個應用中,隻建立bean的一個執行個體。

原型(Prototype):每次注入或者通過Spring應用上下文擷取的時候,都會建立一個新的bean執行個體。

會話(Session):在Web應用中,為每個會話建立一個bean執行個體。

請求(Rquest):在Web應用中,為每個請求建立一個bean執行個體。

    Bean的生命周期:

再學習之Spring(依賴注入).

     Spring自帶了多種類型的上下文(适用于在普通類中利用Spring的上下文加載需要的Bean):

AnnotationConfigApplicationContext:從一個或者多個的Java配置類中加載Spring的應用上下文

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);      

View Code

AnnotationConfigWebApplicationContext:從一個或多個基于Java的配置類中加載Spring Web應用上下文。

ClassPathXmlApplicationContext:從類路徑下的一個或多個XML配置檔案中加載上下文定義,把應用上下文的定義檔案作為類資源。

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
ApplicationContext applicationContext= new ClassPathXmlApplicationContext("applicationContext.xml");      

FileSystemXmlapplicationcontext:從檔案系統下的一個或多個XML配置檔案中加載上下文定義。

XmlWebApplicationContext:從Web應用下的一個或多個XML配置檔案中加載上下文定義。

三、依賴注入

1、@Component 表明該類會作為元件類,并告知Spring 要為這個類建立bean(這個bean的ID預設取名類名的首字母小寫)。不過元件掃描預設是不啟用的。我們還需要顯示配置一下Spring,進而指令他去尋找帶有@Component(類似的還有@Repository @service @controller)注解的類,并為他建立bean。有三種方式來配置Spring建立Bena:

* 自動化裝配的方式:1、建一個配置類。@Configuration 表明這個類是一個配置類 加上@ComponentScan(basePackages={"包名1","包名2"} 或者 basePackageClasses={包1的某個Class,包2的某個Class}) 會掃描和配置類相同的包,以及這個包下的所有子包。

                                  2、Spring XML配置方式: <context:component-scan base-package=""/>

* 在JavaConfig中進行顯示配置(适用于第三方的類庫元件裝配到自己應用中)  

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
@Configuration
public class JavaConfig {



    @Bean(name = "base64Util")
    public Base64Util getBase64Util(){
        return new Base64Util();
    }

    @Bean(name = "base64UtilExpand") //這種顯示配置預設的Bean名是方法名。是以最好顯示配置一下
    public Base64UtilExpand getBase64UtilExpand(){
        //對于這種建立的bean需要引用其他的bean。Spring是這樣處理的:當引用到還沒建立的bean的時候,Spring會攔截下這個引用,等到引用的bean的建立完成。已保證Spring bean的單例模式.
        return new Base64UtilExpand(getBase64Util());
    }
    
    @Bean(name = "base64UtilExpand")
    public Base64UtilExpand getBase64UtilExpand(Base64Util base64Util){
        return new Base64UtilExpand(base64Util);
    }
}      

* 在XML中進行顯示配置,最古老的方式,一般很少用,有兩種方式注入,構造器注入和set注入:

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
<!--構造器注入-->
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg ref="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg value="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <constructor-arg>
            <list>
                <value></value>
            </list>
        </constructor-arg>
    </bean>

    <!--setter注入-->
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" ref="compactDisc">
    </bean>
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" value="compactDisc"><!--裝配字面量:-->
    </bean>
    裝配集合
    <bean id="cDPlayer" class="com.CDPlayer">
        <property name="compactDisc" value="compactDisc">
        <property name="">
            <list>
                <value></value><!--裝配集合-->
            </list>
        </property>
    </bean>      

2、@scope 指定bean建立時的作用域,Spring預設建立單例模式的bean。但也有特殊的情況,這裡需要說明的是如果某個類注入了Session 和 Request 作用域的Bean,因為這個Bean是在使用者請求的時候産生的,在Spring 運作起來的時候并不存在。這裡Spring是這樣處理的:注入給對應Bean的一個代理,而當使用者請求産生session 或者 request 作用域Bean的時候,由這個代理連接配接到相應的Bean處理請求...

再學習之Spring(依賴注入).

原型:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

會話:(接口)@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.INTERFACES)

          (具體類)@Scope(value = WebApplicationContext.SCOPE_SESSION,proxyMode = ScopedProxyMode.TARGET_CLASS)

               當然也可以在XML中配置:

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
<bean id="shoppingCart2" class="com.entity.ShoppingCart2" scope="session">
        <aop:scoped-proxy proxy-target-class="false"/>
    </bean>      

3、限定符(用的較少)

 @Profile 決定哪些bean可以被激活。需要注意的是沒有指定profile的bean始終都會被建立,與激活哪個profile沒有關系。

 @Conditional中給定了一個Class,這個Class 實作了Codition 接口的matches 方法,該方法傳回true 則生成bean,否則不生成。

 @primary 常見的情況是一個接口僅有一個實作類,是以使用@Autowire的後,Spring可以走到對應的實作類。如果一個接口有多個實作類呢?@Component 和 @primary 同時使用,标注哪個實作類優先被使用。

 @Qualifier 使用@primary 仍然無法保證哪個bean被選擇,因為可以在多個實作類上使用@primary。是以可以在實作類用@Qualifter("bean")名指定bean的名字。并在@Autowire 注入接口的時候是使用Qualifier 指定實作類的bean名。當然,也可以用@Resource(name=" ")指定類的名稱。

4、讀取 properties 檔案

(1) @PropertySource 會引用一個類路徑上的properties的檔案,并利用Environment類擷取properties的變量值。例如:@PropertySource("classpath:mongo.properties")

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
@Configuration
@PropertySource("classpath:mongo.properties")
public class JavaConfig {
    
    @Bean(name = "mongoUtil")
    public MongoUtil getMongoUtil(Environment env){
        return new MongoUtil(env.getProperty("mongo.host"),
                env.getProperty("mongo.port"),
                env.getProperty("mongo.database"),
                env.getProperty("mongo.username"),
                env.getProperty("mongo.password"));
    }
}      
再學習之Spring(依賴注入).

(2) 占位符

     Spring 中占位符的形式是使用${}的方式。在代碼檔案中我們可以使用@Value注解将配置檔案的值注入到變量中。為了使用占位符,我們必須配置一個PropertySourcesPlaceholderConfigurer 的類,已生成相關的bean,或者通過XML配置讓Spring為我們自動生成:

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
@Configuration
@PropertySource("classpath:mongo.properties")
public class JavaConfig {
    
    @Bean(name = "propertySourcesPlaceholderConfigurer")
    public PropertySourcesPlaceholderConfigurer getPropertySourcesPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
}      

或者:

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
<!--提供讀取配置檔案可以使用Spring占位符${}-->
    <context:property-placeholder location="classpath:mongo.properties" file-encoding="utf-8" />      

用法如下:

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = JavaConfig.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class Test06 {

    @Value("${mongo.host}")
    private String host;

    @Test
    public void test06(){
        System.out.println(host);
    }
}      

四、Expression Language

 Spring Expression Language,簡稱SpEL,是一種非常靈活的表達式語言,擁有很多特性,包括:

使用bean的ID來引用bean;

調用方法和通路對象的屬性;

對值進行算術、關系和邏輯運算;

正規表達式比對;

集合操作。

SpEL 采用#{}的形式:

1、代表字面值:#{3.14} #{'Hello'} #{false}

2、引用bean、屬性、方法 #{bean} #{bean.artist} #{bean.toUpperCase()} #{bean?.toUpperCase()}(表示如果bean為null 就傳回null,不調用方法)

3、引用某個類 #{T{java.lang.Math}.PI}

4、三元表達式 #{bean.score > 1000 ? "win":"los"} 判空 #{bean.score ?: "win"}

5、正規表達式 #{bean.email matches '表達式'}

6、計算集合 #{bean.song[4].title}

     查詢運算符(.?)  #{bean.songs.?[artist eq 'hello']}

     比對第一個 (.^)  #{bean.songs.^[artist eq 'hello']}

     比對最後一個 (.$) : 

                                #{bean.songs.$[artist eq 'hello']}                          

     投影運算符 (.!)  #{bean.songs.![title]}

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
<bean id="carl" class="com.springinaction.springidol.Instrumentalist">
    <property name="song" value="#{kenny.song}" />
</bean>      
再學習之Spring(依賴注入).
再學習之Spring(依賴注入).
public static class FieldValueTestBean {
    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;
    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }
    public String getDefaultLocale() {
        return this.defaultLocale;
    }
}      

Spring為SpEL創造了兩種特殊的選擇屬性方式:systemEnvironment和systemProperties. 

systemEnvironment包含了應用程式所在機器上的所有環境變量。 

systemProperties包含了java應用程式啟動時所設定的所有屬性。

再學習之Spring(依賴注入).
再學習之Spring(依賴注入).

五、JUnit 測試

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = JavaConfig.class) 加載配置類
@ContextConfiguration(locations = "classpath:applicationContext.xml") //加載配置檔案
public class Test02 {

    @Resource(name = "iceCream")
    private Dessert dessert;

    @Test
    public void test02(){
        dessert.sys();
    }

}