【java基礎】IOC介紹及其簡單實現


  控制反轉(Inversion of Control,英文縮寫為IoC)是一個重要的面向對象編程的法則來削減計算機程序的耦合問題,也是輕量級的Spring框架的核心。 控制反轉一般分為兩種類型,依賴注入(Dependency Injection,簡稱DI)和依賴查找。依賴注入應用比較廣泛,我們這里只介紹依賴注入。

  一、IOC簡介

  控制反轉IOC,它最主要反映的是與傳統面向對象(OO)編程的不同。通常我們編程實現某種功能都需要幾個對象相互作用,從編程的角度出發,也就是一個主對象要保存其他類型對象的引用,通過調用這些引用的方法來完成任務。如何獲得其他類型的對象引用呢?一種方式是主對象內部主動獲得所需引用(也就是通常我們使用的new一個對象);另一種方式是在主對象中設置setter 方法,通過調用setter方法或構造方法傳入所需引用。后一種方式就叫IOC,也是我們常常所說的依賴注入DI。以下我們用一個簡單的例子來說明傳統OO編程與IOC編程的差別。

  這個例子的目的是根據時間不同返回不同的問候字符串, 比如Good Morning, world或Good afternoon, World。

  服務接口:

package cn.test.ioc;
public interface HelloIF {
    String sayHello();
}

  傳統實現:

package cn.test.ioc;

import java.util.Calendar;

/**
 * 傳統實現(非IOC方式)
 * @author Administrator
 *
 */
public class HelloIFImpl implements HelloIF {
    private Calendar cal; // 我們需要的引用

    public HelloIFImpl() {
        cal = Calendar.getInstance(); // 主動獲取
    }

    public String sayHello(){
        if(cal.get(Calendar.AM_PM) == Calendar.AM){
            return "Good morning, World";
        }else{
            return "Good afternoon, World";
        } 
    }
    
    public static void main(String args[]){
        HelloIFImpl hf = new HelloIFImpl();
        System.out.println(hf.sayHello());
    }
}

  采用IOC方式:

package cn.test.ioc;

import java.util.Calendar;

/*
 * IOC方式實現
 */
public class HelloIFImpl2 implements HelloIF {
    private Calendar cal; // 我們需要的引用

    public void setCal(Calendar cal) {
        this.cal = cal;
    } // 依賴注入

    public String sayHello(){
        if(cal.get(Calendar.AM_PM) == Calendar.AM){
            return "Good morning, World";
        }else{
            return "Good afternoon, World";
        } 
    }
    
    public static void main(String args[]){
        HelloIFImpl2 hf = new HelloIFImpl2();
        hf.setCal(Calendar.getInstance());
        System.out.println(hf.sayHello());
    }
}

  在這里你也許會問:我看不出有太大差別,並且依賴注入還需要我先創建外部的Calendar對象,然后再傳到HelloIFImpl對象中。但是,假如我們事先已經在類用new orderOracle(),但是后來由於需求變更,我們需要使用new orderSqlServer(),這樣我們還需要修改所有使用orderOracle()的類,這樣好麻煩。如果我們使用IOC方法編程並且使用了spring框架,這樣我們只需要修改xml配置文件即可。

  IoC則是一種 軟件設計模式,它告訴你應該如何做,來解除相互依賴模塊的耦合。控制反轉(IoC),它為相互依賴的組件提供抽象,將依賴(低層模塊)對象的獲得交給第三方(系統)來控制,即依賴對象不在被依賴模塊的類中直接通過new來獲取。

  二、IOC的一個應用舉例

  下面我們以一個struts2和Spring整合的例子來說明。

  1)整合struts2和Spring

  首先要整合Spring和Struts2,需要先要拷入Spring需要的jar包,既包括Spring本身的jar包,也包括Struts2的Spring插件。將以下幾個jar包拷入到我們的web工程的WEB-INF\lib中:org.springframework.asm-3.0.5.RELEASE.jar ;spring-*.jar(struts2中lib包里所有的jar,共6個); struts2-spring-plugin-*.jar 。

  2)編寫邏輯層接口

package cn.test.springDemo;

public interface SampleService {
    public String getNameById(String userId);  
}

  3)編寫邏輯層實現類

package cn.test.springDemo;

public class SampleServiceImpl implements SampleService{  
    public String getNameById (String userId) {  
        //根據userId到數據層進行查詢,獲取相應的name  
        String name = "hello,"+ userId;  
        return name;  
    }  
}  

  4)編寫ACTION

