Spring Framework(更新中)(最近更新:2019-12-30)


Spring Framework概述

history, design philosophy, feedback, getting started.

Version 5.1.7.RELEASE

轉載請注明出處

注意:
bean definition 為bean 的定義,在內容中沒有翻譯,因為bean definition 常常作為一個整體,如果單詞加漢字會影響整體理解。

Spring使得可以很容易的創建Java企業應用。在一個企業環境中,他完全使用Java語言提供你需要的所有東西,並且也支持基於JVM的Groovy和Kotlin,同時可以根據應用需要靈活的創建多種多樣的架構設計。從Spring Framework 5.1開始,Spring需要JDK8+(Java SE 8+),並為JDK11 LTS提供開箱即用的支持。

1. 我們說“Spring”的時候指的是什么

“Spring”這個詞在不同的語境中有不同的意思,最初,它可以被用來指Spring Framework 項目本身。之后其他的一些Spring 項目基於Spring Framework 創建。通常,大家說“Spring”是指整個Spring家族。這個參考文檔專注於這些的基礎:Spring Framework 。
Spring Framework 划分為多個模塊。應用可以選擇需要的模塊。這些模塊的核心是core容器,包括配置模型和依賴注入的機制。除此之外,Spring為不同的應用架構提供了基礎支持,包括消息,事務數據和持久化,Web。它也提供了基於Servlet的Spring MVC框架,並平行的提供Spring WebFlux響應式Web框架。

