javascript
Spring核心——Bean的依赖注入
依賴注入
在設計模式與IoC這篇文章中,介紹了Spring基礎的三大支柱的兩項內容——IoC、Bean。本篇將繼續圍繞著Bean的創建時的注入方式來介紹Spring的核心思想與設計模式。
天底下所有面向對象的語言都不可能只用一個類來解決問題,即使是最簡單的應用程序都存在類與類之間的依存關系。如下面這個人人都理解的組合例子:
class Foo{
private Other other;
public Foo(){
other = new Other();
}
}
class Other{}
在設計模式上關于類的組合與繼承的適用性不屬于本篇的討論范圍,但是從Spring框架非侵入式的設計思路來看,組合才是使用Spring的正確姿勢。
官方將這種組合的關系叫做“依賴注入(DI——Dependency injection)”。從名字上來看這也是一種依托Ioc容器很自然的實現方式——所有的Bean都放置在容器中,然后通過一些配置來告訴容器bean與bean之間的依存關系。一個類除了在內部塊中通過new關鍵字實現一個組合關系,也可以通過構造方法傳參或接口方法設置。
由于IoC容器不可能去修改一個類內部的代碼,所以類與類的組合方式通過構造方法(Constructor)和set方法(Setter)來實現。此外,Ioc可以根據接口(interface)來注入對應的實現類(class extands interface),所以從設計模式的角度來說,依賴注入的方式很好的規避了標準組合模式中new關鍵字違反依賴倒置原則的問題。
構造方法注入
直接通過構造方法注入組合數據。
class:
package x.y;
public class A {
private B b;
private C c;
public Foo(B b, C c) {
this.b = b;
this.c = c;
}
}
public class B {}
public class C {}
xml:
<beans>
<bean id="a" class="x.y.A">
<constructor-arg ref="b"/>
<constructor-arg ref="c"/>
</bean>
<bean id="b" class="x.y.B"/>
<bean id="c" class="x.y.C"/>
</beans>
如果是源生類型的參數,可以通過指定類型來注入數據:
package x.y;
public class A {
private int b;
private String c;
public Foo(int b, String c) {
this.b = b;
this.c = c;
}
}
<bean id="a" class="x.y.A">
<constructor-arg type="int" value="1"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
也可以通過索引的方式:
<bean id="a" class="x.y.A">
<constructor-arg index="0" value="1"/>
<constructor-arg index="1" value="42"/>
</bean>
配合@ConstructorProperties注解,還可以直接使用名稱來注入:
package x.y;
public class A {
private int b;
private String c;
@ConstructorProperties({"b", "c"})
public Foo(int b, String c) {
this.b = b;
this.c = c;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="b" value="1"/>
<constructor-arg name="c" value="42"/>
</bean>
在Debug模式下不用這個注解也可以實現按名字注入,但是千萬別這樣做。
Set方法注入
package x.y;
public class A {
private B b;
private C c;
private String value;
public void setA(A a){this.a = a;}
public void setB(B b){this.b = b;}
public void setB(String value){this.value = value;}
}
public class B {}
public class C {}
<bean id="a" class="x.y.A">
<property name="b" ref="b"/>
<property name="c" ref="c"/>
<property name="value" value="1"/>
</bean>
<bean id="b" class="x.y.B"/>
<bean id="c" class="x.y.C"/>
使用?Constructor還是Setter?
2種注入方法在使用的過程中我們應該如何選取呢?Spring官方給出的答案是如果注入的數據或bean是一個“必要依賴”那么使用構造方法注入,如果屬于配置性的非必須數據,使用Set方法注入。但是在實際應用時,會發現絕大部分注入方式都是通過Setter實現的,包括一些很流行的開源工具,例如下面的druid:
<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/c3p0jdbctemplate"/>
<property name="user" value="admin"/>
<property name="password" value="123456"/>
</bean>
話說你不提供賬戶和密碼能鏈接到數據庫嗎?這算必要依賴還是配置性依賴?所以也不用死守這些規則。下面是一些關鍵性的建議:
數據配置類使用constructor注入的方法來實現,因為這樣可以將bean設置為一個不可變對象(immutable objects)。這樣結合單例模式能夠很好實現享元模式共享數據,結合原型模式可以創建“淺對比”對象(變更則替換)。
如果構造函數要傳入的參數太多,證明你的類要完成的責任太多,這個時候用Setter當然比較合理,但是建議回頭去看看類當中是不是有可以拆分的功能。給你們推薦一個學習java的平臺659270626,趁年輕,使勁拼,給未來的自己一個交代!
Setter注入主要用于可選的依賴關系,如果沒有設置值,類應該提供默認值。所以Setter方法應該檢查傳入值的有效性(not null、not blank等)。
如果出現了循環依賴,其實可以通過一個bean使用setter注入另外一個bean使用constructor注入來解決,不過最好檢查一下代碼為什么會循環,這是設計模式上的大忌。
最有一個建議最重要。如果用第三方類,別人給什么你只能用什么,沒得選。
注入參數
在XML配置中,用來設定注入方式和注入數據的XML標簽很多,詳細內容就不一一復述了,常規用法可以到官網?Dependencies and configuration in detail? 一節了解。這里僅僅說明一些要點:
父子Bean。Ioc容器提供Bean的父子關系配置。父子關系Bean可以進行數據合并,但是很少看見什么地方有實際應用。
<idref>標簽和<ref>標簽的差異:1)前者只能通過id引入,后者可以通過id或name引入;2)前者可以直接用value屬性替換,但是value屬性的效率會差很多;3)前者只能適用與當前配置文件或當前容器,后者可以引入任何位置的內容。
當需要設置一個null值時,用<null>標簽代替value=""。在執行代碼時直接傳入一個null。
自動裝配
這里所說的自動裝配是通過<bean>上的autowire屬性實現的功能,與@Autowired注解并不是一回事,但是他的一些參數會影像@Autowired注解的行為。
在有@Autowired注解的情況下,autowire屬性現在用得很少。基本上他實現的結果和@Autowired差不多,就是讓Ioc容器根據bean的類型或者bean名稱等自動將容器中其他能對應得上的bean注入到對于的構造方法或者set方法中。詳情了解?Autowiring collaborators。
方法注入
如果每一個Bean都是單例模式,那么我們通過常規的XML配置引用的手段就可以實現所有的依賴組合關系。但是每個bean都有不同的生命周期,常規配置方法很難實現某些應用不同生命周期bean的依賴關系。
第一種方式是通過繼承?ApplicationContextAware 類,繼承后可以直接使用?applicationContext 的 getBean 接口來獲取任何一個 bean。
package x.y;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class BeanManagerFoo implements ApplicationContextAware {
private ApplicationContext applicationContext;
public <T> T getBean(Class<T> type){
return applicationContext.getBean(type);
}
public Object getBean(String id){
return springContext.getBean(id);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
第二種方法是使用Lookup Method。
Lookup Method的實現思路是使用CGLIB生成了動態代理類并放置到Ioc中代替源生的類。看下面的例子。
首先實現我們的抽象類,抽象的要求至少有一個抽象方法:
package x.y;
public abstract class A {
public String getName() {
B b = this.createB();
return b.getName();
}
protected abstract B createB();
}
public class B {
private String name = "B class";
public String getName(http://www.my516.com){
return this.name;
}
}
然后通過<lookup-method>標簽來指定獲取bean的方式:
<bean id="b" class="x.y.B" scope="prototype" />
<bean id="a" class="x.y.A">
<lookup-method name="createB" bean="b"/>
</bean>
現在,在調用A.getName方法時都會創建一個新的B類實例。需要注意scope屬性,如果修改為singleton則每次都獲取同一個B實例。
使用動態代理由于是字節碼級別的變換,所有有很多限制需要注意:方法和類都不能用fina關鍵字;測試用例需要自己實現代理模式,否則抽象類沒有實現;
第三種方法是使用委派模式,即我們執行A.compute方法時,實際執行的是被委派的B.reimplement方法。
先定義2個基礎類——Origin、Replace:
package x.y
public class Origin {
public int compute(int in1, int in2) {
return in1+in2;
}
}
public class Replace {
public int reimplement(Object o, Method m, Object[] args) {
int in1 = (int)args[0];
int in2 = (int)args[1];
return in1+in2;
}
}
然后定義Spring配置:
<bean id="origin" class="x.y.Origin">
<replaced-method name="compute" replacer="replace">
<arg-type>int</arg-type>
<arg-type>int</arg-type>
</replaced-method>
</bean>
<bean id="replace" class="x.y.Replace"/>
這個時候,在任何時候執行“origin”這個bean的compute方法,實際上都是執行的Replace::reimplement方法。
上面<arg-type/>的參數用全稱或簡寫都可以,例如java.lang.String,使用String,Str都是指向這個類型。
使用委派模式的好處是限制少、靈活,并且不會用到CGLIB這種重量級工具。但是委派之后委派方法的真實參數和被委派方法的參數完全不一樣,開發時需要時時刻刻緊跟委派類的結構來修改代碼。一旦委派類發生任何修改而沒有相應的調整被委派類,可能會出現意想不到的問題。
---------------------
轉載于:https://www.cnblogs.com/ly570/p/11155033.html
總結
以上是生活随笔為你收集整理的Spring核心——Bean的依赖注入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Sublime Text 2
- 下一篇: WCF传递强类型DataSet【源码】