package cn.test.springDemo;

import com.opensymphony.xwork2.ActionSupport;

public class SampleAction extends ActionSupport {
    // 通過setter方式,由Spring來注入SampleService實例
    private SampleService service;

    public void setService(SampleService service) {
        this.service = service;
    }

    private String name;
    private String userId;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String execute() throws Exception {
        name = this.service.getNameById(userId);
        return SUCCESS;
    }
}

  在execute方法中不再直接new一個SampleServiceImpl的實例了,而是聲明了一個SampleSerivce類型的屬性,並提供對應的setter方法,這個setter方法是留給Spring注入對象實例的時候調用的,可以不用提供getter方法。也就是說,現在的SampleAction已經不用知道邏輯層的具體實現了。

  5)編寫Spring的配置文件applicationContext.xml

  要讓Spring來管理SampleAction和SampleServiceImpl的實例,還需要新建一個Spring的配置文件。在src下新建一個applicationContext.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-2.5.xsd">

    <bean name="service" class="cn.test.springDemo.SampleServiceImpl" />
    <bean name="sampleAction" class="cn.test.springDemo.SampleAction" scope="prototype" > 
        <property name="service" ref="sampleService"/>  
    </bean>  
</beans>  

  這個xml的根元素是<beans>,在<beans>中聲明了它的schema引用,除此之外,還有兩個<bean>元素,定義了由Spring管理的SampleServiceImpl和SampleAction。

  • 對於第一個<bean>元素來說

  l         name屬性為它設置了一個名字

  l         class元素指定了它的實現類的全類名

  • 對於第二個<bean>元素來說

  l         name屬性和class屬性的含義與第一個<bean>元素完全一樣。

  l         scope屬性,賦值為prototype(原型)。scope屬性非常重要,它管理了注冊在它里面的Bean的作用域。Spring容器默認的作用域是單例,即每次外界向Spring容器請求這個Bean,都是返回同一個實例;但是,Struts2的Action是需要在每次請求的時候,都要新建一個Action實例,所以,在配置對應Action的<bean>元素時,必須把它的scope屬性賦值為prototype,以保證每次請求都會新建一個Action實例。

  l         <property>子元素。<property>元素的name屬性為service,代表SampleAction這個類有一個setter方法叫setSampleService;<property>元素的ref屬性為sampleService,代表Spring容器會將一個名為sampleService的已經存在的Bean,注入給sampleAction的service屬性。

  6)在web.xml中引用Spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
    http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <filter>
        <filter-name>Struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

  listener實現了當這個Web工程啟動的時候,就去讀取Spring的配置文件,這個類是由Spring提供的,這里只需要配置上去就可以了。上下文參數的配置里面,contextConfigLocation的值classpath*:applicationContext.xml,表明了所有出現在classpath路徑下的applicationContext.xml文件,都是上面的這個Listener要讀取的Spring配置文件。

  7)編寫struts.xml  

  就快要大功告成了,最后一步,來修改struts.xml,需要做兩件事:

  首先,添加常量struts.objectFactory,其值為spring,這就指定了Struts2使用Action的時候並不是自己去新建,而是去向Spring請求獲取Action的實例。示例如下:

<constant name="struts.objectFactory" value="spring"/>  

  然后,<action>元素的class屬性,現在並不是要填Action類的全類名了,而是要填一個在Spring配置文件中配置的Action的Bean的名字,也就是<bean>元素的name屬性,很顯然,需要的是sampleAction這個Bean。示例如下:

<package name="springPackage" extends="struts-default">
        <action name="sampleActionName" class="sampleAction">
            <result>/spring/success.jsp</result>
        </action>
</package>

  只有<action>元素的class屬性變了,其他部分不變。來測試一下,運行:http://localhost:8080/struts2Deepen2/sampleActionName.action?userId=test。【其中,struts2Deepen2為web工程的名稱】。運行一切正常,對吧,這也說明Struts2與Spring整合並不是為了實現新功能,而是為了讓表現層組件和邏輯層組件解耦,SampleAction類不用再知道SampleServiceImpl這個具體實現了,只需要知道SampleService這個接口就可以了。

參考資料:

  http://blog.csdn.net/Kettas2008/article/details/2447809

  http://www.iteye.com/topic/1124526

  http://www.cnblogs.com/liuhaorain/p/3747470.htm#title_4 【這篇文章詳細介紹了DIP、IoC、DI以及IoC容器,推薦看看】


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM