(轉)Spring實現IoC的多種方式

控制反轉IoC(Inversion of Control),是一種設計思想,DI(依賴注入)是實現IoC的一種方法,也有人認爲DI只是IoC的另外一種說法。沒有IoC的程序中咱們使用面向對象編程對象的建立與對象間的依賴關係徹底硬編碼在程序中,對象的建立由程序本身控制,控制反轉後將對象的建立轉移給第三方,我的認爲所謂控制反轉就是:獲取依賴對象的方式反轉了正則表達式

IoC是Spring框架的核心內容,使用多種方式完美的實現了IoC,可使用XML配置,也可使用註解,新版本的Spring也能夠零配置實現IoC。Spring容器在初始化時先讀取配置文件,根據配置文件或元數據建立與組織對象存入容器中,程序使用時再從Ioc容器中取出須要的對象。
spring

採用XML方式配置Bean的時候,Bean的定義信息是和實現分離的,而採用註解的方式能夠把二者合爲一體,Bean的定義信息直接以註解的形式定義在實現類中,從而達到了零配置的目的。express

1、使用XML配置的方式實現IOC

假設項目中須要完成對圖書的數據訪問服務,咱們定義好了IBookDAO接口與BookDAO實現類apache

IBookDAO接口以下:編程

複製代碼
package com.zhangguo.Spring051.ioc01;

/**
 * 圖書數據訪問接口
 */
public interface IBookDAO {
    /**
     * 添加圖書
     */
    public String addBook(String bookname);
}
複製代碼

BookDAO實現類以下:框架

複製代碼
package com.zhangguo.Spring051.ioc01;

/**
 * 圖書數據訪問實現類
 */
public class BookDAO implements IBookDAO {

    public String addBook(String bookname) {
        return "添加圖書"+bookname+"成功!";
    }
}
複製代碼

Maven項目的pom.xml以下:maven

複製代碼
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.zhangguo</groupId>
  <artifactId>Spring051</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>Spring051</name>
  <url>http://maven.apache.org</url>

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.3.0.RELEASE</spring.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.4</version>
        </dependency>
    </dependencies>
</project>
複製代碼

業務類BookService以下:函數

複製代碼
package com.zhangguo.Spring051.ioc01;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 圖書業務類
 */
public class BookService {
    IBookDAO bookDAO;
    
    public BookService() {
        //容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans01.xml");
        //從容器中得到id爲bookdao的bean
        bookDAO=(IBookDAO)ctx.getBean("bookdao");
    }
    
    public void storeBook(String bookname){
        System.out.println("圖書上貨");
        String result=bookDAO.addBook(bookname);
        System.out.println(result);
    }
}
複製代碼

 容器的配置文件IOCBeans01.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" 
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="bookdao" class="com.zhangguo.Spring051.ioc01.BookDAO"></bean>
</beans>
複製代碼

測試類Test以下:

複製代碼
package com.zhangguo.Spring051.ioc01;

public class Test {
    @org.junit.Test
    public void testStoreBook()
    {
        BookService bookservice=new BookService();
        bookservice.storeBook("《Spring MVC權威指南 初版》");
    }
}
複製代碼

運行結果:

 

2、使用Spring註解配置IOC

 上一個示例是使用傳統的xml配置完成IOC的,若是內容比較多則配置需花費不少時間,經過註解能夠減輕工做量,但註解後修改要麻煩一些,偶合度會增長,應該根據須要選擇合適的方法。

2.一、修改BookDAO

複製代碼
package com.zhangguo.Spring051.ioc02;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * 圖書數據訪問實現類
 */
@Component("bookdaoObj")
public class BookDAO implements IBookDAO {

    public String addBook(String bookname) {
        return "添加圖書"+bookname+"成功!";
    }
}
複製代碼

在類上增長了一個註解Component,在類的開頭使用了@Component註解,它能夠被Spring容器識別,啓動Spring後,會自動把它轉成容器管理的Bean

除了@Component外,Spring提供了3個功能基本和@Component等效的註解,分別對應於用於對DAO,Service,和Controller進行註解。
1:@Repository 用於對DAO實現類進行註解。
2:@Service 用於對業務層註解,可是目前該功能與 @Component 相同。
3:@Constroller用於對控制層註解,可是目前該功能與 @Component 相同。

