javascript
Spring IOC之依赖
一個標準的企業級應用不只有一個對象組成。即使是最簡單的引用也會有個相互作用的對象以使最終呈現
在用戶面前的是個連貫一致的引用。
1依賴注入
依賴注入(DI)是一個對象定義他們依賴的過程,也就是說他們一起處理的對象,只是通過構造器參數、
工廠方法參數或者是通過工廠飯飯返回或者是通過構造的設置在實例上的屬性。當容器創建這些bean的
時候會注入他們的依賴關系。這個過程實現了飯莊,因此被有了控制反轉的名字(IOC),也就是通過構造器的器或者是
Service Locator模式,bean自己可以控制實例化和依賴的位置。
遵循DI原則的代碼更加整潔,當對象提供了他們的依賴的時候會更加的有效。這些對象不會尋找他們的依賴
而且也不知道依賴類的位置。正式這樣,你的類更加容易去測試,特別是當依賴是接口或者是抽象類的時候,
它將允許在在單元測試中使用。
DI主要有兩個形式,基于構造函數的依賴注入和基于setter方法的依賴注入。
1.1基于構造函數的依賴注入
基于構造函數的DI的完成時通過容器調用含有參數的的構造函數。調用一個有明確參數static工廠方法去構造bean是完全一樣的,這個方式處理
傳遞給構造器和靜態工廠方法是相似的。下面的例子將會展示一個類智能通過構造器注入。注意這個類沒有其他特別地方,
這是一個POJO,它沒有依賴容器規范接口,基礎類或者是注解。
1.1.1構造器參數解決方案
構造器參數解決方案通過匹配參數來使用的。在一個Bean被實例化的時候,如果在一個Bean定義的構造器參數中沒有潛在的模糊存在的話,那么在Bean定義的構造器參數的順序就是就就是會被應用到合適的構造器參數的順序。看下下面的類
package x.y; public class Foo {public Foo(Bar bar, Baz baz) {// ...} }沒有潛在的模糊存在,加入Bar 和Barz類沒有繼承關系。所以下面的配置文件時沒有問題的,而且你不需要去通過原來去指明構造器參數。
<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>當其他的bean被引用的時候,類型是知道的,那么就會匹配的。當一個簡單類型被使用的時候,比如 true,Spring不能定義值的類型,而且沒有其他條件的話也是不能通過類型類匹配的。以下面的類為例:
package examples; public class ExampleBean {// Number of years to calculate the Ultimate Answerprivate int years;// The Answer to Life, the Universe, and Everythingprivate 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>除了解決了多個簡單值的模糊問題,指明索引值也可以解決構造器含有兩個相同參數類型的模糊性問題。需要注意的是索引值是從0開始的。
你可以使用構造器參數名來消除值的模糊性問題:
<bean id="exampleBean" class="examples.ExampleBean"><constructor-arg name="years" value="7500000"/><constructor-arg name="ultimateAnswer" value="42"/> </bean>需要注意的是讓這個功能起作用的話你需要在debug模式開啟下編譯你的代碼以便Spring可以從構造器中尋找參數。如果你想使用debug來編譯你的嗲嗎,你可以使用JDK的注解 @ConstructorProperties來顯示的命名你的構造器參數。下面有一個樣例:
package examples; public class ExampleBean {// Fields omitted@ConstructorProperties({"years", "ultimateAnswer"})public ExampleBean(int years, String ultimateAnswer) {this.years = years;this.ultimateAnswer = ultimateAnswer;} }1.2基于Setter方法的依賴注入
基于Setter方法的依賴注入是通過容器調用一個無參的構造函數或者無參的靜態工廠方法去實例化你的bean之后調用在你的bean中setter方法來實現的:
下面的例子展示了只是通過單純的setter方法注入來實現DI的。這個類是常規的類。它是一個沒有依賴容器具體接口基礎類或者注解的POJO。
public class SimpleMovieLister {// the SimpleMovieLister has a dependency on the MovieFinderprivate MovieFinder movieFinder;// a setter method so that the Spring container can inject a MovieFinderpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// business logic that actually uses the injected MovieFinder is omitted... }ApplicationContext支持基于構造函數和基于setter方法的DI。它也支持在一些依賴已經通過構造器途徑來注入后的setter方法注入。你注冊這些依賴以BeanDefinition的形式,你會使用它來和PropertyEditor來關聯起來將屬性從一個格式轉為另外一個格式。但是,大部分的是Spring用戶不直接和這些類打交道,而是通過XML格式的bean定義、注解組件(@Component、@Controller)或者在基于Java的@Configuration類中的@Bean方法。這些最后在內部會轉為為BeanDefinition實例,用于去加載一個整的Spring IOC容器實例。
1.3依賴方案過程
容器處理bean依賴通過下面途徑:
- ApplicationContext創建后然后使用描述所有bean的配置數據來進行初始化。配置數據是通過XML、java代碼或者注解的方式的指明的。
- 對于每一個bean,如果你使用IOC容器來實例化而不是通過普通的構造函數,它的依賴是通過屬性、構造器參數、靜態工廠方法的參數的形式來表現的。當bean被創建的時候,這些依賴就會提供給bean。
- 在容器中,每一個屬性或者構造器參數就是要賦值給實際的定義或者其他bean的引用。
- 作為值的每一個屬性或者構造器參數從顯示的格式轉換成實際的屬性或者構造器參數類型。默認情況下,Spring能夠將一個String格式的值轉成所有的內置類型,比如int.long.String.boolean等等。
Spring容器在每個bean被創建的是時候會校驗他們的配置。但是,只有當bean被創建的時候bean自己的屬性才會被設置。在容器創建的時候,單例的bean會被提前實例化創建。否則的話,只有在被請求的時候才會被創建。一個bean的創建會潛在的引起多個bean的創建,因為bean的依賴和他們依賴的依賴會被創建和分配。
1.3.1循環依賴
如果你主要使用構造器注入,這樣可能產生一個無法解決的循環依賴情況。
例如:A類需要通過構造器注入得到類B的實例,B類需要通過構造器注入得到類A的實例。如果你配置類A和類B注入給彼此,Spring 的IOC 容器在運行時會發現這個循環引用,就會拋出BeanCurrentlyInCreationException異常。
一個可能的解決方法就是編輯類的源碼使用setter方法注入而不是通過構造器。兩者選其一,只用setter注入而不是使用構造器注入。換句話說,金庫這不是被建議,但是你應該使用setter方法配置循環依賴。
不像一個標準的例子,在Bean A 和Bean B之間的一個循環依賴強迫其中一個bean在自己卑微完全實例化之前被注入。
你會想回Spring在做正確的事情。它發現配置問題,例如在容器加載的時候引用不存在的bean和循環嵌套。當bean實際被創建的時候,Spring設置屬性和解析依賴會盡可能的晚。這意味著如果創建的對象或者它的依賴有問題時,一個已經正確加載的Spring容器在你請求一個對象的時候會產生異常。例如,bean因為缺失或者不合法的屬性時會拋出異常。這種潛在的延遲發現
一些配置問題是為什么ApplicationContext的實現在缺省的情況下回提前實例化單例bean的原因。在他們真正需要之前或消耗寫前置事假內核內存作為代價,你可以發現一些配置問題在ApplicationContext的時候而不是后來。你仍然可以重寫這個缺省的行為一百年單實例bean可是懶加載而不是提前實例化。
如果沒有循環依賴存在的話,當一個或者多個協作的bean被注入到一個依賴的bean的時候,每一個協作的bean會被配置在注入到依賴的bean之前。這意味著如果bean A 有一個依賴bean,Spring Ioc容器在調用bean A的setter方法之前就會完成B的配置。換句話說,Bean被實例化,它的依賴被設置,而且相關的方法(如何一個配置初始化方法或者是懶加載bean的回調方法)被調用。
1.3.2依賴注入的例子
下面的例子使用基于XML配置是使用基于setter方法的DI。作為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"/> 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文件中配置的屬性。下面的例子使用基于構造器的DI;
<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"/> 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 constructorprivate 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;} }傳遞給靜態工廠方法的參數是通過元素來提供的,就好像一個構造器已經被使用過一樣。通過工廠方法返回的類類型不用和包含靜態工廠方法的類類型一樣,盡管在這個例子中是這樣的。
2.詳細的依賴和配置
正如上面部分提到的,你可以定義bean屬性和構造器參數引用給其他管理的bean,或者在內部被定義的值。SPring的基于XML配置的元數據支持子元素類型在 和內部。
2.1直接值(原始類型、字符串等等)
元素 的值屬性指明了元素或者構造器參數可讀的字符串表達式。S平日功能的 轉化服務就是用于將這些值從一個字符串轉為為元素或者參數的具體類型
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroymethod="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/beanshttp://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>上面的XML更加簡明了;但是類型是在運行時發現的而不是在設計的時候,出發你室友像Intellij IDEA或者是 SpringSource Tool Suit 這樣在你創建bean定義的時候支持屬性補全功能的IDE。這樣的IDE幫助是很提倡的。
你可以配置一個java.util.Properities實例像下面:
<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.Driverjdbc.url=jdbc:mysql://localhost:3306/mydb</value></property> </bean>Spring容器通過使用JavaBeanPropertyEditor機制將元素中間的值轉成java.util.Properties實例。這是一個很棒的簡化,而且是少有的一個地方Spring團隊使用了嵌套的元素。
2.2 idref元素
idref元素是一個簡單誤差檢驗校驗方式將在容器中的其他bean的id傳給 或者元素。
<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,這種形式和結果異常可能會在容器部署后才會發現。
注意:在idref中的 local 屬性不在4.0的beans xsd中支持了因為它不在提供bean引用的值了。當升級到4.0 schema的時候 修改 idref local 到idref bean.
在元素帶有值的時候一個共同的地方就是在ProxyFactoryBean bean定義中的AOP 攔截器的配置。當你指明攔截器名字的時候防止你忘記拼寫攔截器的idke可以使用元素。
2.3其他bean的引用
ref元素是bean定義元素內部的最終元素,也就說不存在子元素。你將指定元素的值為容器管理的bean的引用。被引用的bean是一個s屬性將會被設置的bean的依賴,在屬性設置之前它在被請求的時候才會被處理話(如果是單例的話,它可能已經被容器初始化了)。所有的引用 都不會限制作為其他對象的引用。作用域和校驗取決于你通過bean、local、parent屬性來指明其他對象的id/name。
通過標簽的bean屬性指明目標bean是最通用的做法,而且這樣允許在相同的容器或者父容器中創建任何bean的引用,而不管是否在相同的XML文件中。bean屬性的值可以說和目標bean的id屬性或者目標bean的name屬性的其中一個值是一樣的。
<ref bean="someBean"/>通過parent屬性指明目標bean創建了一個在當前容器的父容器的bean的引用。parent屬性的值可以說既可以目標bean的id屬性,目標bean的name屬性的一個值,而且目標bean肯定是在當前容器的父容器中。當你有一個繼承的容器而且你想使用一個和父bean名字相同的代理封裝一個一個在父容器中存在的bean。
<!-- 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>2.4內部bean
在或者 內部的元素可以定義個所謂的內部bean(inner 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中。
2.5.1集合
在, , ,和 元素中,你設置java集合類型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的值可以分配下面元素的類型:
bean | ref | idref | list | set | map | props | value | null
2.5.2集合的合并
Spring容器也支持集合的合并。一個應用的開發中能夠定義一個父風格的, , 或者元素么可以有子風格的, , 或者 ,并且覆蓋父集合的值。也就是說,
子集合的值就是父集合和自己和合并后的元素的結果,可以用子集合的元素覆蓋父集合指明的值。
下面的例子演示了集合合并:
<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>注意在child bean定義中的adminEmails屬性的元素中使用merge=true屬性。當child bean被容器解析和實例化的時候,實例化的結果就會有一個包含子adminEmails和父adminEmails 合并結果的adminEmails Properties集合。
administrator=administrator@example.com sales=sales@example.com support=support@example.co.uk子Properties集合值的繼承了所有的屬性元素從父,而且子的值support覆蓋了父集合的值。
這個合并也適用于, , 和 集合。在這個例子中,語義是和List集合有關的,也就是說,ordered的集合類型的概念是可保持的;父的值在所有子list值的前面。在Map、Set和Properties集合類型中,沒有順序存在。因此沒有排序的集合類型的語義實際上構成相關的Map,Set,和Properties實現類型容器內部使用。
2.5.3集合合并的局限性
你不能合并不同類型的集合,而且如果你這樣做的話就會拋出異常。merge屬性必須要在child定義中指明;聲明在父集合定義的merge屬性是多余的也不會得到想要的合并結果。
2.5.4強類型的集合
在Java5中的泛型類型介紹中,你可以使用強類型的集合,也即是說,可以去聲明集合類型比如只包含String類型的元素。如果使用Spring 去DI一個強類型的集合到一個bean中,你可以李世勇Spring的類型轉換支持,你強類型的集合實例能夠轉換成合適的類型在被加入到集合之前。
public classFoo { privateMap<String, Float> accounts; public voidsetAccounts(Map<String, Float> accounts) { this.accounts = accounts; } } <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的accounts屬性準備好注入的時候,關于強類型 Map<String, Float>的元素的泛型信息時能夠通過反射訪問的。因為Spring的類型轉換機制識別出了不同的值作為Float類型,而且String的值9.99, 2.75, 和 3.99被轉換成實際的Float類型。
2.5.5NULL 和 empty的字符串的值
Spring對待屬性參數的empty值,就像對象空的字符串一樣。下面基于XML的配置信息片段設置Email的值是空的字符串"".
<bean class="ExampleBean"> <property name="email" value=""/> </bean>前面的例子和下面的Java代碼是等價的:
exampleBean.setEmail("")元素用來處理null值。例如:
<bean class="ExampleBean"> <property name="email"> <null/> </property> </bean>上面的代碼和下面的Java代碼等價:
exampleBean.setEmail(null)2.6 p-namespace的XML簡寫
p-namespace可以讓你使用bean元素的屬性,而不是嵌套的去描述你的屬性值或者協作的bean。
Spring支持使用基于XML模式定義的命名空間的擴展的配置。在這章節里討論的bean配置格式都是定義在XML模式文檔中的。但是 p-namespace并不是以一個XSD文件定義的,而且只是存在核心的Spring中。
下面的例子顯示了兩個解解決相同結果的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/beanshttp://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中定義的叫做Email的p-namespace的屬性。這告訴Spring包含一個屬性的生命。正如前面提到的,p-namespace沒有一個模式定義,所以你可以設置一個屬性的明道去給屬性的名字。
下面的例子包括兩個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/beanshttp://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使用 去創建一個引用從bean john到bean jane,第二個bean定義使用 p:spouse-ref="jane" 作為屬性做了幾乎相同的事情。在這個例子中,spouse是屬性名字,盡管-ref部分暗示著不是一個直接的值而是一個對其他bean的引用。
2.7 c-namespace的XML簡寫
和稱為"p-namespace的XML簡寫"部分類似, c-namespace在Spring3.1中被新引進來的,它允許使用內置的屬性在配置構造器參數的時候而不是使用嵌套的屬性。
讓我們看個基于構造器注入的例子,還用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/beanshttp: //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: namespace和 p: one 一樣使用相同的慣例在通過他們的名字來設置構造器參數的是偶。但是盡管他需要在一個XSD模式中聲明但是還需要聲明的(它存在Spring核心的內部)。
對于極少的情況下構造器參數名字無法使用的時候(一般情況下是字節碼變異的時候沒有debug信息),我們可以使用參數索引。
<!-- c-namespace index declaration --> <bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>在實際中,構造器解決機制在真正需要的時候在參數匹配上能夠非常有效的,我們建議使用在你配偶之中使用名字的方式。
2.8 復合的屬性名
你可以使用復合或者嵌套的屬性名在你設置bean屬性的時候,只要素有的路徑的組件期望的最總屬性名字不是null的。看下面的bean定義。
<bean id="foo" class="foo.Bar"><property name="fred.bob.sammy" value="123" /> </bean>foo bean 有一個fred屬性,而fred有一個bob屬性,bob屬性有一個sammy屬性,而且最總的sammy屬性值被設置為123.為了讓這個起作用,fred的屬性bob,bob的屬性fred一定不能為null,在bean被構建以后,或者NullPointerException被拋出。
3.使用依賴
如果一個bean是其他bean的依賴,這一般意味著一個bean被設置為其他的屬性。習慣上講,你完成這個用在基于XML配置的元素。但是,有時候bean之間的依賴不是那么直接,例如,在一個類中的靜態初始化器需要被觸發,比如數據庫驅動注冊的時候。depends-on屬性能夠明確強迫一個多個多個bean在使用這個元素的bean被初始化的時候被初始化。下面的例子使用depends-on屬性去表示在單例中的依賴:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean" />為了表達在多個bean中的依賴,在 dependson屬性中使用用逗號空格冒號連接的列表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" />4.懶加載bean
在默認情況下,ApplicationContext的實現偏好于創建和配置所有的bean作為初始化過程的一部分。一般來講,這個提前實例化是需要的,因為在配置或者周遭環境中的錯誤能夠被立即發現,而不是幾小時或者幾天之后。當這個行為不被需要的時候,你可以通過是bean的定義是懶加載的的方式來阻止單例bean的提前實例化。一個懶加載的bean告訴Ioc容器在它第一次被需要的時候進行實例化而不是在啟動的時候。
在XML中,這種行為是通過元素中的lazy-init屬性來控制的,例如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.foo.AnotherBean"/>當上面的配置被ApplicationContext加載的時候,名為lazy 的bean 在ApplicationContext啟動的時候不會被實例化,而not.lazy的bean則會被提前實例化。
但是,當一個懶加載的bean 是一個不是懶加載的單例的依賴,那么ApplicationContext 也會在啟動的時候創建懶加載的bean,因為它必須滿足單例的依賴關系。懶加載被注入到一個單例bean中,不是懶加載的。
你可以使用在中 default-lazy-init屬性來容器級別來控制攔截在。例如:
<beans default-lazy-init="true"><!-- no beans will be pre-instantiated... --> </beans>5.自動注入協作者
Spring容器科室在協作的bean之間自動注入依賴關系。你可以允許Spring去自動的去解析其他bean通過通過檢查ApplicationContext的內容。自動注入有下面的優勢:
- 自動注入有效的減少了需要指明屬性或者構造器參數的需求。
- 自動注入能夠在你的bean改變的時候更新配置信息。例如,如果你需啊喲添加一個依賴給一個類,囊額依賴可以自動的滿足需求而你不需要去修改配置。因為自動注入在開發中是特別有用的,而不用在基礎代碼變得更加穩定的時候改下選擇區先明確的修改。
當使用基于XML的配置時,你指明可以在元素中的autowire屬性來指明autowire模式為一一個bean 定義。自動注入功能有五個模式,你明確自動注入每一個bean并且選擇哪一種去注入。
| 模式 | 解釋 |
| 沒有(空) | (缺省情況下)沒有自動注入,Bean的引用必須通過ref元素定義。改變缺省的設置在大型部署中是不建議的,因為顯示的指明協作類會保證更好的控制和透明。在一定的范圍程度上,它表示了一個系統的結構 |
| 通過名字 | 通過屬性名來自動注入。Spring尋找一個bean和需要自動注入的屬性名字相同的bean.例如,如果一個bean定義是通過名字自動注入,而且它包含以一個master屬性(也就是說他有一個setMaster()方法),Spring尋找一個定義為名字是master的bean,并且用它來作為屬性名字。 |
| 通過類型 | 如果只有一個屬性類型的bean存在容器中,允許屬性值自動注入。如果有多個的話,就會拋出致命異常,表示你可能不能夠通過byType類型來注入實現。如果沒有匹配的bean,什么事情也不會發生;屬性值不會被設置。 |
| 構造器 | 和通過類型方式類似,但是需要引用到構造器參數。如果在容器中沒有對應的構造器參數的bean,就會有指明的異常錯誤出現。 |
使用byType或者 constructor類型注入模式,你可以應用在數組和集合類型上。在這種情況下,所有在容器中的候選的bean只要匹配上期望的類型就會提供給相應的依賴。你可以注入強類型的Maps如果期望的key類型是String。一個自動注入的Maps的值由匹配所有期望類型的bean實例組成,Maps keys將包含相配的bean 名字。
你可以講自動類型注入和依賴檢查聯系在一起,這個將在注入完成后進行。
5.1自動注入的限制和不利處
當自動注入在整個項目中都被使用的時候,自動注入就會起很好地作用。如果自動注入沒有被使用總是,那么這個可能會使開發者疑惑去使用它去配置只是一個或是兩個bean。
來看看自動注入受限和不利處:
- 在property和constructor-arg 設置的顯示依賴可以覆蓋自動注入。你不可以自動注入所謂的簡單屬性想原生類型,字符串,類,(包括數組)。這種受限是故意這樣設計的。
- 自動注入沒有顯示的嵌套更加的明了。盡管這樣,正如上面表格中寫的那樣,Spring會小心避免那些模糊的事情,比如無法預料的結果,你的Spring管理的對象之間的關系沒有顯示的給出文檔等等。
- 封裝信息可能對于那些聰明給你一個Spring容器中生成文檔的工具是不可用的。
- 在容器里面多個Bean的定義通過setter方法構造器參數來匹配指明的類型來實現自動注入。對于數組、集合、或者maps,這些可能不會有什么問題。但是對于那些期望有一個值得依賴,這種模糊性無法得到直接的解析。如果沒有唯一的Bean定義,就會拋出異常。
對于后面的場景,你可以有這些選擇:
- 使用顯示的封裝而不是用自動注入。
- 通過設置屬性autowire-candidata 屬性為FALSE,而不適用自動注入對一個Bean的定義。
- 通過設置的primary屬性為TRUE來指定一個單獨的Bean定義作為primary candidate。
- 通過基于注解的配置的方式實現更細粒度的控制。
5.2將一個Bean排除在自動注入之外
在一個Bean的基礎之上,你可以將一個Bean排除在自動注入以外。在Spring的XML格式中,將的屬性autowire-candidate設置為FALSE;容器就會使用指明的Bean的定義無法使用自動注入(即使使用注解@autowired也是不行)
你可以限制自動注入的Bean通過模式匹配而不是Bean的名字。頂級元素通過它的default-autowire-candidates屬性接受一個或者多個模式匹配。例如,為限制所有以Repository結尾的自動注入的Bean的狀態,
只需要提供一個 *Repository的值就可以了。為了提供多一個模式,在一個逗號分隔的列表中指出來就可以了。對于一個Bean定義的utowire-candidate
屬性的明確的值TRUE和FALSE常常提前生效,對于這些Bean,模式匹配就不在起作用了。
這些技術在你永遠都想通過自動注入的方式來注入到其他的Bean的Bean是很用用處的。這個不意味著一個被排除的Bean自己不能通過自動注入的方式了當然了,Bean自己不再是自動注入其他Bean的候選者了。
6.方法注入
在絕大部分的引用場景中,在容器中的大部分的Bean是單例的。當一個單例的Bean需要和其他的單例的Bean產生寫作關系時,或者一個非單例的Bean需要和其他非單例Bean產生寫作關系時,你以通過定義把這個Bean定義為其他Bean的屬性的方式來定義依賴。
當Bean的生命周期不同的時候就會出現問題了。假設單例Bean A 需要去使用一個非單例Bean B,或許使用在A中的每一個方法調用。容器只會創建單例A 一次,而且只會有一次的機會去設置屬性。容器不會給Bean A在每一次需要的時候提供給一個新的實例Bean B.
一種解決方案就是放棄以前的一些控制反轉原則。你可以通過實現ApplicationContextAware的方式來讓Bean A來通過容器提醒,而且通過一個getBean("B")的方法告訴容器去請求Bean B的實例在A需要的時候。下面就是這種方式的一個例子:
前面的是不需要的,因為業務代碼對于Spring框架來說是已知的。方法注入,多少算得上SpringIOC容器的高級特性了,允許以一種簡潔的方式來處理了。
6.1查看方法注入
查看方法注入是容器重寫容器管理的Bean的方法的功能,它可以返回在容器中其他命名的Bean的擦看結果。這個查看一般會涉及到一個原型Bean,正如在前面的場景中描述到的一樣。
Spring框架通過使用CGLIGB庫的字節碼生成來動態的產生一個子類來重寫這個方法來實現方法注入的。
看下在前面的代碼中的CommandManager類,你可以看到Spring容器動態覆蓋了 createCommand()方法的實現。你的CommandManager
中不會有任何Spring的依賴,這個可以通過下面的例子看到:
客戶端中需啊喲含有注入的方法,這個需要被注入的方法需要像下面形式的簽名:
<public|protected>[abstract] <return-type>theMethodName(no-arguments);如果方法是抽象的,鼎泰的產生的子類需要實現那個方法。否則,動態產生的子類就會覆蓋在原始類中蒂尼的具體的方法。例如:
<!-- 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調用它自己的createCommand()方法,在任何時候它需要一個新的實例的command Bean。你必須小心去將
command bean作為一個原型。如果部署是單例的話,那么command bean 每次返回的就是同一個實例了。
6.2 武斷的方式替換
一個不太有用的方法注入形式比查看方法注入是替換在管理的bean中用其他方法實現的方法的能力。
通過基于XML配置的數據,對于一個已經部署的bean,你可以使用replaced-method元素去替換一個存在的方法用其他方法。
看下下面的有computeValue方法的類,我們準備用去復寫這種方法:
實現了 org.springframework.beans.factory.support.MethodReplacer接口的類提供給了一個新的方法定義。
/** * meant to be used to override the existing computeValue(String) * implementation in MyValueCalculator */ public classReplacementComputeValue implementsMethodReplacer { publicObject reimplement(Object o, Method m, Object[] args) throwsThrowable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return...; } }為了部署原始的類的bean定義,并且指明了覆蓋的方法:
<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"/>你可以在內部使用一個或者多個包含元素來表明需要被覆蓋的方法的簽名。
只有一個方法被重載或者在一個類里面有多個變體的時候方法的參數才是需要的。為了方便,String類型的參數可能是一個完整的指明
類型名稱的子串。例如,下面的都匹配java.lang.String:
因為通常來講參數的數量就足夠來區分不同的選擇,通過允許你去指出最短的字符串來匹配參數類型的這種簡寫可以節省很多的類型書寫。
轉載于:https://www.cnblogs.com/zhangminghui/p/4248079.html
總結
以上是生活随笔為你收集整理的Spring IOC之依赖的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu 改变workspace布局
- 下一篇: 一步一图一代码之排序二叉树