本章簡單介紹spring 對於ioc原則的實現.IOC也被稱爲DI.這是一個過程,經過對象來定義他們的依賴(指那些須要和他們一塊兒工做的對象),而且只能經過構造器參數,工廠方法參數,或者被構造器或被工廠方法返回實例後進行屬性設置.(實際上是bean被設置)這個過程是反轉的基礎.控制反轉,在於bean本身控制它的依賴的實例化或直接經過類的構造器定位他們,又或者經過Service Locator pattern(服務定位模式)的機制來定義.html
org.springframework.beans和org.springframework.context包是spring ioc容器的基礎.beanFactory接口提供了高級的配置來管理任何種類的對象.ApplicationContext是beanFactory的一個下屬接口.它實現了對spring Aop功能的簡單集成,消息資源的處理(國際化中使用),事件發佈,以及應用層次的特定上下文(例如,WebApplicationContext就是在web應用中使用的).java
簡而言之,BeanFactory提供了配置框架和基礎功能,ApplicationContext添加了許多企業定製的功能.ApplicationContext是beanFactory的徹底超集,她的用法有特定的介紹.要使用BeanFactory來取代ApplicationContext,查閱7.16部分.mysql
在spring中,bean如此定義:組成你應用基礎並被spring ioC容器管理的對象;一個bean是一個能夠被實例化,被組裝,且能被Spring ioc 容器管理的一個對象.另外,一個bean是你應用中許多對象的一個簡單抽象.beans,還有在Beans中的依賴,都經過一個容器在configuration元數據反應出來.web
org.springframework.context.ApplicationContext接口實現了spring ioc 容器,主要負責以前提到的beans實例化,配置,組裝.容器獲得本身的指令去實例化,配置,以及讀取配置元數據進行組裝. 配置元數據能夠是xml,java註解,java代碼.它容許你表達組成應用的對象,以及這些對象之間複雜的關係;spring
spring提供了ApplicationContext的幾種開箱即用的實現.在單獨的應用中,它能夠輕易的建立ClassPathXmlApplicationContext或者FileSystemXmlApplicationContext的實例.Xml是定義配置元數據的傳統格式,然你可使用java註解或代碼來左右元數據格式來構造你的容器,固然這須要你提供少許的xml配置來申明對這些額外的元數據格式的支持.sql
在大部分的應用場景裏,顯示的用戶代碼不須要你去實例化一個或多個spring IOC 容器.例如,在一個web場景裏在應用的web.xml文件裏的一個簡單的八行左右的典型的web描述XML將足夠使用的.若是你使用STS的開發環境,那麼這些配置將輕易的經過一些鼠標點擊和鍵盤事件來實現.數據庫
下面的圖表是spring工做流程的高水平視圖.你的類能夠經過配置元數據來進行組裝,由於當你的ApplicationContext建立和初始化後,你將會擁有一個全面的配置和可執行的系統或應用;apache
如前面的流程圖所示,spring ioc 容器消費了一系列的配置元數據.這個配置元數據體現出你做爲一個應用開發者如何在你的應用中使spring容器來實例化,配置並裝備你應用中的對象的.api
配置元數據是在一個簡單直觀的xml格式中是傳統的生產者,本章中大部份內容都在講述這個spring ioc 容器的核心概念功能;數組
備註:XML-based 元數據不是配置元數據的惟一格式;spring IOC 容器的配置和書寫方式其實是徹底解耦的.最近不少開發者使用 基於Java的配置來配置他們的spring應用.
從spring3.0開始,許多Spring JavaConfig project提供的功能成爲了spring 核心框架的一部分.所以你可使用java而不是xml文件來定義你項目文件中額外的bean.要實現這些新功能,請查看@Configuration,@Bean,@Import和@DependsOn註解.
spring 配置至少由一個bean或者多個bean定義(他們由容器來管理);基於xml的配置會在<Beans/>級別的元素裏經過<bean/>元素來配置.java配置通常是在@Configuration 類裏使用@Bean註解加在方法之上.
與這些bean定義的真正對象組成了你的應用.通常你會定義服務層對象,數據訪問層對象(DAOs),表現層對象,例如Struts 的action實例,框架對象如Hibernate 的SessionFactories,JMS Queues等等.通常而言,應用不會再容器裏配置細粒度的領域對象,由於建立和加載這些領域對象是DAOs和服務邏輯層的責任.然而,你可使用spring 的AspectJ來在IOC 容器以外來配置對象.查看 Using AspectJ to dependency-inject domain objects with Spring.
<?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 http://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屬性的值指向協做對象.xml中的協做對象並無在這裏展現,詳情看dependencies章節
實例化一個spring容器能夠直截了當.提供給ApplicationContext構造器的定位路徑都是真實資源字符串,他們容許容器從一些額外的資源來加載配置元數據,如從本地文件系統,從java的classpath,等等. ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"services.xml", "daos.xml"});
在瞭解了Spring IOC 容器以後,你可能會問關於Spirng Resource 抽象的問題,在第8章Resource中,它會提供一個完善的機制來從用URI語法定義的位置讀取其文件的輸出流.特別指出的是Resource路徑能夠用來構造應用上下文,如8.7節中所示,"Application contexts and Resource paths";
下個例子是服務層對象的配置文件: <?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 http://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 http://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>
<beans> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/> <bean id="bean2" class="..."/> </beans>
在上述的例子中,額外的bean定義從如下三個額外文件加載--services.xml,messageSource.xml,themeSource.xml.全部的定位路徑都與定義文件的引用相關,因此引用時services.xml文件必須在相同路徑或者在classpath位置下,而messageSource.xml和themeSource.xml必須在resource位置下.如你所見,反斜槓被忽略了,你能夠給定相對路徑,這比之前要好的多.要引入的文件內容,必須包含最高級的<beans>元素,且必須使用spring Schema來驗證XML bean定義;
備註:有個能夠但不推薦的作法,在父路徑引用文件應該加上相對路徑"../".這樣作會建立一個獨立於當前應用的依賴文件.特別,這個方法不推薦在classpath中使用,當運行方案過程選擇最近的classpath根路徑,而後查找它的父路徑.classpath路徑配置改變可能會致使錯誤的結果.你可使用全資源路徑來替代相對路徑.例如,使用"file:c:/config/services.xml"或者"classpath:/config/services.xml".可是你最好使用抽象位置路徑,
ApplicationContext是一個先進工廠來管理一系列不一樣的beans和她們的依賴的註冊器.使用方法 T getBean(String name,Class<T> requiredType),你將get到你全部bean的實例
//create and configure beans ApplicationContext context =new ClassPathXmlApplicationContext(new String[]{"services.xml","dao.xml"}); //retrieve configured instance PetStoreService service=context.getBean("petStore",PetStoreService.class); //user configured instance List<String> userList=service.getUsernameList();
你可用使用getBean()來獲取你的Beans的實例.ApplicationContext接口有其餘的方法來獲取bean,但理想的應用不該該用到他們.並且,你的應用代碼不該該調用getBean()方法,而且不須要依賴spring 的API.例如,你的spring web的集成框架提供了各類web框架的controller和JSF-managed beans的依賴注入.
一個spring IOc容器能夠管理一個或多個Beans.這些Bean由你提供給容器的配置元數據來建立.例如,以XML的<bean/>形式定義的 在容器中這些bean定義被視爲BeanDefinition對象,它包含如下元數據信息:
一個包含包名的類名稱:一個bean定義的典型實現.
Bean行爲的配置元素,它描述了在容器中這個bean該如何表現(scope,lifecycle callbacks等)
Reference 協做依賴
其餘對新建對象的配置設置 ,例如一個管理鏈接池的bean中連接的數量,及其鏈接池的規模
bean定義
class instantiating beans name Naming beans scope Bean scopes constructor arguments Dependency injection properties dependency injection autowiring mode Autowiring collaborators lazy-initialization mode Lazy-initialized beans intialization method the section called "initialization callbacks" destruction method the sction called "Destruction callbacks"
另外,bean的定義包含如何去建立一個具體的bean;ApplicationContext的實現也容許你去註冊一個在IOC容器以外的已被建立的對象.它是這樣實現的: 經過getBeanFactory()方法來返回App BeanFactory實現 DefaultListableBeanFactory來訪問ApplicationContext BeanFactory. DefaultListableBeanFactory經過方法registerSingleton(..)和registerBeanDefinition(..)來實現註冊.可是,通常的應用運行中用到的bean定義是經過元數據Bean定義來實現的
備註: bean的元數據和手動單例越早註冊越好,以便於容器更好推測他們來進行組裝或其餘思考步驟;當你重寫已存在的元數據或已存在的單例,ioc只在必定程度上支持; 註冊新的bean在運行(併發實時訪問bean工廠)時不會得到正式的支持,並在容器中會致使併發訪問異常和/或狀態不一致;
每個bean都有一個或多個標誌,這些標誌在管理它們的容器中必須是惟一的.一個bean通常只有一個標誌,但若是它須要不止一個,那個多出來的被認爲是別名.
一個基於xml的配置元數據,你可用使用id 和/或 name屬性來指定bean的標識符.id屬性容許你指定一個具體的bean的Id.這些名字通常都是字母(如'myBean','fooService'等),但可能包含特殊字符.若是你要引入bean的其餘別名,你能夠在name屬性來指明他們,用逗號(,),分號(;),或者空格來隔開.在歷史記錄中,spring3.1裏的id屬性並命名爲xsd:ID類型,這會限制字符.在3.1裏,它被定義爲xsd:String類型.記住bean的id是獨一無二的,這是有容器強制決定的,而不是由XML轉化器.
你不須要爲每一個bean提供一個name或id.若是id或name沒有指定,容器就會爲bean生成一個惟一的名字.可是,若是你想經過名字來引用bean,如經過使用ref標籤或者服務定位風格來查找,那麼你必須提供一個名字.沒有提供名字,通常都與內部bean或者自動注機制入相關.
這個規範指在給bean命名時要使用標準的java實例字段規範.它以下:bean的名字從一個小寫字母開始,使用駝峯命名法.這些名字的例子以下:"accountManager","accountService","userDao","loginController"等等. 給bean命名通常會讓你的配置更容易讀取和理解.若是要使用spring AOP調用advice到一串bean裏時,能夠經過名字查找這些bean.這也頗有幫助.
備註:當在classpath中掃描組件時,spring會自動爲沒有命名的bean命名,按如下規則:通常,採用其簡易類名,並將第一個字母小寫.然若是第一和第二個字母都大寫,那麼類名將保留.這個是由java.beans.Introspector.decapitalize 來定義的,固然spring也在使用
在bean定義裏,你能夠提供多個名字,經過使用id來提供 一個命名,而後在那麼屬性裏有多個命名.這些名字等同於bean的別名,對於一些狀況頗有用,例如容許應用中的每一個組件來經過一個特定於本組件的bean的名稱來引用一個公共的依賴.
然而,別名和bean的實際命名是不同的.有時候會從給在其餘地方定義的bean引入一個別名.這種狀況通常爲:在大系統中,會被分割爲幾個的子系統,每一個子系統都有本身的對象定義集.在基於xml配置元數據中,你可使用<alias/>元素來完成.
<alias name="fromName" alias="toName"/>
在此狀況下,在同一容器中一個bean被命名爲fromName,但在使用別名以後,也能夠用toName來指向它.
例如,子系統A中配置元數據可能指向一個名爲subsystemA-dataSource的數據源.而配置在子系統B中的經過subsystemB-dataSource來指向一個數據源.當你編譯主系統時,主系統能夠經過myApp-dataSource來指向這個數據源.這樣,就有三個指向相同對象的名字被你添加到了MyApp配置元數據裏了.其別名定義以下:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/> <alias name="subsystemA-dataSource" alias="myApp-dataSource" />
如今每一個組件和主應用均可以經過惟一的名字來引用一個相同的dataSource,並保證不與其餘定義衝突(有效的建立命名空間).
bean定義根本上是爲了創造一個或多個對象的清單.當一個命名bean被請求時,容器就會查找該清單,使用被封裝的bean定義的配置元數據來建立(獲取)一個實際的對象.
若是你使用基於xml的配置元數據,你須要,在bean元素裏class的屬性裏指定你要實例化對象的類型.這個class屬性,本質上是BeanDefinition的class屬性,通常是強制要求的.(固然有例外:用工程實例方法來實例化或7.7 bean定義繼承).你這樣使用class特徵:
通常而言,指定bean的class是爲了在構造時容器能經過反射調用它的構造器來直接新建bean,必定程度上如同java代碼使用new 操縱符.
調用實際上包含某種靜態工廠方法能夠建立對象的類,一些狀況下容器能夠調用靜態工廠方法來建立bean.靜態工廠方法調用的返回對象類型能夠與class類型相同或不一樣的類
內部類的命名
加入你要配置一個靜態內部類的bean定義,你必須使用內部類的二進制名稱. 例如,若是你有一個類叫Foo在com.example包裏,並且Foo有一個靜態內部類叫Bar,那麼Bar的bean定義中的class屬性應該這樣: com.example.Foo$Bar 記住$符號的使用,它能夠從外部類的名字中分割內部類的名字;
當你使用構造器方法來建立bean時,全部正常的類均可以被spring使用和兼容.這樣的話,這些類就不須要實現特定的接口或者被編碼到特定的快照中.簡單指定class的類型就足夠了.可是,取決於那種你具體使用的ioc容器的類型,你可能須要一個默認構造器.
Spring Ioc 容器能夠管理任何你須要管理的類;它不止能管理javaBeans.大多數spring用戶傾向於選擇javaBean,它的定義以下:一個默認構造器(非參),其屬性有合適的set和get方法模塊的容器. 你也能夠在你的容器裏有其餘非bean風格的類.例如,若是你使用一個邏輯鏈接池,它徹底不符合javaBean的定義,spring依然能管理它. 在基於xml的配置元數據中,你能夠如此管理你的bean:
<bean id="exampleBean" class="examples.ExampleBean" /> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
關於如何將參數提供到構造器的機制和對象構造以後如何設置實例屬性,參見 Injecting Dependencies ;
當你要用靜態方法工廠來建立的定義bean時,你須要在class屬性裏指定一個包含了靜態工廠方法的類,在factory-method方法裏指定該工廠方法.你可用調用該方法並返回一個實例,隨後你能夠像一個構造器造出的bean同樣來使用它了.一我的使用這種bean的定義方式,主要是想在邏輯代碼裏調用靜態工廠.
接下來的bean定義說明,bean會被經過調用工廠方法來建立.這個定義不會指定返回對象的類型,只有哪一個類包含有工廠方法.在本例中,這個 createInstance()方法必須是靜態方法.
<bean id="clientService" class="examples.ClientService" factory-method="createInstance" /> public class ClientService { private static ClientService clientService = new ClientService(); private ClientService() {} public static ClientService createInstance() { return clientService; } }
細節參照 Dependencied and configuration in detail
與靜態工廠方法類似,一個實例工廠方法的實例化會調用一個在容器中已存在的bean的非靜態方法,並用來建立一個新的bean.以下使用:不填class屬性,並在factory-bean屬性裏指定有實例工廠方法的並確實要調用類的bean的標識符.在factory-method方法裏添上實例工廠方法.
<!-- 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"/> public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private DefaultServiceLocator() {} 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"/> public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); private DefaultServiceLocator() {} public ClientService createClientServiceInstance() { return clientService; } public AccountService createAccountServiceInstance() { return accountService; } }
改方法顯示工廠Bean自己也能夠經過依賴注入被管理和配置.具體查看 [dependencies and configuration in detail].(http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-factory-properties-detailed)
一個典型的企業應用不是由單個對象組成的.即便是最簡單的應用也有好幾個對象協同工做,並展示給終端用戶一個條例分明的應用.這章將講述如何定義不少獨立的bean,並使他們在一個良好運行的應用裏相互協做並實現目標的.
依賴注入(DI)是一個過程,即對象定義他們的依賴關係,也就是說與它協做的對象,只有經過構造器參數,工廠方法參數,或者當構造器或工廠方法生成實例返回以後才能設置其屬性.當容器建立這個bean時,它會注入這些依賴.這個過程是反轉(控制反轉)的基礎,經過直接調用類構造器或者Service Locator pattern反轉的bean本身控制自身的實例化或其依賴的定位
在DI原則下,代碼會更加簡潔,且由於對象同它們的依賴一塊兒提供,解耦會更好實現.對象沒必要查找他們的依賴,也沒必要知道依賴的位置和他們的class.所以,你的class會更容易測試,特別是當這些依賴是接口或抽象類時會容許單元測試進行存根或實現模擬; DI存在兩個變種:基於構造器的依賴注入和基於Setter的依賴注入
基於構造器的注入依賴是經過容器來調用有不少參數的構造器來完成,每一個構造器表明一種依賴.調用一個帶有具體參數的靜態工廠方法來構造bean是同樣的,這個討論對於構造器和靜態工廠方法的參數也是相同的.下面的例子介紹了一種只能基於構造器的的依賴注入.注意,這種類沒有其餘特別的東西,它只是一個POJO類,沒有以及容器實現的接口,基礎類,或者註解 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... }
構造器參數解析要匹配注入時的參數類型.若是在一個bean定義中沒有潛在的歧義,那麼在Bean被實例化時,構造器會按照bean定義當中提供的構造器參數順序來加載使用.以下所示: package x.y;
public class Foo { public Foo(Bar bar, Baz baz) { // ... } }
無潛在歧義存在,通常認爲Bar和Brz類之間無繼承關係.所以在剩下的配置工做中,你不須要在<constructor-arg/>標籤中指定構造器參數的加載順序或者類型說明. <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>
使用index來指明構造器參數明確的加載順序,如:
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>
另外,消除簡單類型值的歧義,若是一個構造器有兩個相同類型的參數,能夠指定其下標index來消除歧義.記住下標從0開始. 你可用使用構造器參數名來消除歧義.
<bean id="exampleBean" class="examples.ExampleBean"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </bean>
請記住,爲了可以開箱即用,你的代碼編譯時必須打開調試模式,因此spring能夠從構造器裏查找參數名字.若是你不想讓你的代碼同調試標誌一塊兒編譯,你可用使用@ConstructorPropertiesJDK註解,來指定你的構造器參數的名稱.例子以下:
package examples; public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"}) public ExampleBean(int years, String ultimateAnswer) { this.years = years; this.ultimateAnswer = ultimateAnswer; } }
Setter-based DI是在容器調用無參構造器或者無參靜態工廠方法實例化你的bean以後,在調用你bean裏的setter方法來實現的. 下面的例子展現了一個只使用setter注入來完成依賴注入的class.這個是一個傳統的java類.它是一個簡單的java對象,不依賴於容器的特定的接口,基本類或者註解.
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方法的DI.它還支持當你使用構造器方法注入一些依賴以後,還可使用基於Setter的依賴注入.你能夠以BeanDefinition的形式來定義這些依賴,這樣可使用PropertyEditor實例來將其屬性從一種形式轉化爲另外一種.然而,spring用戶並不直接與這些類打交道,然是經過XML bean定義,註解組件(用@Component,@Controller標記的類),或者基於Java的@Configuration類裏的@Bean方法來使用它們.這些資源將被相互轉化爲BeanDefinition的實例,並能夠用來加載一個完整的Spring ioc的實例.
既然你能夠同時使用基於構造器和基於setter的依賴注入,那麼最好的作法就是使用構造器來加載強制依賴,用setter方法或配置方法來加載可選依賴.記住在setter方法上加上@Required註解,就可使一個屬性變爲強制依賴.
spring團隊原則上主張構造器注入,它能夠保證一個應用組件實現做爲一個不可變對象並保證全部的強制依賴不爲空.另外,構造器注入組件老是返回客戶端(調用)代碼在一個徹底初始化的狀態.另外一方面也說明,大量的構造其參數是一個糟糕的氣味,暗示這個類有太多的職責,應該分離一些其關注點並改造其爲更好的專一.
Setter注入通常只用於可選依賴,這樣它能在類裏分配到合理的默認值.另外,在使用這些依賴的任何地方都必需要非空檢驗.好處之一,能夠對該類的對象進行重構或從新注入.經過JMX MBean來管理是Setter注入一個很是好的實踐.
使用UI的方式要具體分析.有時,咱們要使用沒有資源的第三方的類,對你來講選擇以肯定.例如,若是一個第三方的類沒有暴露set方法,那麼構造器注入就是依賴注入惟一的形式.
容器對bean依賴的解決步驟以下:
ApplicationContext讀取描述因此beans的配置元數據進行新建和實例化.配置元數據能夠經過xml,java代碼,註解來定義
對於每一個bean,若是你不用正常的構造器時,它的依賴能夠以屬性,構造器參數,或者靜態工廠方法參數的形式表現.當bean被正式建立時,這些依賴會被提供給bean.
每一個屬性或構造器參數能夠定義注入的具體值,也能夠定義爲容器中的一個引用.
每一個屬性或者構造器參數的值都是經過特定形式來轉化爲參數或構造器的實際類型.默認的spring會把string類型定義的值轉化爲各類內置類型,如int,long,String,boolean等.
在spring容器建立時spring就會驗證各個bean的配置.單例或預安裝bean會隨容器一塊兒建立.做用域查看7.5"bean Scope".不然,bean只有當被請求時纔會建立.一個bean的建立潛在的會引發一組bean的建立,由於在此時bean的依賴和bean依賴的依賴都會建立.第一次建立bean時,這些依賴的解決會顯稍後顯示.
若是你主要使用了構造器依賴,這將會產生沒法解決的循環依賴問題. 例如,類A須要類B的一個實例且類B須要類A的實例,且類B須要類A的實例.若是你配置類A和類B相互注入,那麼spring ioc容器就會判斷其在運行時爲循環依賴.並會拋出一個BeanCurrentlyInCreationException.
一個解決方案是將一些類的資源代碼配置爲Setter注入而不是構造器注入.另外,避免只是用構造器依賴和只是用setter依賴.換而言之,儘管沒有被明確指出,但你仍是能夠把循環依賴配置爲setter依賴.
不一樣於其餘典型狀況(沒有循環依賴),一個beanA和Bean B間的循環依賴會強制其中一個bean在注入到其餘Bean以前先完成自身的實例化(一個典型的雞和蛋的問題)
你能夠認爲spring所作的都是正確的.它能夠在容器加載時檢測配置問題,如未存在的bean的引用,以及循環依賴.當bean實際上已經建立了,那麼spring會盡可能晚的去設置屬性和解決依賴.當你請求一個對象在建立對象或其中一個依賴時出現問題,已正確加載的spring容器稍後會產生一個異常.例如,若是一個屬性缺失或無效,這個bean就會拋出異常.由於ApplicationContext是由前置實例化單例化beans實現的,因爲配置問題可能會潛在的下降可見性.有建立這些前置單例Bean會花費一些時間和內存,因此當ApplicationContext建立時你會發現配置問題,而不是以後.你也能夠重寫這個默認的行爲,這樣單例Bean就會懶加載,而不是前置實例化.
若是沒循環依賴存在,當有一個或多個bean被注入到一個依賴的bean中,每一個協同bean都會配置注入到依賴bean的優先級.這意味着若是bean A有一個關於Bean B的依賴,那麼Spring ioc容器總會配置Bean B比調用Bean A的setter方法優先.換句話說,bean會先實例化(若是不是一個前置單例模式),依賴接着被設置,然後與生命週期相關的方法(例如 configured init method ,InitializingBean callback method)會被調用.
下面的例子是基於Xml的用於setter方法注入依賴的配置元數據.一小段spring XML配置文件指明瞭一些bean的定義.
<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; } } ```
在上面的例子中,setter被宣佈來匹配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; } }
bean定義裏的構造器參數能夠用於ExampleBean的構造器的參數. 如今能夠認爲是上面例子的變種,替換使用構造器,spring介紹了一種靜態工廠方法來返回對象的實例
<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"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
靜態工廠方法類:
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; } }
靜態工廠方法的參數經過<constructor-arg>標籤來提供,和構造器實際使用的是同樣的.靜態工廠方法返回的類型沒有必要和擁有靜態工廠方法的類的類型一致,雖然上個例子是一致的.實例工廠方法使用了一致的方法(除了使用factory-bean屬性來替代class屬性),因此細節不在這討論了.
如上節所示,你能夠將bean的屬性和構造器參數定義爲其餘被管理bean的引用,或者是內聯的bean.spring 基於xml的配置元數據支持使用<property/>和<constructor-arg/>來實現目標.
經過<property/>標籤中的value元素來指定屬性或構造器參數爲一個可讀的string形式.spring的conversion service用來轉換這些值從string類型到屬性或參數的實際值.
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="masterkaoli"/> </bean>
下面是用p-namespace來實現更加簡潔的xml配置
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="masterkaoli"/> </beans>
上面的例子更加的簡潔.可是編碼的錯誤只有在運行時纔會發現而不是在編譯時,除非你有好的IDE工具例如IntelliJ IDEA或者Spring Tool Suite ,當你建立bean定義時,它們支持自動屬性補全.這些IDE工具的幫助是很是重要的.
固然你能夠這樣定義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容器經過JavaBeans的PropertyEditor機制將<value/>標籤裏的文本內容轉化爲java.util.Properties的實例.這是一個漂亮的簡化,在不少地方spring團隊用<value/>標籤來代替value屬性.
idref元素用於將容器中其餘bean的id(值是字符串,而不是引用)傳給一個<constructor-arg/>或<property>標籤.
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean" /> </property> </bean>
上面的bean定義片斷很是等同於下面的片斷;
<bean id="theTargetBean" class="..." /> <bean id="client" class="..."> <property name="targetName" value="theTargetBean" /> </bean>
第一種形式優於第二種,由於使用idref標籤容許容器在部署期間驗證其引用的bean是否存在.第二種形式,不會對client Bean的targetName屬性的值作驗證.錯誤的代碼只有在Client Bean被徹底初始化是才被發現(伴隨很是糟糕的結果).若是client bean是原型bean的話,那麼這個錯誤和異常可能只有再容器部署很長一段時間後纔會被發現.
ref元素上的local屬性在4.0 Bean xsd以後再也不支持,由於它不能爲一個普通的bean提供值.若是你要升級到4.0,那麼你就將你的idref local引用修改完idref bean屬性.
一個共同的地方(至少早於spring2.0):<idref/>元素返回值是由ProxyFactoryBean的bean定義裏的AOP interceptors配置的.當你使用<idref/>元素時,只要你指定攔截器的名字,即便拼錯攔截器的id也沒有問題.
ref元素是<constructor-arg/>或<property/>的最後一個元素.這裏米能夠設置一個bean裏的特定元素,使其指向其餘被管理的bean.引用bean是那個屬性要設置的bean的依賴.在其屬性設置以前它將初始化.若是該依賴是單例Bean,那麼它在容器建立時就會初始化.全部的引用都指向其餘的對象.做用域和驗證依賴於你經過bean,local,或者parent指定的其餘對象的id/name.
通常經過bean屬性裏的<ref/>標籤來指定目標bean;能夠指定在同一容器或父容器裏的bean對象,而無論它是否在同一xml文件裏.bean屬性的值能夠和目標bean的id元素同樣,或者是目標bean裏name屬性的值之一.
<ref bean="someBean"/>
指定目標bean能夠經過parent屬性來建立一個關於在父容器或當前容器bean的引用.當你要使用父容器裏的bean,且本身要定義的bean的id和父容器的bean相同時,你可使用parent標籤.
這是父容器裏的東西
<!-- in the parent context --> <bean id="accountService" class="com.foo.SimpleAccountService"> <!-- insert dependencies as required as here --> </bean>
這是子容器裏的東西
<!-- in the child (descendant) context --> <bean id="accountService" <!-- bean name is the same as the parent bean --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- notice how we refer to the parent bean --> </property> <!-- insert other configuration and dependencies as required here --> </bean>
在<property/>或<constructor-arg/>裏的元素<bean/>定義被稱爲內部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>
一個內部bean不須要id或name屬性,即便設置了,容器也會忽略.容器也會忽略其scope標誌,內部bean通常是匿名的,它們和外部的bean一塊兒建立.你不可能將內部bean注入到非外部bean以外的bean,你也不能單獨訪問內部bean.
做爲特例,內部bean也可能從自定義做用域裏收到銷燬回調.例如,一個請求做用域的內部bean被一個單例bean持有,內部bean的建立會管理其持有bean,但銷燬回調容許他參與到請求做用域的生命週期中.這只是個特例.通常內部bean的做用域和其持有bean相同.
在<list/>,<set/>,<map/>,<props/>元素中,你可用直接設置屬性和構造器參數中的集合類型,如List,Set,Map以及Properties.
<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource" /> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key ="a ref" value-ref="myDataSource"/> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource" /> </set> </property> </bean>
map的key或value,set的value值,能夠是如下元素:
bean,ref,idref,list,set,map,props,value,null
spring 容器支持集合合併.應用開發者能夠定義一個父風格的<list/>,<set/>,<map/>,<props>元素,能夠有子風格的<list/>,<map/>,<set/>,<props/>元素繼承並重寫付集合的值.子集合的值是合併父集合和子集合值的結果,子集合的元素之能夠重寫指定的父集合的值. 這部分討論關於父子bean的機制.
<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans>
能夠看到子Bean的<props/>標籤裏添加了"merger=true"屬性.當子Bean被容器釋放和實例化,其結果實例中的adminEmails屬性將包含子Bean中adminEmails集合和父Bean中adminEmails集合.
administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk
子Bean中的Properties集合即支持從父Bean的<props/>繼承屬性,也支持子Bean重寫父Bean的集合元素.
這個合併行爲一樣適用於<list/>,<map/>,<set/>等集合類型.可是有list的語法的做用,List裏的父Bean的元素的順序優先於子Bean的順序.其餘類型如Map,Set,Properties,由於沒有排序的存在,因此也不會對Map,Set,Properties的實現類的排序形成影響.
1.5的泛型以後,咱們可使用強類型的集合.你能夠申明一個只有String類型元素的集合.若是你要使用強類型的依賴注入的集合到一個bean裏,你能夠利用spring 的類型轉化,在注入到集合以前將其轉化合適的類型.
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } }
xml配置:
<beans> <bean id="foo" class="x.y.Foo"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans>
當foo Bean裏的account屬性準備注入時,其屬性的類型Map<String,Float>中的元素的泛型信息會經過反射得到.這樣spring的類型轉化會了解到其各類值元素都是Float類型,並把String類型的值9.99等轉化爲Float類型.
spring將空的屬性參數視爲空字符串.下面的xml配置元數據片斷,將Email屬性的值設爲空的字符串.
<bean class="ExampleBean"> <property name="email" value=""/> </bean>
等同於如下java代碼:
exampleBean.setEmail("")
<null/>標籤將會認爲是null值.以下:
<bean class="ExampleBean"> <property name="email"> <null/> </property> </bean>
上面的配置等價於下面的代碼:
exampleBean.setEmail(null)
p-namespace能夠替代<property/>標籤,來描述某個屬性值或協助bean. spring支持對namespacce的擴展配置格式,只要基於XML Schema定義.beans的配置格式會有專章介紹.可是p-namespace不存在與XSD文件裏,只存在於spring 核心中.你須要設置P標籤的引用(xmlns:p="http://www.springframework.org/schema/p") 下面的兩個xml片斷會有相同的結果:第一個使用XML格式,第二個用的是p-namespace.
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value="foo@bar.com"/> </bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email="foo@bar.com"/> </beans>
這個例子說的是在bean定義中一個屬性叫p-namespace.由於p-namespace沒有一個schema定義,因此你能夠把屬性的名字設置其上. 下面的例子是多個bean且有其餘bean的引用
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person"> <property name="name" value="Jane Doe"/> </bean> </beans>
如你所見,p-namespace不只能夠設置一個屬性值,也能夠設置bean引用(用p:name-ref屬性).
同上一節相同,但本節是3.1以後引入的.運行用相同的方式,能夠替代constructor-arg元素.你須要設置c標籤的引用(xmlns:c="http://www.springframework.org/schema/c")
一個是用c:namespace的例子:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/> <!-- traditional declaration --> <bean id="foo" class="x.y.Foo"> <constructor-arg ref="bar"/> <constructor-arg ref="baz"/> <constructor-arg value="foo@bar.com"/> </bean> <!-- c-namespace declaration --> <bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> </beans>
從上例中能夠看出,使用c:name來設置屬性值,使用c:name-ref來設置依賴. 有時構造器參數沒法獲取(一般是子節碼沒有調試信息的編譯),你可使用參數順序.
<!-- c-namespace index declaration --> <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
你可使用複合或關聯屬性名稱來設置bean的屬性,只要左後全部須要的路徑組件屬性不爲空便可.以下定義:
<bean id="foo" class="foo.Bar"> <property name="fred.bob.sammy" value="123" /> </bean>
解釋:在foo.Bar類下,有一個名爲fred的屬性,fred裏有個bob的屬性,bob裏有一個sammy的屬性,要給sammy設置123的值.注意,實例化foo Bean以後,fred屬性,和bob屬性必須不爲空才行.
若是一個bean被某個bean做爲依賴,那麼表示該Bean是某個Bean的屬性.通常你可用使用<ref/>標籤來完成它.可是,有時候依賴間沒有直接聯繫,例如,一個靜態的初始器須要被觸發,好比數據庫引擎的註冊.使用depends-on可用強迫一個或多個bean先於該Bean被初始化.下面的例子是用depends-on屬性來變形對一個單例Bean的依賴:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" />
使用beanOne以前會強制加載manager Bean.若是依賴是多個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" />
這樣就強制依賴了兩個bean.
depends-on屬性還能夠指定相應銷燬時的依賴.該依賴只針對於singleton bean這樣的depends-on,它們的依賴先於singleton bean銷燬.
通常全部的單例bean會在ApplicationContext實現容器初始化時加載.基本上,這種前置實例化是好的,由於在配置或周邊環境裏的錯誤能夠當即發現,而不是幾個小時甚至幾天之後.當這個行爲不理想時,咱們能夠將該Bean定義爲懶記載(lazy-initialized).懶加載的bean能夠告訴容器在第一次被請求時建立該bean的實例,而不是啓動時.
在xml裏,如此在<bean/>裏配置lazy-init屬性:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/>
當該配置被ApplicationContext讀取時,命名爲lazy的bean不會急切的在容器加載時提早初始化. 可是當它被其餘bean依賴時,該配置無效,由於容器須要初始化它來建立其餘的bean. 咱們可使用default-lazy-init屬性來控制整個容器的初始化;例如:
<beans default-lazy-init="true"> <!-- no beans will be pre-instantiated... --> </beans>
spring容器能夠自動裝配協助beans之間的關係.你能夠容許spring經過檢測ApplicationContext的內容來爲你的bean自動釋放合做者.
自動裝配有如下好處:
1.明顯減小對指定屬性和構造器參數的須要.
2.當你對象改變時,autowiring能夠自動更新設置.在開發時,它會很是有用,當代碼穩定時你可改成顯示配置.
當使用基於xml的配置元數據時,你能夠在<bean/>裏的autowire來指定bean的自動裝配的模式.自動裝配有四種模式,簡介以下;
no 不自動裝配 byName 經過bean的name屬性來自動裝配, byType 經過bean的Type來裝配,若是有多個bean,會拋異常;沒找到則不設置. constructor 與byType類似,只適用於構造器參數.若是沒準確的值,就會拋出錯誤.
在byType和constructor模式下,你能夠裝配數組或者強類型集合.這種狀況下,容器裏全部的匹配類型都會來適配這個依賴.若是你的期待類型是string,你也能夠選擇強類型Map.一個自動裝配的Map的值能夠由全部符合指望類型的實例組成,那麼Maps的key值會包含相應的bean的名字. 你能夠在自動裝配完成後進行驗證.
自動裝配最好在你的項目裏所有使用.若是自動裝配基本不使用或只使用一兩個bean定義,它會困擾開發者. 缺點及侷限以下:
1.屬性和構造參數明確的依賴會重寫自動裝配.另外,你不能自動裝配簡單屬性,例如原生的,String,或者class(或者簡單類型的數組).這個是設計侷限
2.自動裝配沒有顯示裝配精確.spring已經很當心的避免有歧義帶來的猜想,由於這會帶來難以預料的後果.
3.文檔生成工具沒法從spring容器中獲取裝配信息.
4.容器中的混合bean定義可能要匹配構造參數或setter方法來注入.對於數組,集合,Maps來講,這不是一個問題.但對於單個值的依賴來講,這種歧義卻沒法消除.若是沒有明確的bean定義,異常會拋出.
你有如下選擇:
在提早實例化bean的基礎上,能夠將bean排除自動裝配.spring xml格式中,能夠將autowire-candidate屬性設置爲false,容器會使特定的bean不能夠用於自動裝配(包括註解配置好比@Autowired)
你也能夠對bean的名字進行匹配,在頂層<beans/> 元素中,使用default-autowire-candidates屬性,接受一個或多個模式. 這個方式是在自動裝配獲取名單中排除該bean.該bean自己仍是能夠用自動裝配進行初始化的
大多數狀況,spring的bean都是單例bean.當一個單例bean同其餘單例bean協做,一個非單例bean同非單例bean協做,你通常會把該依賴設置爲bean的某個屬性.可是當bean的生命週期不一樣就會有問題,一個protype的bean不可能每次都給一個singleton的bean一個new 實例.
用如下方式解決
lookup的原理是經過cglib動態代理產生子類,來覆蓋父類的方法
建立一個抽象類,將工廠方法設爲抽象
package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
xml文件配置
<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="command" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --> </bean> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="command"/> </bean>
commandManager bean裏使用lookup-method標籤,name是要查找的方法,bean是返回的實例的依賴Bean.本例中每次調用createCommand方法都會返回一個新的command的bean.若是你把其做用域不設爲prototype,每次都會返回新的command實例.
原理爲spring ioc會自動編譯,若是方法是抽象的,就會實現該方法;若是方法不抽象,就重寫該方法.
這個方法可能比lookup方法注入要稍微差點,但仍能夠替換任意方法.用戶能夠忽略全部不須要的方法,只使用實際須要的功能.
基於xml的配置元數據,對於一個已部署的bean,你可使用replaced-method元素去替換一個已存在的方法實現.在下面的class中,一個方法computeValue,這個咱們想要重寫的:
public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... }
該接口的實現類以下:
/** * meant to be used to override the existing computeValue(String) * implementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer { public Object reimplement(Object o, Method m, Object[] args) throws Throwable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return ...; } }
該Bean 定義原生class和指定方法重寫以下:
<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> <!-- arbitrary method replacement --> <replaced-method name="computeValue" replacer="replacementComputeValue"> <arg-type>String</arg-type> </replaced-method> </bean> <bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>
你能夠用<arg-type/>元素來定義方法簽名以指定哪一個方法你須要重寫.當類裏有方法重載且有變種時,參數簽名會很是重要.爲了方便,參數類型的字符串能夠是其全路徑名的一部分.例如,一下都適配java.lang.String: java.lang.String String Str 由於參數的數目一般足夠用來區分每一個可能的選擇,這個簡化能夠節省不少打字,你能夠經過打更少的字來匹配參數類型.