javascript
【Spring学习笔记】之【3.3 DI的深入 二】
3.3.4 ?依賴檢查
? ? ? ?上一節介紹的自動裝配,很可能發生沒有匹配的Bean進行自動裝配,如果此種情況發生,只有在程序運行過程中發生了空指針異常才能發現錯誤,如果能提前發現該多好啊,這就是依賴檢查的作用。
依賴檢查:用于檢查Bean定義的屬性都注入數據了,不管是自動裝配的還是配置方式注入的都能檢查,如果沒有注入數據將報錯,從而提前發現注入錯誤,只檢查具有setter方法的屬性。
Spring3+也不推薦配置方式依賴檢查了,建議采用Java5+ @Required注解方式,測試時請將XML schema降低為2.5版本的,和自動裝配中“autodetect”配置方式的xsd一樣。
<?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-2.5.xsd
</beans> ?
依賴檢查有none、simple、object、all四種方式,接下來讓我們詳細介紹一下:
一、none:默認方式,表示不檢查;
二、objects:檢查除基本類型外的依賴對象,配置方式為:dependency-check="objects",此處我們為HelloApiDecorator添加一個String類型屬性“message”,來測試如果有簡單數據類型的屬性為null,也不報錯;
<bean id="helloApi"class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> ?
<!-- 注意我們沒有注入helloApi,所以測試時會報錯 --> ?
<bean id="bean"
class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
? ? dependency-check="objects"> ?
<property name="message" value="Haha"/> ?
</bean> ?
? ? ? 注意由于我們沒有注入bean需要的依賴“helloApi”,所以應該拋出異常UnsatisfiedDependencyException,表示沒有發現滿足的依賴:
package cn.javass.spring.chapter3; ?
import java.io.IOException; ?
import org.junit.Test; ?
import org.springframework.beans.factory.UnsatisfiedDependencyException; ?
import org.springframework.context.support.ClassPathXmlApplicationContext; ?
publicclass DependencyCheckTest { ?
@Test(expected = UnsatisfiedDependencyException.class) ?
publicvoid testDependencyCheckByObject() throws IOException { ?
//將拋出異常
new ClassPathXmlApplicationContext("chapter3/dependency-check-object.xml"); ?
? ?} ?
} ?
三、simple:對基本類型進行依賴檢查,包括數組類型,其他依賴不報錯;配置方式為:dependency-check="simple",以下配置中沒有注入message屬性,所以會拋出異常:
<bean id="helloApi"class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> ?
<!-- 注意我們沒有注入message屬性,所以測試時會報錯 --> ?
<bean id="bean"
class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
? ? dependency-check="simple"> ?
? <property name="helloApi" ref="helloApi"/> ?
</bean> ?
四、all:對所以類型進行依賴檢查,配置方式為:dependency-check="all",如下配置方式中如果兩個屬性其中一個沒配置將報錯。
<bean id="helloApi"class="cn.javass.spring.chapter2.helloworld.HelloImpl"/> ?
<bean id="bean"
class="cn.javass.spring.chapter3.bean.HelloApiDecorator"
? ? dependency-check="all"> ?
?<property name="helloApi" ref="helloApi"/> ?
<property name="message" value="Haha"/> ?
</bean> ?
依賴檢查也可以通過“<beans>”標簽中default-dependency-check屬性來指定全局依賴檢查配置。
3.3.5 方法注入
所謂方法注入其實就是通過配置方式覆蓋或攔截指定的方法,通常通過代理模式實現。Spring提供兩種方法注入:查找方法注入和方法替換注入。
因為Spring是通過CGLIB動態代理方式實現方法注入,也就是通過動態修改類的字節碼來實現的,本質就是生成需方法注入的類的子類方式實現。
在進行測試之前,我們需要確保將“com.springsource.cn.sf.cglib-2.2.0.jar”放到lib里并添加到“Java Build Path”中的Libararies中。否則報錯,異常中包含“nested exception is java.lang.NoClassDefFoundError: cn/sf/cglib/proxy/CallbackFilter”。
? ? ? 傳統方式和Spring容器管理方式唯一不同的是不需要我們手動生成子類,而是通過配置方式來實現;其中如果要替換createPrinter()方法的返回值就使用查找方法注入;如果想完全替換sayHello()方法體就使用方法替換注入。 ? ? ? 接下來讓我們看看具體實現吧。
一、查找方法注入:又稱為Lookup方法注入,用于注入方法返回結果,也就是說能通過配置方式替換方法返回結果。使用<lookup-method name="方法名" bean="bean名字"/>配置;其中name屬性指定方法名,bean屬性指定方法需返回的Bean。
方法定義格式:訪問級別必須是public或protected,保證能被子類重載,可以是抽象方法,必須有返回值,必須是無參數方法,查找方法的類和被重載的方法必須為非final:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
因為“singleton”Bean在容器中只有一個實例,而“prototype”Bean是每次獲取容器都返回一個全新的實例,所以如果“singleton”Bean在使用“prototype” Bean情況時,那么“prototype”Bean由于是“singleton”Bean的一個字段屬性,所以獲取的這個“prototype”Bean就和它所在的“singleton”Bean具有同樣的生命周期,所以不是我們所期待的結果。因此查找方法注入就是用于解決這個問題。
1) ?首先定義我們需要的類,Printer類是一個有狀態的類,counter字段記錄訪問次數:
package cn.javass.spring.chapter3.bean; ?
publicclass Printer { ?
privateint counter = 0; ?
publicvoid print(String type) { ?
? ? ? ?System.out.println(type + " printer: " + counter++); ?
? ?} ?
} ?
? ? ? HelloImpl5類用于打印歡迎信息,其中包括setter注入和方法注入,此處特別需要注意的是該類是抽象的,充分說明了需要容器對其進行子類化處理,還定義了一個抽象方法createPrototypePrinter用于創建“prototype”Bean,createSingletonPrinter方法用于創建“singleton”Bean,此處注意方法會被Spring攔截,不會執行方法體代碼:
package cn.javass.spring.chapter3; ?
import cn.javass.spring.chapter2.helloworld.HelloApi; ?
import cn.javass.spring.chapter3.bean.Printer; ?
publicabstractclass HelloImpl5 implements HelloApi { ?
private Printer printer; ?
publicvoid sayHello() { ?
? ? ? ?printer.print("setter"); ?
? ? ? ?createPrototypePrinter().print("prototype"); ?
? ? ? ?createSingletonPrinter().print("singleton");
? ?} ?
publicabstract Printer createPrototypePrinter(); ?
public Printer createSingletonPrinter() { ?
? ? ? ?System.out.println("該方法不會被執行,如果輸出就錯了"); ?
returnnew Printer(); ?
? ?} ?
publicvoid setPrinter(Printer printer) { ?
this.printer = printer; ?
? ?} ?
} ?
2) ?開始配置了,配置文件在(resources/chapter3/lookupMethodInject.xml),其中“prototypePrinter”是“prototype”Printer,“singletonPrinter”是“singleton”Printer,“helloApi1”是“singleton”Bean,而“helloApi2”注入了“prototype”Bean:
<bean id="prototypePrinter"
class="cn.javass.spring.chapter3.bean.Printer" scope="prototype"/> ?
<bean id="singletonPrinter"
class="cn.javass.spring.chapter3.bean.Printer" scope="singleton"/> ?
<bean id="helloApi1"class="cn.javass.spring.chapter3.HelloImpl5" scope="singleton"> ?
<property name="printer" ref="prototypePrinter"/> ?
<lookup-method name="createPrototypePrinter" bean="prototypePrinter"/> ?
<lookup-method name="createSingletonPrinter" bean="singletonPrinter"/> ?
</bean> ? ? ? ? ? ?
<bean id="helloApi2"class="cn.javass.spring.chapter3.HelloImpl5" scope="prototype"> ?
<property name="printer" ref="prototypePrinter"/> ?
<lookup-method name="createPrototypePrinter" bean="prototypePrinter"/> ?
<lookup-method name="createSingletonPrinter" bean="singletonPrinter"/> ?
</bean> ? ? ? ? ? ?
? ? ? 3)測試代碼如下:
package cn.javass.spring.chapter3; ?
import org.junit.Test; ?
import org.springframework.context.support.ClassPathXmlApplicationContext; ?
import cn.javass.spring.chapter2.helloworld.HelloApi; ?
publicclass MethodInjectTest { ?
@Test
publicvoid testLookup() { ?
ClassPathXmlApplicationContext context = ?
new ClassPathXmlApplicationContext("chapter3/lookupMethodInject.xml"); ?
? ? ? ?System.out.println("=======singleton sayHello======"); ?
? ? ? ?HelloApi helloApi1 = context.getBean("helloApi1", HelloApi.class); ?
? ? ? ?helloApi1.sayHello(); ?
? ? ? ?helloApi1 = context.getBean("helloApi1", HelloApi.class); ?
? ? ? ?helloApi1.sayHello(); ?
? ? ? ?System.out.println("=======prototype sayHello======"); ?
? ? ? ?HelloApi helloApi2 = context.getBean("helloApi2", HelloApi.class); ?
? ? ? ?helloApi2.sayHello(); ?
? ? ? ?helloApi2 = context.getBean("helloApi2", HelloApi.class); ?
? ? ? ?helloApi2.sayHello(); ?
}} ?
? ? ? 其中“helloApi1”測試中,其輸出結果如下:
=======singleton sayHello====== ?
setter printer: 0
prototype printer: 0
singleton printer: 0
setter printer: 1
prototype printer: 0
singleton printer: 1
? ? ? 首先“helloApi1”是“singleton”,通過setter注入的“printer”是“prototypePrinter”,所以它應該輸出“setter printer:0”和“setter printer:1”;而“createPrototypePrinter”方法注入了“prototypePrinter”,所以應該輸出兩次“prototype printer:0”;而“createSingletonPrinter”注入了“singletonPrinter”,所以應該輸出“singleton printer:0”和“singleton printer:1”。
? ? ? 而“helloApi2”測試中,其輸出結果如下:
=======prototype sayHello====== ?
setter printer: 0
prototype printer: 0
singleton printer: 2
setter printer: 0
prototype printer: 0
singleton printer: 3
? ? ? 首先“helloApi2”是“prototype”,通過setter注入的“printer”是“prototypePrinter”,所以它應該輸出兩次“setter printer:0”;而“createPrototypePrinter”方法注入了“prototypePrinter”,所以應該輸出兩次“prototype printer:0”;而“createSingletonPrinter”注入了“singletonPrinter”,所以應該輸出“singleton printer:2”和“singleton printer:3”。
? ? ? 大家是否注意到“createSingletonPrinter”方法應該輸出“該方法不會被執行,如果輸出就錯了”,而實際是沒輸出的,這說明Spring攔截了該方法并使用注入的Bean替換了返回結果。
方法注入主要用于處理“singleton”作用域的Bean需要其他作用域的Bean時,采用Spring查找方法注入方式無需修改任何代碼即能獲取需要的其他作用域的Bean。
二、替換方法注入:也叫“MethodReplacer”注入,和查找注入方法不一樣的是,他主要用來替換方法體。通過首先定義一個MethodReplacer接口實現,然后如下配置來實現:
<replaced-method name="方法名" replacer="MethodReplacer實現"> ?
<arg-type>參數類型</arg-type> ?
</replaced-method>” ?
? ? ? 1)首先定義MethodReplacer實現,完全替換掉被替換方法的方法體及返回值,其中reimplement方法重定義方法 功能,參數obj為被替換方法的對象,method為被替換方法,args為方法參數;最需要注意的是不能再 通過“method.invoke(obj, new String[]{"hehe"});” 反射形式再去調用原來方法,這樣會產生循環調用;如果返回值類型為Void,請在實現中返回null:
package cn.javass.spring.chapter3.bean; ?
import java.lang.reflect.Method; ?
import org.springframework.beans.factory.support.MethodReplacer; ?
publicclass PrinterReplacer implements MethodReplacer { ?
@Override
public Object reimplement(Object obj, Method method, Object[] args) ? throws Throwable { ?
? ? ? ?System.out.println("Print Replacer"); ?
//注意此處不能再通過反射調用了,否則會產生循環調用,知道內存溢出
//method.invoke(obj, new String[]{"hehe"});
returnnull; ?
? ?} ?
} ?
? ? ? 2)配置如下,首先定義MethodReplacer實現,使用< replaced-method >標簽來指定要進行替換方法,屬性name指定替換的方法名字,replacer指定該方法的重新實現者,子標簽< arg-type >用來指定原來方法參數的類型,必須指定否則找不到原方法:
<bean id="replacer"class="cn.javass.spring.chapter3.bean.PrinterReplacer"/> ?
<bean id="printer"class="cn.javass.spring.chapter3.bean.Printer"> ?
<replaced-method name="print" replacer="replacer"> ?
? ? ? ?<arg-type>java.lang.String</arg-type> ?
? ?</replaced-method> ?
</bean> ?
? ? ? 3)測試代碼將輸出“Print Replacer ”,說明方法體確實被替換了:
@Test
publicvoid testMethodReplacer() { ?
? ?ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("chapter3/methodReplacerInject.xml"); ?
? ?Printer printer = context.getBean("printer", Printer.class); ?
? ?printer.print("我將被替換"); ?
} ?
轉載于:https://blog.51cto.com/zhaohaibo/1284602
總結
以上是生活随笔為你收集整理的【Spring学习笔记】之【3.3 DI的深入 二】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Exchange2010之资源邮箱
- 下一篇: Server2003PDC迁移到Serv