上篇文章咱們學習了官網中的1.2,1.3兩小節,主要是涉及了容器,以及Spring實例化對象的一些知識。這篇文章咱們繼續學習Spring官網,主要是針對1.4小節,主要涉及到Spring的依賴注入。雖然只有一節,可是涉及的東西確很多。話很少說,開始正文。java
[TOC]spring
根據官網介紹,依賴注入主要分爲兩種方式app
咱們分別對以上兩種方式進行測試,官網上用的是XML的方式,我這邊就採用註解的方式了:ide
測試代碼以下,咱們經過在Service中注入LuBanService這個過程來函數
public class Main02 { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new // config類主要完成對類的掃描 AnnotationConfigApplicationContext(Config.class); Service service = (Service) ac.getBean("service"); service.test(); }}@Componentpublic class LuBanService { LuBanService(){ System.out.println("luBan create "); }}
@Componentpublic class Service { private LuBanService luBanService; public Service() { System.out.println("service create"); } public void test(){ System.out.println(luBanService); } // 經過autowired指定使用set方法完成注入 @Autowired public void setLuBanService(LuBanService luBanService) { System.out.println("注入luBanService by setter"); this.luBanService = luBanService; }}
輸出以下:學習
luBan create service create注入luBanService by setter // 驗證了確實是經過setter注入的com.dmz.official.service.LuBanService@5a01ccaa
@Componentpublic class Service { private LuBanService luBanService; public Service() { System.out.println("service create by no args constructor"); } // 經過Autowired指定使用這個構造函數,不然默認會使用無參 @Autowired public Service(LuBanService luBanService) { System.out.println("注入luBanService by constructor with arg"); this.luBanService = luBanService; System.out.println("service create by constructor with arg"); } public void test(){ System.out.println(luBanService); }}
輸出以下:測試
luBan create 注入luBanService by constructor // 驗證了確實是經過constructor注入的service create by constructorcom.dmz.official.service.LuBanService@1b40d5f0
在上面的驗證中,你們可能會有如下幾個疑問:ui
根據上圖中官網所說,咱們能夠得出以下結論:this
咱們不徹底按照官網順序進行學習,先看這一小節,對應官網上的位置以下圖:spa
首先咱們思考一個問題,在有了依賴注入的狀況下,爲何還須要方法注入這種方式呢?換而言之,方法注入解決了什麼問題?
咱們來看下面這種場景:
@Componentpublic class MyService { @Autowired private LuBanService luBanService; public void test(int a){ luBanService.addAndPrint(a); }}@Component// 原型對象@Scope("prototype")public class LuBanService { int i; LuBanService() { System.out.println("luBan create "); } // 每次將當前對象的屬性i+a而後打印 public void addAndPrint(int a) { i+=a; System.out.println(i); }}public class Main02 { public static void main(String[] args) { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); MyService service = (MyService) ac.getBean("myService"); service.test(1); service.test(2); service.test(3); }}
在上面的代碼中,咱們有兩個Bean,MyService爲單例的Bean,LuBanService爲原型的Bean。咱們的本意多是但願每次都能獲取到不一樣的LuBanService,預期的結果應該打印出:
1,2,3
實際輸出:
1 3 6
這個結果說明咱們每次調用到的LuBanService是同一個對象。固然,這也很好理解,由於在依賴注入階段咱們就完成了LuBanService的注入,以後咱們在調用測試方法時,不會再去進行注入,因此咱們一直使用的是同一個對象。
咱們能夠這麼說,原型對象在這種狀況下,失去了原型的意義,由於每次都使用的是同一個對象。那麼如何解決這個問題呢?只要我每次在使用這個Bean的時候都去從新獲取就能夠了,那麼這個時候咱們能夠經過方法注入來解決。
又分爲如下兩種方式:
@Componentpublic class MyService implements ApplicationContextAware { private ApplicationContext applicationContext; public void test(int a) { LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService")); luBanService.addAndPrint(a); } @Override public void setApplicationContext(@Nullable ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}
@Componentpublic class MyService{ @Autowired private ApplicationContext applicationContext; public void test(int a) { LuBanService luBanService = ((LuBanService) applicationContext.getBean("luBanService")); luBanService.addAndPrint(a); }}
@Componentpublic class MyService{ public void test(int a) { LuBanService luBanService = lookUp(); luBanService.addAndPrint(a); } // @Lookup public LuBanService lookUp(){ return null; }}
方法注入還有一種方式,即經過replace-method這種形式,沒有找到對應的註解,因此這裏咱們也就用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="myService" class="com.dmz.official.service.MyService"> <replaced-method replacer="replacer" name="test"/> </bean> <bean id="replacer" class="com.dmz.official.service.MyReplacer"/></beans>
public class MyReplacer implements MethodReplacer { @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable { System.out.println("替代"+obj+"中的方法,方法名稱:"+method.getName()); System.out.println("執行新方法中的邏輯"); return null; }}public class MyService{ public void test(int a) { System.out.println(a); }}public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext cc = new ClassPathXmlApplicationContext("application.xml"); MyService myService = ((MyService) cc.getBean("myService")); myService.test(1); }}
執行結果:
替代com.dmz.official.service.MyService$$EnhancerBySpringCGLIB$$61c14242@63e31ee中的方法,方法名稱:test執行新方法中的邏輯
這裏須要注意一點:
我在測試replace-method這種方法注入的方式時,受動態代理的影響,一直想將執行咱們被替代的方法。用代碼體現以下:
public class MyReplacer implements MethodReplacer { @Override public Object reimplement(Object obj, Method method, Object[] args) throws Throwable {// System.out.println("替代"+obj+"中的方法,方法名稱:"+method.getName());// System.out.println("執行新方法中的邏輯"); method.invoke(obj,args); return null; }}
可是,這段代碼是沒法執行的,會報棧內存溢出。由於obj是咱們的代理對象,method.invoke(obj,args)執行時會進入方法調用的死循環。最終我也沒有找到一種合適的方式來執行被替代的方法。目前看來這可能也是Spring的設計,因此咱們使用replace-method的場景應該是想徹底替代某種方法的執行邏輯,而不是像AOP那樣更多的用於在方法的執行先後等時機完成某些邏輯。
能夠說,一個對象的依賴就是它自身的屬性,Spring中的依賴注入就是屬性注入。
畫圖以下:
這篇文章到這裏就結束了,看完記得點個關注+分享,咱們下篇文章再見!