1. 程式人生 > >spring 裝配bean的三種方式

spring 裝配bean的三種方式

str xmlns utf-8 聲明 學習 pri 改變 div 系統

這段時間在學習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      */
 7     public 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 */ 16 public int addpost(int userid); 17 }

再在bbs.service包中創建一個postservice接口及其實現類,依賴Postdao和Userdao。

 1 package bbs.service;
 2 public interface PostService {
 3     /*
 4     用戶發帖後,先添加帖子信息再更新用戶發帖數量
 5     */
 6     public void addpost(String title,String content,int userid);
 7 }
 8 
 9 package bbs.service;
10 @Component
11 public class PostserviceImpl implements PostService {
12 
13     private Postdao postdao;
14     private 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
29     public PostserviceImpl(Postdao postdao,Userdao userdao)
30     {
31         this.userdao=userdao;
32         this.postdao=postdao;
33     }
34 
35     public void addpost(String title, String content, int userid) {
36         int i=postdao.addpost(title, content, userid);
37         int j=userdao.addpost(userid);
38         if(i==1&j==1)
39             System.out.println("發帖成功");
40         else
41             System.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註解

spring 裝配bean的三種方式