Spring的框架包可以基於JDK 9的模塊路徑("Jigsaw")構建。Spring Framework 5 的jar包使用"Automatic-Module-Name" 聲明項,聲明項根據固定的語法的模塊名("spring.core", "spring.context" 等等) 而不是jar文件名(jar文件遵循同樣的命名模式,只是單詞連接使用“-”而不是“.",比如"spring-core" and "spring-context")。當然Spring的框架包依然可以正常使用JDK8和JDK9+基於classpath的方式

2.Spring 和 Spring Framework 的歷史

Spring起始於2003的一個對於早期J2EE規范的回復.然而一些觀點認為Java EE 和Spring是競爭關系,Spring實際上是Java EE的補充。Spring 編程設計並沒有遵循Java EE平台規范,確切的說,它小心的選擇了一下Java EE下的獨立的規范:

  • Servlet API (JSR 340)

  • WebSocket API (JSR 356)

  • Concurrency Utilities (JSR 236)

  • JSON Binding API (JSR 367)

  • Bean Validation (JSR 303)

  • JPA (JSR 338)

  • JMS (JSR 914)

  • 以及在需要的情況下提供JTA/JCA的事務協作設置

    Spring Framework 也支持應用開發者使用依賴注入 (JSR 330)和通用注解(JSR250)規范來替換由Spring Framework提供的Spring定義的機制,
    從Spring Framework 5.0開始,Spring的最低版本是Java EE 7 (相應的其他規范比如Servlet 3.1+,JPA2.1+),同時提供了了集成在Java EE 8 (相應的其他規范比如Servlet4.0,JSON Binding API)上的新API的運行時支持。這使得Spring完全兼容比如Tomcat 8 和9,WebSphere 9 和 JBoss EAP 7.
    隨着時間的過去,應用開發中的Java EE規則也已經進行了改進,在Java EE 和 Spring 的早期時候,應用的完成需要依賴於應用服務器。現在,因為有了 Spring Boot,應用只需要很小的改動就可以以一個Devops 和雲友好的方式使用內置的 Servlet 容器。從Spring Framework 5開始,WebFlux甚至不需要直接使用Servlet API並且運行在沒有Servlet 容器的服務器上
    Spring在不斷地創新和發展。Spring Framework之外還有 Spring Boot, Spring Security, Spring Data, Spring Cloud, Spring Batch等等的一些項目。需要注意的是每個項目都有各自的源碼資源,問題追蹤和發布節奏,點擊spring.io/projects 查看Spring項目完整列表

3.設計哲學

當你學習一個框架的時候,知道她遵循了什么原則比知道她做了什么更重要,下列是Spring Framework的指導原則:

  • 為各個級別提供選擇。Spring讓你盡量推遲設計選型。例如你可以不修改代碼通過配置切換提供持久化的組件。對於很多其他的基礎組件問題或者第三方API整合也是一樣。
  • 適應各種角度。Spring擁抱靈活性,並不固執於事情應該怎么做。Spring從不同的角度支持廣泛的應用需求。
  • 保持強壯的向后兼容性。Spring的發展被小心的管理使得版本間有很少的重大變化。Spring提供了一個精心選擇過的JDK版本和第三方工具庫范圍,以方便依賴於Spring的應用和工具庫。
  • 注重API設計。Spring團隊花了很多想法和時間在使API直觀並且在眾多版本以及很長的時間都能適用。
  • 設置高標准的代碼質量。Spring Framework很注重有一個有意義的,正確的,精准的javadoc。它是極少數可以聲明干凈代碼結構且程序包之間沒有循環依賴關系的項目之一。

4.反饋與建議

對於如何使用的問題,以及判斷或調試的問題,我們推薦使用 StackOverflow,並且我們也有一個用來記錄建議標簽的問題頁,如果您確定了一個SpringFramework中的問題,或者想要提供一個特性,請使用GitHub Issues
如果您有解決方案或建議的解決方案,可以在Github上提交拉取請求。 但是,請注意對於除最瑣碎的問題以外的所有問題,我們希望在問題跟蹤器中記錄故障單,在該跟蹤器中進行討論並保留記錄以備將來參考。
有關更多詳細信息,請參閱貢獻頂級項目頁面上的准則。

5.准備開始

如果你剛開始使用Spring,你可以能希望從一個基於Spring Boot的應用開始使用Spring Framework.Spring Boot 提供了一個快捷和固定的方式去創建一個基於Spring的生產就緒狀態的應用.它基於Spring Framework,相比配置更傾向於約定的旨在使你盡快起步和運行》
你可以使用start.spring.io來創建一個基本的項目或者跟着"Getting Started" guides其中的一個做,比如開始創建一個Restful Web Service.為了容易理解,這些引導文檔更關注於引導如何創建,並且大部分都是基於Spring Boot.它們也包含了一些當你解決一個特定問題時可能考慮用到的Spring家族的其他項目.









Core 技術

IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP.

Version 5.1.7.RELEASE

這部分的參考文檔覆蓋了Spring Framework所有的必不可少的技術

1.IoC容器

這一章是關於Spring的控制反轉(IoC)容器的。

1.1 Spring IoC容器和Bean的介紹

這一章是關於Spring Framework對於控制反轉(Ioc)規范的實現。IoC也被稱為依賴注入(DI)。它是一個在構造之后或者工廠方法返回值后對象決定他們的依賴值通過構造器參數,工廠方法參數或者設置在對象實例上的參數的處理形式。容器在創建這些bean之后會注入到對象的依賴.這種處理方式從根本上是bean直接通過類的構造或者比如服務定位器模式的機制控制bean本身實例化或者bean依賴的定義的方式的反轉(因此被稱為依賴反轉)

org.springframework.beans 和 org.springframework.context 包是Spring Framework IoC容器的基礎。BeanFactory接口提供了一個可以管理任意對象類型的高級配置機制。ApplicationContext 是BeanFactory的一個子接口,它增加了:

  • 集成了Spring AOP 特性
  • 消息資源管理(針對國際化的用戶)
  • 事件發布
  • 對於Web應用用戶應用層特有的上下文WebApplicationContext

簡單的說,BeanFactory 提供了配置框架和基礎特性,ApplicationContext添加了一些針對企業的特性.ApplicationContext是BeanFactory的完整的超集所以在本章關於Spring IoC容器的講解中只使用ApplicationContext.獲得關於BeanFactory的更多信息,seeThe BeanFactory
在Spring中,主要構成應用的對象被Spring IoC容器所管理,被稱為bean。一個bean是已經實例化,已經組合過的並且被Spring IoC管理的對象。實際上,一個bean只是應用中眾多對象中簡單的一個。Bean以及他們的依賴都是容器使用配置元數據反射得到的。

1.2 容器概述

org.springframework.context.ApplicationContext 接口即是指IoC容器,它負責實例化,配置,和組合bean.容器通過讀取配置元數據獲得去實例化,配置和組合什么對象的指引。配置元數據具體是指XML,Java注解或者Java代碼。它可以讓你描述那些組成應用的對象和他們之間的完整的互相依賴關系。

Spring提供了幾個繼承自ApplicationContext的接口,在獨立的應用中,通常創建一個ClassPathXMLApplicationContext或者FileSystemXMLApplicationContext.雖然XML是定義配置元數據的傳統格式,你也可以通過少量XML配置去聲明開啟額外的元數據格式使得容器使用Java注解或者代碼作為元數據格式。

在大多數應用場景中,用戶不必要顯性的編寫代碼來實例化一個Spring容器的一些實例。例如,在Web應用場景中,通常需要在應用的web.xml文件中添加簡單的大約8行的模板web標識符XML(see Convenient ApplicationContext Instantiation for Web Applications).如果你使用Spring Tool Suite(一個基於Eclipse的開發環境),你可以簡單的通過點按幾下鼠標或鍵盤來創建這些模板。

下圖展示了Spring如何工作的一個高層的視角.首先你的應用中的類通過配置元數據組合,然后在ApplicationContext被創建和初始化后,你就有了一個完整配置過和可執行的系統或應用。

Figure 1. The Spring IoC container
Figure 1. The Spring IoC container

1.2.1 配置元數據

如前圖所展示的一樣,Spring IoC 容器 消費 消費這個詞類似於生產者消費者模式中的消費者消費的概念了一份配置元數據,這個配置元數據描述了作為一個應用開發者的你告訴Spring 容器如果去實例化,配置和組合你應用中的對象。

配置元數據傳統的方式是用簡單和直觀的XML格式,這一章大部分也使用XML格式來表現Spring IoC 容器的概念和特性。

基於XML的元數據並不是配置元數據可以接收的唯一格式,Spring IoC容器本身與元數據的編寫方式是解耦的。現在,很多開發者為他們的Spring應用選擇了使用基於Java注解的方式

獲取更多關於為Spring 容器使用其他格式的信息,請看:

  • 基於注解配置:Spring 2.5提出了基於注解配置元數據的支持
  • 基於Java配置:從Spring3.0開始,很多Spring JavaConfig項目提出的特性變成了核心Spring Framework的一部分。因此你可以使用Java 在應用里類的外面定義bean的方式而不是XML文件的方式。要使用這些新的特性,請看@Configuration,@Bean,@Import,和@DependsOn注解

Spring 配置 包括了容器必須管理的至少一個,實際上超過一個的bean definition。基於XML配置元數據通過頂級元素 下的 元素配置這些bean。Java配置通常使用@Bean,直接在一個@Configuration類里面的方法上。

這些bean definition與組成你應用的實際對象相一致。通常,你定義service層對象,數據訪問對象(DAOs,data access objects),比如Struts Action實例的展示對象,比如Hibernate SessionFactories的基礎對象。JMS隊列等等。通常,不為容器中的領域對象進行細粒度配置,因為通常是DAO和業務邏輯負責創建和使用領域對象。不過你可以使用讓Spring集成AspectJ來配置已經在IoC容器之外創建的對象。請看Using AspetJ to dependency-inject domain objects with Spring.

下面例子展示了基於XML配置元數據的基本結構:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

id 屬性標識每個bean definition的字符串。
class 屬性定義了bean的類型,需要使用完全限定類名。

id 屬性的值指向協作對象,引用寫作對象的XML沒有展示在例子中,請看Dependencies獲取更多信息。

1.2.2實例化容器

提供給ApplicationContext構造器的定位路徑是一些讓容器通過多種形式的外部資源載入的資源字符串。比如一個本地文件系統路徑,Java CLASSPATH路徑等等

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

學習過Spring的IoC容器后,你可能希望知道更多關於Spring的Resource抽象概念(如Resources),Resource提供了一個便捷的方式去讀取通過URI語法定義的InputStream。尤其是:如Application Contexts and Resource Paths中介紹的Resource路徑被用來構建應用上下文。

下面例子展示了service層(services.xml)配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- services -->

    <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
        <property name="accountDao" ref="accountDao"/>
        <property name="itemDao" ref="itemDao"/>
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for services go here -->

</beans>

下面文件展示了數據訪問對象(daos.xml)文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="accountDao"
        class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions for data access objects go here -->

</beans>

在上面的例子中,服務層包含了PetStoreServiceImpl類和兩個類型為JpaAccountDaoJpaItemDaod的數據訪問對象(基於JPA ORM(Object-Relational Mapping,對象關系映射)規范)。property name元素指向JavaBean屬性的名稱(即在這個bean的類相對應的屬性名稱),ref元素指向另一個bean definition的名稱(即id屬性)。idref元素表示了協作對象間的依賴關系。獲取更多關於配置一個對象依賴的信息,請看依賴

.

基於XML構成配置元數據

跨多XML文件獲取bean definition很有用,通常,一個單獨的XML配置文件代表了一個架構中邏輯層或者模塊。

你可以使用應用上下文構造器從所有的這些XML分段來載入bean definition。這個構造器需要多個Resource 定位 ,就想本節開始展示的那樣。或者可以使用一個或多個<import/>元素來從其他文件導入 bean definition。下面例子展示了如何這樣做:

<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>

在上面的例子中,從三個services.xmlmessageSource.xmlthemeSource.xml文件中載入了外部的bean definition。所有的定位路徑都關聯到執行導入的定義文件。所以作為執行導入的文件services.xml必須處於同一目錄下或者classpath路徑下,而messageSource.xmlthemeSource.xml必須在導入文件位置下的resource里。你可以看到,路徑頭部的/被過濾了。然而,這些路徑是相對路徑,最好不要在頭部使用/。根據Spring Schema,被導入文件的內容,包括頂級的<beans/>元素必須是正確的XML bean definition。

使用相對路徑“../"在父目錄里引用文件是可以做到,但是不推薦這樣做。這樣做會創建對當前應用程序外部文件的依賴。這種引用尤其不推薦使用在classpath:路徑(例如,classpath:../services.xml),因為運行時解析處理會選擇“最親近的(nearest)”classpath然后查找其父級目錄。classpath配置更改可能導致選擇到一個與之前不同的錯誤的目錄。

你可以使用完全限定資源路徑代替相對路徑:比如,file:C:/config/services.xml或者classpath:/config/services.xml。然而,你需要意識到你應用的配置和確定的絕對路徑是耦合在一起的。比如通過使用在運行時針對JVM系統屬性解析的 “${...}”占位符來保證為這些絕對路徑保持間接聯系通常會更好一些。

命名空間本身提供了import指令功能。Spring提供的一系列XML命名空間提供了除了簡單的bean definition之外的更多配置功能---比如contextutil命名空間。

Groovy Bean Definition DSL

作為進一步外部配置元數據的例子,bean definition也能在從Grails框架引進的Groovy Bean Definition DSL 中表達。通常這樣的配置存放在一個“.groovy”文件,使用下面例子中的結構:

beans {
    dataSource(BasicDataSource) {
        driverClassName = "org.hsqldb.jdbcDriver"
        url = "jdbc:hsqldb:mem:grailsDB"
        username = "sa"
        password = ""
        settings = [mynew:"setting"]
    }
    sessionFactory(SessionFactory) {
        dataSource = dataSource
    }
    myService(MyService) {
        nestedBean = { AnotherBean bean ->
            dataSource = dataSource
        }
    }
}

這種配置風格大致等同於XML bean definition,甚至支持Spring 的XML配置命名空間。它也允許通過一個importBean指令導入XML bean definition文件。

1.2.3 使用容器

ApplicationContext是一個維護各種bean和他們的依賴的高級工廠的接口。使用T getBean(String name, Class<T> requiredType)可以獲得bean的實例。

ApplicationContext可以讓你獲取bean definition 也可以訪問bean,就如下面例子展示的一樣

// create and configure beans 創建和配置bean
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();

使用Groovy配置,引導程序看起來非常相似。有一個不同的可以識別Groovy的上下文實現類(也可以識別XML bean definition)。下面例子展示了Groovy配置:

ApplicationContext context = new GenericGroovyApplicationContext("services.groovy", "daos.groovy");

最靈活的方式是GenericApplicationContext組合使用reader委托---例如讀取XML文件的XmlBeanDefinitionReader,如下例所示:

GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();

你也可以使用讀取Groovy文件的GroovyBeanDefinitionReader,如下例所示:

GenericApplicationContext context = new GenericApplicationContext();
new GroovyBeanDefinitionReader(context).loadBeanDefinitions("services.groovy", "daos.groovy");
context.refresh();

你可以在同一個ApplicationContext混合使用相匹配的reader委托從不同的數據源讀取bean definition。

之后你可以使用getBean獲取bean的實例。ApplicationContext接口有一些其他獲取bean的方法,但是,理論上你應用代碼應該永遠不使用他們。確切的說,你的應用代碼完全不應該有關於getbean()方法的調用,這樣你的應用代碼就完全不會有對於Spring API的依賴。例如,Spring與web框架的整合為各種web框架組件提供了依賴注入,比如controllers 和 JSF - 托管的bean,讓你通過元數據聲明依賴一個特定的bean(例如一個自動裝配 注解)。

1.3 Bean 概述

一個Spring IoC 容器管理一個或多個bean。這些bean是通過你提供給容器的配置元數據創建的(例如,以XML<bean/>definition的格式。

在容器內,這些bean definition表示為BeanDefinition對象,包含了下面的元數據和其他信息:

  • 一個包限定的類名稱:通常是被定義過的bean的具體實現類。
  • Bean 行為配置元素:用於聲明Bean在容器中的行為(作用域,生命周期回調等)。
  • 為了bean的運行需要對他們bean的引用;這些引用也被稱為協作者或者依賴。
  • 要在新創建的對象中設置的其他配置設置;例如,池的大小限制或要在管理連接池的bean中使用的連接數。

這個元數據轉變為構成每個bean definition的一組數據,下表描述了這些屬性:

Table 1. The bean definition

屬性 在以下內容中講解
Class (類) 實例化 Bean
Name(名) 命名 Beans
Scope(作用域) Bean 作用域
Constructor arguments(構造器參數) 依賴注入
Properties(屬性) 依賴注入
Autowiring mode (自動裝配模式) 自動裝配 協作者
Lazy initialization mode(懶加載模式) Lazy-加載 Bean
Initialization method(加載方法) 加載回調
Destruction method(銷毀方法) 銷毀回調

除了bean definition(bean definition包括如何創建指定bean的信息),ApplicaitonContext實現 也允許由用戶在容器外創建過的已經存在的對象。通過使用返回BeanFactory(DefaultListableBeanFactoryd的實現)的getBeanFactory()方法訪問應用上下文的BeanFactory .DefaultListableBeanFactoryd提供通過registerSingleton(..)registerBeanDefinition(..)方法注冊。不過標准的應用應該只使用通過常規的bean definition元數據定義過的bean。

Bean 元數據和手動提供的單例需要盡早注冊,以便容器在自動裝配或者其他introspection(內省) 步驟。雖然在某種程度上支持重寫現有的元數據和現有的單例,在運行時注冊新的bean(並發的實時訪問工廠)不是官方支持的,可能引起並發訪問異常或者bean容器中狀態不一致,或者兩者皆有。

1.3.1 命名bean

每個bean都有一個或多個標識符。這些標識符在掌握這些bean的容器內必須是獨一無二的。一個bean通常只有一個標識符,然而如果確實需要多個,多出的會被視為alias(別名)。
在基於XML的配置元數據中,使用id屬性,name屬性或者兩者一起來指示bean標識符。id屬性可讓您精確指定一個id。通常這些名字是含有字母和數字的('myBean','someService'等等),不過也可以包含特殊字符。如果想要引入這個bean的其他別名,你可以在name屬性中指定,使用(,)(;)或者空格分隔多個別名。歷史備注:Spring之前的版本,id屬性被定義為xsd:ID類型,限制了可能的字符。從3.1開始,id屬性被定義為xsd:string類型。請注意,盡管不再受XML解析器限制,bean的id仍由容器約束唯一性。
並不是必須為一個bean提供nameid。如果你沒有顯性的提供一個idname,容器會為bean生成一個唯一的名字。不過,如果你想使用名字引用bean(通過ref元素或者服務定位器樣式查找)就必須定義一個名字。之所以可以不提供一個名字跟使用內部bean自動裝配協作者有關。


Bean 命名約定

約定是當命名bean時使用標准Java約定實例化字段名。也就是bean名字開頭使用小寫字符,后面使用駝峰大小寫形式。比如accountManager``accountService``userDao``loginController等等。

統一的命名bean會使你的配置更容易閱讀和理解。並且,如果你使用Spring AOP,當通知到一系列用名字關聯的bean的時候會很有用。


在掃描classpath內的組件時候,Spring會為沒有命名的組件根據上面的約定生成bean名稱 : 實際上是使用首字母小寫的simple class name(class name 全名稱是類似java.lang.String,simple class name 是指String,首字母縮寫之后是string) 。不過如果有多個字符,並且第一二個字符都是大寫,那么將會保留原始的大小寫格式不做修改。這些規則就和java.beans.Introspector.decapitalize(Spring所使用的)定義的一樣。

在Bean Definition 外給bean起一個別名

在bean definition內,一個bean 多個名稱的話,可以使用這樣一個組合:一個名字用id屬性指定,其他的一些名字放在name屬性里。這些名字等同於同一個bean的別名,在一些情境下很有用。比如讓應用中的每個組件都通過一個指定給該組件使用的bean名稱指向一個公共依賴。
然而,在bean實際定義的地方並不總是能夠定義所有的別名。有時需要從其他地方定義一個bean的別名。在一個大型系統中配置分配在每個子系統中,每個子系統都有它自己的對象定義,通常使用這種方式。再基於XML的配置元數據中,你可以使用<alias/>元素實現這種方式。下面例子展示了如何做:

<alias name="fromName" alias="toName"/>

在這種情況下,使用別名定義后,被命名為fromName的bean也被稱為toName
例如,子系統A的配置元數據可能指向一個名字為subsystemA-dataSource的數據源。子系統B的配置元數據可能指向一個名字為subsystemB-dataSource的數據源。當這些子系統組合到主應用時,主應用指向名字為myApp-dataSource的數據源。為了使這三個名字指向同樣的對象。你可以把下面的別名定義添加到配置元數據中:

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

現在每個組件和主應用都可以通過一個唯一名字指向數據,並且和其他的定義不沖突(有效的創建一個namespace(命名空間)),實際上他們指向了同一個bean。


Java-configuration

如果你使用Java-configuration @Bean注解可以被用來提供別名。請看使用@Bean注解獲取詳情。

1.3.2 實例化Bean

bean definition 實際上是創建一個或者多個對象的菜譜。容器被請求時會查看一個命名過的bean的菜譜,然后利用bean definition里面的配置元數據去創建(或者獲得)一個實際的對象。

如果使用基於XML的配置元數據,在<bean/>元素的class屬性指定要實例化的對象的類型(或類)。class屬性(在BeanDefinition對象中實際上是Class屬性)通常是強制的(其他情況,請看使用實例工廠方法實例化Bean Definition繼承)。你可以在下面兩種方式中的一種使用Class屬性。

  • 通常,用容器本身通過反射調用其構造函數直接創建Bean的辦法來指定要構造的Bean類,這在某種程度上等同於使用new運算符的Java代碼。
  • 在不太常見的情況下,指定一個包含用來調用去創建對象的static工廠方法的實際類,容器調用類的static工廠方法去創建bean。調用static工廠方法返回的對象類型可能是同樣的類或者截然不同的其他類。

內部類名稱
如果你想要為一個static嵌套類配置bean definition,你必須使用嵌套類的二進制名稱。

例如,在com.example包中有一個類SomeThing,這個SomeThing類有一個static嵌套類,叫OtherThing,bean definition中class屬性的值應該是com.exemple.SomeThing$OtherThing.

注意在名字中使用了$字符分隔了嵌套類名稱和外部類名稱。


使用構造器初始化

當你使用構造器的途徑來創建一個bean的時候,所有的普通類都可以被Spring使用和兼容。也就是正在開發中的類並不需要集成任何特定的接口或者以特定方式編寫代碼。簡單指定bean 的類就可以了。不過,考慮到你用於該特定類的IoC的類型,你可能需要一個默認(空)的構造器。
Spring IoC 容器可以管理你想要它管理的幾乎所有的類。它並不限於管理真正的JavaBean。大多數Spring用戶更傾向於僅有一個默認的(無參)構造器並且為容器內的屬性創建的相應的setter和getter的實際的類。在你的容器內你可以有各種奇特的非bean樣式(non-bean-style)的類。如果,舉個例子,你需要使用一個絕對不遵守JavaBean規范的傳統的連接池,Spring也可以管理它。
基於XML的配置元數據,你可以像下面這樣指定你的bean的類:

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

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

獲得更多關於給構造器提供參數的機制(如果必要的話)和關於在對象構造后設置對象實例屬性的詳情,請看依賴注入

使用靜態工廠方法初始化

定義使用靜態工廠方法創建的bean時,使用class屬性指定包含static工廠方法的類,factory-method屬性指定工廠方法的名字。你應該可以調用這個方法(帶有可選參數的情況之后會有描述)然后返回一個活動(live)對象,之后該對象將被視為已經通過一個構造器創建。其中一種這樣的bean definition的用法就是在傳統的代碼中調用static工廠。
下面的bean definition 指定了通過調用一個工廠方法創建的bean。這個定義不指定返回的類型(類),只有包含工廠方法的類。在這個例子中,createInstance()方法必須是靜態方法,下面例子展示了如何指定一個工廠方法:

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

下面例子展示了一個將在bean definition之前工作的類。

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

    public static ClientService createInstance() {
        return clientService;
    }
}

獲取更多關於提供給工廠方法參數(可選)的機制和關於工廠返回的對象設置對象實例屬性的信息,請看細節上的依賴和配置.

通過工廠方法實例初始化

跟通過靜態工廠方法初始化很相似,使用實例工廠方法實例化會從容器中調用現有bean的非靜態方法來創建新bean。使用這種機制,需要保證class屬性為空,factory-bean屬性指定一個在當前(或父 或祖先)容器內的含有可以調用創建對象的實例方法的bean的名字。工廠方法的名稱設置到factory-method屬性。下面例子展示了如何配置這樣的一個bean:

<!-- 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類:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一個工廠類也可以包含多個工廠方法,就如下面例子中的一樣:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>

下面例子展示了相應的Java類:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

工廠bean可以被依賴注入(DI)管理和配置的方法,請看細節上的依賴和配置

在Spring文檔中,"factory bean"指在Spring容器中配置通過一個實例靜態工廠方法創建的bean。相比之下,FactoryBean(注意是大寫的)指一個Spring特有的FactoryBean

1.4 依賴

一個典型的企業應用不會只包含一個對象(或者Spring的說法,bean)。即使是最簡單的應用也有一些互相協作的類呈現給終端用戶一個看起來有條理的應用。下一節講解了你該如何從定義一定數量獨立的bean definition到一個對象互相協作達成目標的完全實現的應用。

1.4.1 依賴注入

依賴注入(DI)是一個過程,通過該過程,對象僅通過構造函數參數,工廠方法的參數,以及在構造或創建對象實例后或 從工廠方法返回后在對象實例上設置的屬性 來定義其依賴關系(即,與它們一起工作的其他對象)。容器會在創建bean時注入這些依賴。這種處理方式從根本上是bean直接通過類的構造或者服務定位器模式控制bean本身實例化或者bean依賴的定義的方式的反轉(因此被稱為依賴反轉)。

使用 DI 原則,代碼會更簡潔,當為對象提供依賴時解耦更加有效。對象不查找他的依賴,也不知道依賴的位置和類。由此帶來的結果是你的類變得更容易測試,特別是當一來是接口或者抽象基類的時候可以在單元測試使用stub(樁代碼)或者mock實現。

DI 有兩種實現形式:基於構造器依賴注入基於Setter依賴注入.

基於構造器依賴注入

基於構造器依賴注入是由容器調用帶有一定數量參數的構造器,每個參數代表一個依賴。帶有指定參數調用一個static工廠方法基本上是一樣的,我們的討論也以為構造器參數和static工廠方法參數是類似的。下面例子展示了一個只能通過構造器注入來依賴注入的類:

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

注意這個類沒有什么特別的地方,這只是一個 POJO (plain old java object),不依賴於容器特定的接口,基類或者注解。

構造器參數解析

構造函數參數解析匹配通過使用參數的類型進行。如果 bean definition 的構造參數沒有潛在歧義的話,則在實例化時,bean definition中定義的構造器參數順序就是這些參數提供給適當構造器的順序。思考下面的這個類:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}

假定 ThingTwoThingThree 這兩個類之間沒有繼承關系,也沒有潛在歧義存在。那么下面的配置運行正常。你也不需要指定構造器參數序號或者在 <constructor-arg/> 顯性表述類型.

<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM