Spring 核心技術

Part III. Core Technologies

github 地址 https://github.com/yanghj7425/article/blob/master/spring/framework/PartIII.CoreTechnologies.md

發(fā)現一個小問題:github 的目錄在 簡書這里不能用。我也就不改了刁岸,小伙伴們可以去我github看哈 : ??

目錄

這部分的參考文檔覆蓋的所有技術寡夹,都是 Spring 框架的組成部分关霸。
其中最主要的是 Spring 框架中的反轉控制(IOC)容器骤肛。Spring 框架的 IoC 容器的徹底的處理是緊隨其后的是 Spring 的全面覆蓋的面向方面編程(aop)技術印颤。Spring 框架有它自己的AOP框架盒发,它的概念是容易理解的兔沃,他成功的解決了80% 的企業(yè)級 AOP 編程的需求橄碾。
……

IoC 容器

Spring IOC 容器和 Java bean 的介紹

這章覆蓋了Spring 框架的 IoC 的實現規(guī)則卵沉。IoC 因依賴注入兒眾所周知。這也是定義它自己依賴的方式的一種方式法牲,就是這樣和其他的對象跟它一起工作史汗,只有通過構造函器的參數,參數傳遞給一個工廠方法或者屬性拒垃,在它的構造器或者工廠方法返回之后設置一個實例停撞。當它創(chuàng)建類的時候容器注入這些依賴關系。這個過程是反轉的基礎悼瓮,因此叫做反轉控制(IoC)戈毒,類它自己控制實例或者它依賴的位置通過直接構造類,或一個機制如服務定位器模式横堡。


org.springframework.beans 和 org.springframework.context 包是 Spring 框架Ioc的基礎埋市, BeanFactory 接口提供了一個高級的配置機制關于管理任何類型的對象。ApplicationContext 是 BeanFactory 的一個子接口命贴,他更容易的集成了Spring AOP 的特性: 消息資源控制道宅,公共事件和應用層指定的上下文例如:WebApplicationContext 被用在 Web 應用中。


總而言之胸蛛,BeanFactory 提供了框架的配置和基礎的功能污茵,ApplicationContext 添加了更多企業(yè)級的特性。ApplicationContext 是一個 BeanFactory 的一個完美超類葬项,它僅僅被用在這章描述 Spring 的 Ioc 容器泞当。更多用 BeanFactory 代替 ApplicationContext 的信息,參考7.16 BeanFactory玷室。

在 Spring 中零蓉,對象在應用中主要的形式和被 IOC 容器管理叫做 Bean,一個 Bean 是一個對象穷缤,它是一個實例敌蜂,一個集合和任何被 Ioc 容器管理的。另外津肛,一個 bean 不過是在你應用中眾多對象中的一個章喉。Beans ,他們之間的依賴關系唄映射到一個配置的數據文件里面被容器使用。

容器綜述

org.springframework.context.ApplicationContext 接口代表了 Spring 的 Ioc 容器和負責實例化秸脱、配置落包、集成上述提到的 bean。容器得到他的實例對于每一個通過讀配置數據文件實例摊唇、配置咐蝇、裝配的對象。數據配置文件描述在 XML 文件巷查、Java 注解有序、或者 Java 代碼。它允許你表達你的對象然后組成你的應用并且豐富對象之間的依賴接口岛请。


一些實現了 ApplicationContext 接口被 Spring 提供為沒有內建的旭寿。在單例應用中,通常是創(chuàng)建一個 ClasspathXmlApplication 或者FileSystemXmlApplicationContext 實例崇败。XML 已經形成傳統(tǒng)的格式盅称,為了配置的數據文件你可以指示容器使用 Java 注解或者代碼作為數據元格式通過提供少量的配置文件起聲明使能支持這些添加的元數據格式。

在大多數應用的場景后室,顯示的用戶代碼對于一個實例或者多個實例不是必須要的缩膝。例如,在 web 應用場景中咧擂,簡單的 8 行樣本文件 web 描述文件在 web.xml 中已經是足夠的逞盆。如果你正在用 Spring Tool Suite 這樣的開發(fā)環(huán)境這樣的樣本配置文件可以可以在點幾下鼠標或者少量的按鍵后容易的被創(chuàng)建。

在 ApplicationContext 被創(chuàng)建和初始化之后松申,你的應用類和配置數據將被整合,你擁有一個配置完整和可以執(zhí)行的系統(tǒng)和應用俯逾。

配置元數據

Spring 的 IOC 容器需要一個格式的來配置元數據贸桶;這個元數據代表你作為一個應用開發(fā)者告訴 Spring 容器怎樣在應用中實例化、配置和集成對象桌肴。

注意

基于 XML 的元數據不是唯一的配置方式皇筛。Spring Ioc 容器已經完全從某一種形式的元數據中解耦。目前坠七,許多的開發(fā)者在 Spring 應用中選擇使用 基于 Java 配置的方式.

  1. XML 配置通過在 <beans> 元素內使用 <bean> 元素來定義水醋。

  2. Java 配置通過使用 @Configuration 注解類,在類內部使用 @Bean 注解方法彪置。

    • 關于 Java 配置的注解:
      • @Configuration
      • @Bean
      • @Import
      • @DependsOn

實例化一個容器

<span id="InstantiatingContainer"> </span>
實例化一個 Ioc 容器是簡單的拄踪,ApplicaationContext 構造器是一個實際的資源字符串允許從多種外部資源加載配置元數據,如:本地文件系統(tǒng)拳魁,Java ClassPath 等等惶桐。

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

合并基于XML配置的元數據

可以使用 ApplicationContext 構造器從所有的 XML 碎片(多個 XML 文件)中加載 bean 的定義。構造器可以持有多個 Resources 路徑,就像上面這樣姚糊。另外也可以使用 <import/> 元素:

<!-- 文件名稱 : beans.xml -->
<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>
    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

注意:

所有的路徑都是相對于定義文件做的導入贿衍。beans.xml 和 servives.xml 在同一個路徑下。

使用 ../ 引用一個父目錄里面的文件是可能的救恨,但是不推薦贸辈。

尤其是不推薦使用 classpath: URL(如:classpath:../services.xml),這樣運行時會選擇最近的根路徑然后再查找父路徑。Classpath 配置更改可能會選擇不同加載的不同的一個不正確的目錄肠槽。

如果使用絕對路徑:比如裙椭,file:c:/config.xml。 一般來說:最好保持一個對于這些資源路徑的相對引用署浩,可以通過${}占位符揉燃,JVM 在運行時解析屬性。

容器的使用

Application 使你可以讀到 bean 的定義和訪問他們通過下面的方式:

// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();

Bean的概念

容器的 bean 被綁定元數據創(chuàng)建筋栋,例如炊汤,xml 里面的 <bean/> 定義的。在容器內部這些 bean 被定義為一個包含元數據的 BeanDefinition 對象弊攘,通常包含以下信息:

  • 一個指定的包名:通常定義一個指定的實現類 bean抢腐。
  • bean 的行為配置元素,bean 在容器里面的狀態(tài)(作用域襟交、生命周期回掉 等等)迈倍。
  • bean 之間的依賴關系。
  • 在新創(chuàng)建的對象中配置其他的設置捣域,比如啼染,用于管理連接池的連接數、連接池的大小焕梅。

此外迹鹅,Spring 也允許用戶注冊一個已經存在的被其他容器創(chuàng)建的對象。通過 getBeanFactory() 訪問 ApplicationContext 的 BeanFactory贞言,它返回的 BeanFactory 實現了的 DefaultListableBeanFactory 支持通過 registerSingleton() 和 registerBeanDefinition() 方法注冊斜棚。

在 xml 的配置里面,使用 id 或 name 屬性來指定一個標識符该窗。如果想要給 bean 起別名弟蚀,可以使用 name 屬性。如果沒有指定 id/name 容器會自動為 bean 生成一個唯一的名稱酗失。然而如果想要明確的通過名稱來引用义钉,就必須提供一個名稱。

通過 <alias/> 元素可以用不同的名稱訪問同一個對象:

<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />

這樣就有三個名稱指向同一個對象级零。

通過構造器實例化

通過默認構造器實例化 bean :

    <bean id="exampleBean" class="examples.ExampleBean"/>
    <bean name="anotherExample" class="examples.ExampleBeanTwo"/>

通過靜態(tài)工廠方法實例化

創(chuàng)建的類可以通過 factory-method 屬性指定方法名稱來通過指定的靜態(tài)工廠方法來實例化:

xml 配置:

    <bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

Java Demo:

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}
    public static ClientService createInstance() {  //靜態(tài)工廠方法
        return clientService;
    }
}

使用實例工廠方法來實例化

和靜態(tài)工廠方法類似断医,實例工廠方法從容器中調用一個 bean 的非靜態(tài)方法來創(chuàng)建一個新的 bean滞乙。為了使用這種機制,保持 class 屬性為空鉴嗤,factory-bean 屬性里面指定當前容器中被調用的 bean 對象斩启,factory-method 指定方法名稱。

  • 一個實例化工廠可以實例化多個 bean醉锅。

    xml 配置:
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

Java Demo:

public class DefaultServiceLocator {
    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() { // 非靜態(tài)方法
     return clientService;
    }
}

依賴

一個典型的企業(yè)級應用不會由單一的一個對象組成兔簇。甚至一個簡單的應用也會由多個對象一起工作。

依賴注入

依賴注入是通過定義對象來定義他們的依賴關系硬耍,這樣垄琐,其他的對象就可以一起工作,只有通過構造器參數经柴、工廠方法參數或屬性設置一個對象狸窘,在對象被構造或者工廠方法返回之后。當 bean 被創(chuàng)建的時候坯认,容器就會注入這些依賴翻擒。

構造器注入

構造器的注入是通過容器調用一個有參數的構造器完成的,每一個參數代表一個依賴關系牛哺。

構造器使用參數類型來匹配當前的構造器陋气。如果在構造器參數定義上沒有歧義,在 bean 被構建的時候將按照構造器參數的順序實例化:

package x.y;
public class Foo {
    public Foo(Bar bar, Baz baz) {
    // ...
    }
}

沒有歧義存在引润,假設 Bar 和 Baz 不存在繼承關系巩趁。就可以像下面這樣配置:

<beans>
    <bean id="foo" class="x.y.Foo">
        <constructor-arg ref="bar"/>
        <constructor-arg ref="baz"/>
    </bean>
    <bean id="bar" class="x.y.Bar"/>
    <bean id="baz" class="x.y.Baz"/>
</beans>

當一個類被引用,類型匹配就可以出現淳附。當使用簡單類的時候议慰,比如 <value>true</value>,沒有類型值得幫助 Spring 不能通過類型匹配:

package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
    private int years;
    // The Answer to Life, the Universe, and Everything
    private String ultimateAnswer;
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}

如果明確指定了構造器參數的類型燃观,容器可以通 type 屬性類類型匹配一些簡單類型:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

使用下標屬來明確指定構造器的參數:

<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg index="0" value="7500000"/>
    <constructor-arg index="1" value="42"/>
</bean>

基于setter的賴注入

基于 setter 的依賴注入是通過在 bean 調用無參構造器或者無參數的靜態(tài)工廠方法實例化你的 bean 之后調用 setter 方法褒脯。

下面的例子展示了你可以單純的通過 setter 注入來實現依賴注入:

public class SimpleMovieLister {
    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;
    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }   
// business logic that actually uses the injected MovieFinder is omitted...
}

ApplicationContext 對 bean 的管理支持構造器注入和 setter 注入。也可以通過構造器注入容器內已經存在的依賴缆毁。

  • 在 setter 方法中使用 @Required 注解可以使屬性成為必須的依賴。
  • Spring 推薦使用構造器注入它使一個實現的應用組件作為一個不可變的對象并確保必須的依賴不為空到涂。
  • setter 注入使可以選擇的依賴項的首選脊框。
    • 它可以設置一個默認值。
    • setter 注入使對象可以重新配置或者重新注入践啄。

依賴解析過程

容器會這樣執(zhí)行去解決依賴問題:

  • ApplicationContext 會被配置元數據描述的 bean 創(chuàng)建和初始化浇雹。配置元數據可以通過 xml、Java code屿讽、或者注解昭灵。
  • 對于每一個 bean 它的依賴關系通過屬性吠裆、構造參數、靜態(tài)工廠方法的參數表達烂完。當這個 bean 被實際創(chuàng)建的時候這些依賴關鍵會被提供給 bean试疙。
  • 每一個屬性或構造器參數是被實際定義的值設置的,或者是引用容器里面其他的 bean抠蚣。
  • 每一個屬性或者構造器參數會從指定的格式轉變回屬性或構造器參數實際的類型祝旷。默認的,Spring 提供默認的值轉變從一個 String 到一個編譯類型嘶窄,比如:int怀跛、long、String柄冲、boolean等等吻谋。

循環(huán)依賴

使用構造器注入的時候會產生一個循環(huán)依賴的場景:A 依賴 B,B 依賴 A现横。如果這樣配置 Spring 在運行時會拋出一個 BeanCurrentlyInCreationException漓拾。

解決辦法: 使用 setter 注入, 雖然不推薦长赞,但是使用 setter 注入可以配置一個循環(huán)依賴晦攒。

  • spring 的 bean 默認采用預實例化和單例的方式:
    • 在這個 bean 實際被實例化之前預先分配一些時間和空間,這樣可以發(fā)現一些 ApplicationContext 配置過程中的問題得哆「眨可以改變默認的行為 lazy-initialize 而不是 pre-instantiated;或者改變作用域贩据,為一個非單例的栋操。

依賴注入的列子

  • xml 配置的元數據基于 setter 方法的注入:

    xml 實例:

    <bean id="exampleBean" class="examples.ExampleBean">
        <!-- setter injection using the nested ref element -->
        <property name="beanOne">
            <ref bean="anotherExampleBean"/>
        </property>
        <!-- setter injection using the neater ref attribute -->
        <property name="beanTwo" ref="yetAnotherBean"/>
        <property name="integerProperty" value="1"/>
    </bean>
    <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
    
    

    java 實例:

        public class ExampleBean {
            private AnotherBean beanOne;
            private YetAnotherBean beanTwo;
            private int i;
            public void setBeanOne(AnotherBean beanOne) {
                this.beanOne = beanOne;
            }
            public void setBeanTwo(YetAnotherBean beanTwo) {
                this.beanTwo = beanTwo;
            }
            public void setIntegerProperty(int i) {
                this.i = i;
            }
        }
    
    
  • xml 配置的元數據基于構造器的注入:

    xml 實例:

        <bean id="exampleBean" class="examples.ExampleBean">
        <!-- constructor injection using the nested ref element -->
            <constructor-arg>
                <ref bean="anotherExampleBean"/>
            </constructor-arg>
            <!-- constructor injection using the neater ref attribute -->
            <constructor-arg ref="yetAnotherBean"/>
            <constructor-arg type="int" value="1"/>
        </bean>
        <bean id="anotherExampleBean" class="examples.AnotherBean"/>
        <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
    

    java 實例:

        public class ExampleBean {
            private AnotherBean beanOne;
            private YetAnotherBean beanTwo;
            private int i;
            public ExampleBean(
            AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
                this.beanOne = anotherBean;
                this.beanTwo = yetAnotherBean;
                this.i = i;
            }
        }
    
    
  • 使用靜態(tài)工廠方法注入:

    • 靜態(tài)工廠方法使用時,可以用 <constructor-arg> 元素來指定參數饱亮。

    xml 實例:

        <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
            <constructor-arg ref="anotherExampleBean"/>
            <constructor-arg ref="yetAnotherBean"/>
            <constructor-arg value="1"/>
        </bean>
        <bean id="anotherExampleBean" class="examples.AnotherBean"/>
    
    

    java 實例:

        public class ExampleBean {
        // a private constructor
            private ExampleBean(...) {
            //...
            }
            // a static factory method; the arguments to this method can be
            // considered the dependencies of the bean that is returned,
            // regardless of how those arguments are actually used.
            public static ExampleBean createInstance (
                AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
                ExampleBean eb = new ExampleBean (...);
                // some other operations...
                return eb;
            }
        }
    
    

依賴配置細節(jié)

你可以定義屬性和構造參數引用其它容器內管理的 bean矾芙。在 xml 的配置中,<property/><constructor-arg/> 元素來支持這個功能近上。

你可以配置一個 java.util.Properties 實例:

<bean id="mappings"
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <!-- typed as a java.util.Properties -->
    <property name="properties">
        <value>
        jdbc.driver.className=com.mysql.jdbc.Driver
        jdbc.url=jdbc:mysql://localhost:3306/mydb
        </value>
    </property>
</bean>
  • Spring 容器通過 PropertyEditor 機制剔宪,轉變 <value/> 元素內部的文本到一個 java.util.Properties 實例。 Spring 團隊推薦使用嵌套 <value/> 元素來覆蓋 value 屬性壹无。

idref元素

idref 元素是一個簡單的錯誤保證方式葱绒;通過其它容器里內 bean 的 id 來引用元素到一個<constructor-arg/><property/> 元素。

xml 實例:

   <bean id="theTargetBean" class="..."/>
   <bean id="theClientBean" class="...">
       <property name="targetName">
           <idref bean="theTargetBean"/>
       </property>
   </bean>

上面的實例和下面的實例是完全等價的:

   <bean id="theTargetBean" class="..." />
   <bean id="client" class="...">
       <property name="targetName" value="theTargetBean"/>
   </bean>
  • 更加推薦使用 idref 標簽斗锭,這個標簽運行容器在發(fā)布時校驗引用地淀、名稱是否確實存在。

注意

自從 4.0 beans xsd 以后 idref 元素就不提供 local 屬性岖是,不提供對一個常規(guī)的 bean 的引用帮毁。簡單的改變已經存在的 idref local 引用為 idref bean 就可以升級為 4.0 語法实苞。

一個共同的地方(最新的和 spring 2.0),<idref/> 元素帶來的值是在一個攔截器里面作為 ProxyFactoryBean 被定義的烈疚。使用 <idref/> 元素當你用攔截器去指定攔截元素的時候會保證你不會拼寫錯誤黔牵。

內部bean

一個 <bean/> 元素在 <property/> 或者 <constructor-arg/> 元素內部定義,叫作內部 bean胞得。

<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

  • 內部類總是匿名的荧止,隨著外部類的創(chuàng)建而創(chuàng)建的。

使用depends-on

depends-on 屬性可以現實的強制一個或者多個 bean 阶剑,在使用了 depends-on 的這個元素初始化之前跃巡。

xml 實例,依賴單個 bean:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

xml 實例,依賴多個 bean:

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
    <property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

注意:

依賴定義的 depends-on 依賴關系首先被銷毀牧愁,在 bean 銷毀之前素邪。因此,depends-on 也可以控制關閉順序猪半。

方法注入

在大多數引用場景兔朦,容器里面的 bean 都是單例的∧ト罚可以通過依賴關系注入沽甥,但是當兩個 bean 作用域不一樣的時候:一個單例需要一個非單例的 bean,此時單例 A 將在創(chuàng)建的時候添加非單例的 B 的依賴乏奥。但是 A 只會在創(chuàng)建的時候被調用摆舟,并且實例化 B。這樣 B 就不能及時得到一個新的實例邓了。

一個解決方法是放棄一些反轉控制特性恨诱,可以讓 A 通過實現 ApplicationContextAware 接口,使 bean 讓容器保持警覺骗炉,這樣通過 getBean() 方法獲取到的 bean 就都是新的實例了照宝。

查找方法注入

查找方法注入是一種能讓容器覆蓋當前容器內管理的 bean 的方法的能力。Spring 框架從 CGLIB 庫使用字節(jié)碼動態(tài)生成一個子類覆蓋這個方法句葵。

注意:

  • 為了使動態(tài)子類工作厕鹃,Spring 容器將子類化的 bean 不能是 final 的,將要覆蓋的方法也不能是 final 的乍丈。

bean域

創(chuàng)建一個 bean 的定義時熊响,就創(chuàng)建了一個通過 bean 的定義創(chuàng)建實例的處方。bean 的定義是一個處方這是一個重要的概念诗赌,因為著意味著:作為一個類,你可以從一個處方中創(chuàng)建多個對象的實例秸弛。

你不僅可以控制被插入對象的個別的 bean 的定義及其各種依賴關系和配置的值铭若,而且對象的域從一特殊的 bean 定義創(chuàng)建洪碳。這種方式時非常有用和高度靈活的,你可以通過配置來替代 class 的水平來選擇你所創(chuàng)建對象的作用域叼屠。Beans 可以被定義和發(fā)布在多個作用域中:Spring Framework 提供 7 種作用域瞳腌,5 種是可用的如果你使用 ApplicationContext。

作用域 描述
singleton 默認的, IOC 容器內只有一個實例對象
prototype 對任意數量的 bean 定義
request 對于每一個 HTTP 請求都創(chuàng)建一個單獨的 bean镜雨。只有上下文是一個Spring ApplicationContext 的 web-aware嫂侍,時才是有效的
session bean 定義在 session 的生命周期中,只有上下文是一個Spring ApplicationContext 的 web-aware,時才是有效的
globalSession global HTTP session 的生命周期有一個單獨的 bean 定義荚坞。典型的是挑宠,它只在使用 Protlet 的 web 中才有定義。只有上下文是一個Spring ApplicationContext 的 web-aware颓影,時才是有效的
application ServletContext 的生命周期有一個單獨的 bean 定義各淀。只有上下文是一個Spring ApplicationContext 的 web-aware,時才是有效的
websocket WebSocket 的生命周期是一個單獨的 bean 定義诡挂。只有上下文是一個Spring ApplicationContext 的 web-aware 時才是有效的

單例作用域

被管理的單例 bean 只共享一個實例碎浇,所請求跟 id 或者 ids 與 Spring 容器管理的 bean 定義匹配的實例會被返回。
參考定義如下:

<bean id="accountService" class="com.foo.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

原型作用域

非單例的璃俗,原型作用域發(fā)布的結果是每一次請求都創(chuàng)建一個新的實例奴璃。換言之,bean 被注入到另一個 bean 中或者通過 getBean() 方法在容器內請求城豁。作為一個規(guī)則苟穆,對所有 有狀態(tài) 的 bean 使用原型作用域;對 無狀態(tài) 的 bean 使用單例作用域钮蛛。參考定義如下:

<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>

單例作用域和原型作用域的依賴

當在一個單例作用域的 bean 依賴一個 原型作用域的 bean 的時候鞭缭,要意識到依賴關系是在實例化的時候確定的。因此如果依賴注入一個原型作用域的 bean 到一個單例作用域的 bean 中魏颓,一個新的原型 bean 將被實例化然后注入到單例 bean 中岭辣。這個原型實例是為單例作用域 bean 提供的唯一的實例。

然而甸饱,假設你想要一個單例作用域 bean 在運行時需要一個新的原型作用域實例。你不能依賴注入一個原型作用域的 bean 到你單例作用域的 bean 中叹话,因為注入只出現一次偷遗,當 Spring 容器初始化單例作用域 bean 的時候就會解析和注入它們的依賴關系。如果在運行時不止一次需要一個新的原型作用域驼壶,請參考方法注入氏豌。

request、session 热凹、global session泵喘、application 和 WebSocket 域

request, session,global session, application 和 websocket 域只有你使用了有 web-ware 的 Spring ApplicationContext 實現(比如 XmlWebApplication)泪电。如果你在一個正常的 Spring IOC 容器中使用這些作用域比如: ClassPathXmlApplicationContext,一個 IllegalStateException 將要被拋出抱怨說:不知道這個 bean 的作用域。

初始化Web配置

為了支持 request, session,globalSession,application 和 websocket 作用域,在定義 bean 之前一些輔助的初始化配置是必須的沿腰。(這樣的初始化在標準的單例作用域和原型作用域中是不需要的。)

你如何完成這個初始化步驟取決于你特定的 Servlet 環(huán)境突诬。

如果你訪問作用域在 Spring Web MVC 中,實際上請求時被 DispatcherServlet 或者 DispatcherPortlet 處理芜繁,然后就沒有必須的步驟了:DispatcherServlet 和 DispatcherPortlet 已經暴露了所有相關狀態(tài)旺隙。

如果你使用 Servlet 2.5 的 web 容器,請求被 Spring 的 DispatcherServlet 之外的處理浆洗,你需要注冊一個org.springframework.web.context.request.RequestContextListener催束。對于 Servlet 3.0+,可以通過編程的方式實現 WebApplicationInitalizer 接口伏社。另外抠刺,對于更老的容器,添加下面的聲明在應用的 web.xml 文件中:

<web-app>
    <listener>
        <listener-class>
        org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
</web-app>

另一種選擇摘昌,如果你的監(jiān)聽啟動有一些問題速妖,可以考慮使用 Spring 的 RequestContextFilter。這個過濾器圍繞著 web 應用的配置映射聪黎。所以級可以更加可適應的改變它罕容。配置如下:

<web-app>
    <filter>
        <filter-name>requestContextFilter</filter-name>
        <filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>requestContextFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

DiapatcherServlet、RequestContextListener稿饰、RequestContextFilter 都做著同樣的事情锦秒,也就是綁定 HTTP 請求對象到一個請求服務的 Thread。這使得在 request-scoped 和 session-scoped 上的 bean 可以沿著鏈往下喉镰。

請求作用域

對于一個 bean 的定義參考下面的 XML 配置:

    <bean id="loginAction" class="com.foo.LoginAction" scope="request"/>

Spring 容器通過使用 loginAction bean 定義 為每一個 HTTP 請求創(chuàng)建一個新的 LoginAction 實例旅择。就是這樣,loginAction bean 是 HTTP 請求水平的侣姆。你可以在 bean 已經被創(chuàng)建好的時候盡你想要的改變其內部狀態(tài)生真,因為其它的實例會從 loginAction 的定義中被創(chuàng)建將不會康健這個改變的狀態(tài);它們特殊的捺宗,對單獨的 request柱蟀。 當請求完成了處理,在 request 作用域內的 bean 會被廢棄蚜厉。


單使用基于注解驅動的組件或者 Java Config长已,@RequestScope 注解可以被用來分配一個元素到 request 作用域中。

@RequestScope
@Component
public class LoginAction{
    ///
}

會話作用域

對于一個 bean 的定義參考下面的 XML 配置:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

Spring 容器為一個獨立的 HTTP 會話通過 userPerferences 的定義創(chuàng)建一個新的 UserPreferences 實例。換言之痰哨,userPerferences bean 的有效域在 HTTP session 水平胶果。和請求作用域的 bean 一樣,你可以改變創(chuàng)建所 bean 實例的內部狀態(tài)斤斧。其他的 Http session 實例同樣用 userPerferences 定義創(chuàng)建的實例不會看見這些改變的狀態(tài),因為他們是在每一個 HTTP session 之間是獨立的霎烙。當 HTTP session 最終被丟棄撬讽,作用域到特定HTTP會話的bean也被丟棄。

當使用注解驅動或者 Java Config悬垃,@SessionScope 注解可以被分配到 session 作用域的組件游昼。

@SessionScope
@Component
public class UserPreferences {
// ...
}

Global回話作用域

參考下面的配置:

<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>

global session 作用域和標準的 HTTP session作用域非常相似,并且只能用在 基于 protlet 的 web 應用中尝蠕。protlet 特別定義了 gloabl session 的定義來在所有 protlet 之間共享烘豌,這樣組成了一個單一的 protlet web 應用。Beans 被定義在 globalSession 作用域的生命周期是 global portlet Session看彼。

如果你寫了一個標準的基于 Servlet 的 web 應用你也定義了一個或者多個 beans 具有globalSession 作用域廊佩,標準的 HTTP session 將會被使用,不會拋出異常靖榕。

  • 待續(xù) 2018-06-01 ...
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末标锄,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子茁计,更是在濱河造成了極大的恐慌料皇,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件星压,死亡現場離奇詭異践剂,居然都是意外死亡,警方通過查閱死者的電腦和手機娜膘,發(fā)現死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門逊脯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人劲绪,你說我怎么就攤上這事男窟。” “怎么了贾富?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵歉眷,是天一觀的道長。 經常有香客問我颤枪,道長汗捡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮扇住,結果婚禮上春缕,老公的妹妹穿的比我還像新娘。我一直安慰自己艘蹋,他們只是感情好锄贼,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著女阀,像睡著了一般宅荤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上浸策,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天冯键,我揣著相機與錄音,去河邊找鬼庸汗。 笑死惫确,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蚯舱。 我是一名探鬼主播改化,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晓淀!你這毒婦竟也來了所袁?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凶掰,失蹤者是張志新(化名)和其女友劉穎燥爷,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體懦窘,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡前翎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了畅涂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片港华。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖午衰,靈堂內的尸體忽然破棺而出立宜,到底是詐尸還是另有隱情,我是刑警寧澤臊岸,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布橙数,位于F島的核電站,受9級特大地震影響帅戒,放射性物質發(fā)生泄漏灯帮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望钟哥。 院中可真熱鬧迎献,春花似錦、人聲如沸腻贰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽银受。三九已至践盼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宾巍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工渔伯, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留顶霞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓锣吼,卻偏偏與公主長得像选浑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子玄叠,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355