spring 裝配bean的三種方式
這段時間在學習Spring,依賴注入DI和麵向切面程式設計AOP是Spring框架最核心的部分 。這次主要是總結依賴注入的bean的裝配方式。
什麼是依賴注入呢?也可以稱為控制反轉,簡單的來說,一般完成稍微複雜的業務邏輯,可能需要多個類,會出現有些類要引用其他類的例項,也可以稱為依賴其他類。傳統的方法就是直接引用那個類物件作為自己的一個屬性,但如果我們每次建立這個類的物件時,都會建立依賴的類的物件,還有如果那個類將來可能不用了,還需要到這個類去刪除這個物件,那破壞了程式碼的複用性和導致高度耦合 !
依賴注入的出現可以很好地解決這個問題,依賴注入就是由系統負責協調類的依賴物件的建立,我們無需自己去顯示的建立依賴物件,而是由系統給我們注入這個物件,系統控制了這個物件的建立,也稱為控制反轉。
Spring給我們注入物件有三種方式:
- 隱式的bean掃描發現機制和自動裝配
- 在java中進行顯示配置
- 在XML中進行顯示配置
第一種:
spring從兩個角度實現自動化裝配:元件掃描和自動裝配。
當對一個類標註@Component註解時,表明該類會作為元件類,spring將為這個類建立bean。當在應用文中引用這個bean,spring會自動掃描事先指定的包查詢這個 bean。但spring預設是不啟用元件掃描的,可以在XML中配置加上<context:component-scan base-package="xx"/>。還有一種方法:在新建一個配置類,類中可以什麼不用寫,在配置類上加上@ComponentScan註解,spring會自動掃描改配置類所在的包,一般應該傾向xml配置。下面是一個bbs論壇系統使用者發帖的功能小例子:
1 package bbs.dao; 2 @Component 3 public interface Postdao { 4/* 5*使用者發帖 ,post表新增帖子資訊 6*/ 7public int addpost(@Param("title") String title,@Param("content") String content,@Param("userid") int userid); 8 } 9 10 package bbs.dao; 11 @Component 12 public interface Userdao { 13/* 14* 使用者發帖後,user表將使用者發帖數加一 15*/ 16public int addpost(int userid); 17 }
再在bbs.service包中建立一個postservice介面及其實現類,依賴Postdao和Userdao。
1 package bbs.service; 2 public interface PostService { 3/* 4使用者發帖後,先新增帖子資訊再更新使用者發帖數量 5*/ 6public void addpost(String title,String content,int userid); 7 } 8 9 package bbs.service; 10 @Component 11 public class PostserviceImpl implements PostService { 12 13private Postdao postdao; 14private Userdao userdao; 15 16 //@Autowired 17 //public void setPostdao(Postdao postdao) 18 //{ 19 //this.postdao=postdao; 20 //} 21 // 22 //@Autowired 23 //public void setUserdao(Userdao userdao) 24 //{ 25 //this.userdao=userdao; 26 //} 27 28@Autowired 29public PostserviceImpl(Postdao postdao,Userdao userdao) 30{ 31this.userdao=userdao; 32this.postdao=postdao; 33} 34 35public void addpost(String title, String content, int userid) { 36int i=postdao.addpost(title, content, userid); 37int j=userdao.addpost(userid); 38if(i==1&j==1) 39System.out.println("發帖成功"); 40else 41System.out.println("發帖失敗"); 42} 43 }
@Component在介面實現上註解就可以,但發現在userdao、postdao介面也加上了,其實可以去掉,因為我採用mybatis在xml中配置資料庫的操作,動態實現dao介面。等下會提到。上面程式碼出現的@Autowired註解實現bean自動裝配,會在spring應用上下文中的元件類尋找需求的bean。一般有兩種裝配方式:構造器和Setter方法(其他方法名也行,只要能夠使注入的bean成為這個類的屬性就行)
也可能出現spring沒有查詢到匹配的bean會丟擲異常,在@Autowired加上required=false,如果沒有匹配的bean時,spring會使這個bean處於未裝配的狀態,沒有裝配成功。還有可能會出現相同名字的bean有很多個,會產生歧義,一般在元件類上添加註解@Qualifier()括號寫這個bean的id,在注入時也加上@Qualifier(),寫上bean的id。像下面:
1 @Component 2 @Qualifier("postdao") 3 public interface Postdao{ 4 . . . . 5 } 6 7 @Component 8 @Qualifier("userdao") 9 public interface Userdao{ 10 . . . . 11 } 12 13 @Autowired 14 @Qualifier("usedao") 15 public void setUserdao(Userdao userdao) 16 {. . . 17 } 18 19 @Autowired 20 @Qualifier("postdao") 21 public void setUserdao(Postdao postdao) 22 {. . . 23 }
由於java不允許在同一個條目上重複出現相同型別的多個註解,所有注入採用set方式。但是其實可以建立自定義的限定符註解。這裡就不介紹啦。
第二種:
通過java程式碼裝配bean
一般通過元件掃描和自動裝配方式就比較方便了,但如果由於需求我們要使用第三方的庫的類,在這種情況沒有辦法到第三方庫中去給類加註解,就不能使用第一種方法了。這時得采用顯示裝配,可以採用java程式碼或xml顯示裝配bean。使用java程式碼,先新建一個配置類JavaConfig,裡面都是配置所需的bean,不應該有業務邏輯程式碼,所以單獨建一個類。
@Configuration @ContextConfiguration(locations = {"classpath:spring/spring-dao.xml","classpath:scan.xml"}) public class bbsConfig{ private Postdao postdao; private Userdao userdao; @Bean(name="postservice") public PostService getPost() { return new PostserviceImpl(postdao,userdao); }
在對PostService的bean注入時,同時又依賴了兩個bean,postdao和userdao。直接引用beanID就可以,spring會自動地從容器中獲取這些bean,只要他們的配置是正確的就行。這個例子中userdao、postdao是Mybatis配置自動掃描將dao介面生成代理注入到spring的 ,其實也算是xml裝配bean。可參考這篇文章,寫的挺清楚的。 https://bijian1013.iteye.com/blog/2318860
這裡如果再宣告一個bean,返回的仍是postserviceImpl物件,和之前的那個bean完全一樣,是同一個例項。一般spring@bean如果是同一個beanID,預設返回的是一個單例bean,注入的是同一個例項。如果修改其中一個會都改變的。
不過在這裡要注意進行測試時,由於spring的單元測試和springIoc容器是完全獨立的,postdao和userdao注入檢測時是使用locations載入xml檔案,而postservice使用classes載入config類的,但是兩個不能同時混用在@ContextConfiguration中。所以非要都測試的話,就分開測試吧。
第三種:
在XML中裝配bean
<?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 http://www.springframework.org/schema/context"> <import resource="spring/spring-dao.xml"/> <bean id="postservice" class="com.bbs.service.impl.PostserviceImpl"> <constructor-arg ref="postdao"/> <constructor-arg ref="userdao"/> </bean> </beans>
配置postservice的bean時需要引入兩個bean,postdao和userdao,放到constructor-arg的標籤中,ref指的是依賴的bean的ID。如果是在javaConfig中配置的,就寫@Bean的內容。如果是@Component就寫@Qualifier的內容。這裡是引入的是動態實現的dao介面的bean,是在spring-dao.xml中配置的,引入這個配置檔案就可以自動獲得beanID。
混合使用三種裝配:
1.在類上可以使用@ import(bbsConfig.class)組合其他java註解
2.在類上使用@ imortResource("classpath:spring-dao.xml")組合其他xml註解
3.在類上可以使用@ContenxtConfiguration包含class或者xml
4.在xml中可以用<import resource="spring-dao.xml">引入xml註解,也可以使用<bean class="com.bbs.dao.Userdao">引入java註解