2.二、修改BookService

複製代碼
package com.zhangguo.Spring051.ioc02;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * 圖書業務類
 */
@Component
public class BookService {
    IBookDAO bookDAO;
    
    public void storeBook(String bookname){
        //容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml");
        //從容器中得到id爲bookdao的bean
        bookDAO=(IBookDAO)ctx.getBean("bookdaoObj");
        System.out.println("圖書上貨");
        String result=bookDAO.addBook(bookname);
        System.out.println(result);
    }
}
複製代碼

將構造方法中的代碼直接寫在了storeBook方法中,避免循環加載的問題。

2.三、修改IOC配置文件IOCBeans02.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" 
    xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
        <context:component-scan base-package="com.zhangguo.Spring051.ioc02"></context:component-scan>
</beans>
複製代碼

粗體字是新增的xml命名空間與模式約束文件位置。增長了註解掃描的範圍,指定了一個包,能夠經過屬性設置更加精確的範圍如:

<context>標記經常使用屬性配置:
resource-pattern:對指定的基包下面的子包進行選取
<context>子標記:
include-filter:指定須要包含的包
exclude-filter:指定須要排除的包
<!-- 自動掃描com.zhangguo.anno.bo中的類進行掃描 --> 
<context:component-scan base-package="com.zhangguo.anno" resource-pattern="bo/*.class" />

<context:component-scan base-package="com.zhangguo.anno" >

  <context:include-filter type="aspectj「 expression="com.zhangguo.anno.dao.*.*"/>
  <context:exclude-filter type=「aspectj」 expression=「com.zhangguo.anno.entity.*.*」/>

</context:component-scan>

include-filter表示須要包含的目標類型,exclude-filter表示須要排除的目標類型,type表示採的過濾類型,共有以下5種類型:

Filter Type Examples Expression Description
annotation org.example.SomeAnnotation 註解了SomeAnnotation的類
assignable org.example.SomeClass 全部擴展或者實現SomeClass的類
aspectj org.example..*Service+ AspectJ語法表示org.example包下全部包含Service的類及其子類
regex org\.example\.Default.* Regelar Expression,正則表達式
custom org.example.MyTypeFilter 經過代碼過濾,實現org.springframework.core.type.TypeFilter接口

expression表示過濾的表達式。

    <!-- 一、若是僅但願掃描特定的類而非基包下的全部類,可以使用resource-pattern屬性過濾特定的類 -->
    <context:component-scan base-package="com.zhangguo.Spring051"
        resource-pattern="ioc04/A*.class">
    </context:component-scan>

只掃描com.zhangguo.Spring051.ioc04下全部名稱以A開始的類。

複製代碼
    <!--二、掃描註解了org.springframework.stereotype.Repository的類
     exclude-filter表示排除,include-filter表示包含,能夠有多個-->
    <context:component-scan base-package="com.zhangguo.Spring051.ioc04"> 
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository" />
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
    </context:component-scan>
複製代碼
複製代碼
   <!--三、aspectj類型,掃描dao下全部的類,排除entity下全部的類-->
  <context:component-scan base-package="com.zhangguo.anno" >
  <context:include-filter type="aspectj" expression="com.zhangguo.anno.dao.*.*"/>
  <context:exclude-filter type="aspectj" expression="com.zhangguo.anno.entity.*.*"/>
</context:component-scan>
複製代碼

2.四、測試類

複製代碼
package com.zhangguo.Spring051.ioc02;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    @org.junit.Test
    public void testStoreBook()
    {
        //容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans02.xml");
        BookService bookservice=ctx.getBean(BookService.class);
        bookservice.storeBook("《Spring MVC權威指南 第二版》");
    }
}
複製代碼

運行結果:

2.五、小結

從配置文件中咱們能夠看出咱們並無聲明bookdaoObj與BookService類型的對象,但仍是從容器中得到了實例併成功運行了,緣由是:在類的開頭使用了@Component註解,它能夠被Spring容器識別,啓動Spring後,會自動把它轉成容器管理的Bean。

 

3、自動裝配

 從上一個示例中能夠看出有兩個位置都使用了ApplicationContext初始化容器後得到須要的Bean,能夠經過自動裝配簡化。

3.一、修改BookDAO

複製代碼
package com.zhangguo.Spring051.ioc03;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * 圖書數據訪問實現類
 */
@Repository
public class BookDAO implements IBookDAO {

    public String addBook(String bookname) {
        return "添加圖書"+bookname+"成功!";
    }
}
複製代碼

把註解修改爲了Repository,比Component更貼切一些,非必要。

3.二、修改BookService

複製代碼
package com.zhangguo.Spring051.ioc03;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

/**
 * 圖書業務類
 */
@Service
public class BookService {
    @Autowired
    IBookDAO bookDAO;
    
    public void storeBook(String bookname){
        System.out.println("圖書上貨");
        String result=bookDAO.addBook(bookname);
        System.out.println(result);
    }
}
複製代碼

將類BookService上的註解替換成了Service;在bookDao成員變量上增長了一個註解@Autowired,該註解的做用是:能夠對成員變量、方法和構造函數進行註解,來完成自動裝配的工做,通俗來講就是會根據類型從容器中自動查到到一個Bean給bookDAO字段。@Autowired是根據類型進行自動裝配的,若是須要按名稱進行裝配,則須要配合@Qualifier。另外可使用其它註解,@ Resource :等同於@Qualifier,@Inject:等同於@ Autowired。

@Service用於註解業務層組件(咱們一般定義的service層就用這個)

@Controller用於註解控制層組件(如struts中的action)

@Repository用於註解數據訪問組件,即DAO組件

@Component泛指組件,當組件很差歸類的時候,咱們可使用這個註解進行註解。

裝配註解主要有:@Autowired、@Qualifier、@Resource,它們的特色是:

一、@Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean纔會按照類型來裝配注入;

二、@Autowired默認是按照類型裝配注入的,若是想按照名稱來轉配注入,則須要結合@Qualifier一塊兒使用;

三、@Resource註解是又J2EE提供,而@Autowired是由spring提供,故減小系統對spring的依賴建議使用@Resource的方式;若是Maven項目是1.5的JRE則需換成更高版本的。

四、@Resource和@Autowired均可以書寫註解在字段或者該字段的setter方法之上

五、@Autowired 能夠對成員變量、方法以及構造函數進行註釋,而 @Qualifier 的註解對象是成員變量、方法入參、構造函數入參。

六、@Qualifier("XXX") 中的 XX是 Bean 的名稱,因此 @Autowired 和 @Qualifier 結合使用時,自動注入的策略就從 byType 轉變成 byName 了。

七、@Autowired 註釋進行自動注入時,Spring 容器中匹配的候選 Bean 數目必須有且僅有一個,經過屬性required能夠設置非必要。

八、@Resource裝配順序
  8.1. 若是同時指定了name和type,則從Spring上下文中找到惟一匹配的bean進行裝配,找不到則拋出異常
  8.2. 若是指定了name,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常
  8.3. 若是指定了type,則從上下文中找到類型匹配的惟一bean進行裝配,找不到或者找到多個,都會拋出異常
  8.4. 若是既沒有指定name,又沒有指定type,則自動按照byName方式進行裝配;若是沒有匹配,則回退爲一個原始類型進行匹配,若是匹配則自動裝配;

複製代碼
package com.zhangguo.Spring051.ioc05;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

/**
 * 圖書業務類
 */
@Service
public class BookService {
    
    public IBookDAO getDaoofbook() {
        return daoofbook;
    }

    /*
    @Autowired
    @Qualifier("bookdao02")
    public void setDaoofbook(IBookDAO daoofbook) {
        this.daoofbook = daoofbook;
    }*/
    
    @Resource(name="bookdao02")
    public void setDaoofbook(IBookDAO daoofbook) {
        this.daoofbook = daoofbook;
    }

    /*
    @Autowired
    @Qualifier("bookdao02")
    */
    IBookDAO daoofbook;
    
    /*
    public BookService(@Qualifier("bookdao02") IBookDAO daoofbook) {
        this.daoofbook=daoofbook;
    }*/
    
    public void storeBook(String bookname){
        System.out.println("圖書上貨");
        String result=daoofbook.addBook(bookname);
        System.out.println(result);
    }
}
複製代碼

3.三、測試運行

複製代碼
package com.zhangguo.Spring051.ioc03;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
    @org.junit.Test
    public void testStoreBook()
    {
        //容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("IOCBeans03.xml");
        BookService bookservice=ctx.getBean(BookService.class);
        bookservice.storeBook("《Spring MVC權威指南 第三版》");
    }
}
複製代碼

運行結果:

4、零配置實現IOC

所謂的零配置就是再也不使用xml文件來初始化容器,使用一個類型來替代,

 IBookDAO代碼以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

/**
 * 圖書數據訪問接口
 */
public interface IBookDAO {
    /**
     * 添加圖書
     */
    public String addBook(String bookname);
}
複製代碼

IBookDAO的實現類BookDAO代碼以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * 圖書數據訪問實現類
 */
@Repository
public class BookDAO implements IBookDAO {

    public String addBook(String bookname) {
        return "添加圖書"+bookname+"成功!";
    }
}
複製代碼

在BookDAO類上註解了@Repository當初始化時該類將被容器管理會生成一個Bean,能夠經過構造方法測試。

業務層BookService代碼以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

import javax.annotation.Resource;

import org.springframework.stereotype.Service;

/**
 * 圖書業務類
 */
@Service
public class BookService {
    @Resource
    IBookDAO bookDAO;
    
    public void storeBook(String bookname){
        System.out.println("圖書上貨");
        String result=bookDAO.addBook(bookname);
        System.out.println(result);
    }
}
複製代碼

類BookService將對容器管理由於註解了@Service,初始化時會生成一個單例的Bean,類型爲BookService。在字段bookDAO上註解了@Resource,用於自動裝配,Resource默認是按照名稱來裝配注入的,只有當找不到與名稱匹配的bean纔會按照類型來裝配注入。

新增一個用於替代原xml配置文件的ApplicationCfg類,代碼以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 容器的配置類
 */
@Configuration
@ComponentScan(basePackages="com.zhangguo.Spring051.ioc06")
public class ApplicationCfg {
    @Bean
    public User getUser(){
        return new User("成功");
    }
}
複製代碼

@Configuration至關於配置文件中的<beans/>,ComponentScan至關於配置文件中的context:component-scan,屬性也同樣設置

,@Bean至關於<bean/>,只能註解在方法和註解上,通常在方法上使用,源碼中描述:@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),方法名至關於id。中間使用到了User,User類的代碼以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

import org.springframework.stereotype.Component;

@Component("user1")
public class User {
    public User() {
        System.out.println("建立User對象");
    }
    public User(String msg) {
        System.out.println("建立User對象"+msg);
    }
    public void show(){
        System.out.println("一個學生對象!");
    }
}
複製代碼

初始化容器的代碼與之前有一些不同,具體以下:

複製代碼
package com.zhangguo.Spring051.ioc06;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    @org.junit.Test
    public void testStoreBook()
    {
        //容器,註解配置應用程序容器,Spring經過反射ApplicationCfg.class初始化容器
        ApplicationContext ctx=new AnnotationConfigApplicationContext(ApplicationCfg.class);
        BookService bookservice=ctx.getBean(BookService.class);
        bookservice.storeBook("《Spring MVC權威指南 第四版》");
        User user1=ctx.getBean("user1",User.class);
        user1.show();
        User getUser=ctx.getBean("getUser",User.class);
        getUser.show();
    }
}
複製代碼

容器的初始化經過一個類型完成,Spring經過反射ApplicationCfg.class初始化容器,中間user1與getUser是否爲相同的Bean呢?

答案是否認的,由於在ApplicationCfg中聲明的方法getUser當相於在xml文件中定義了一個<bean id="getUser" class="..."/>,在User類上註解@Component("user1")至關於另外一個<bean id="user1" class="..."/>。

運行結果:

小結:使用零配置和註解雖然方便,不須要編寫麻煩的xml文件,但並不是爲了取代xml,應該根據實例須要選擇,或兩者結合使用,畢竟使用一個類做爲容器的配置信息是硬編碼的,很差在發佈後修改。

 

5、示例下載

點擊下載

相關文章
相關標籤/搜索