Spring的IOC容器—依賴注入

前面一篇博客大致講了一下Spring的IOC容器的原理,IOC即控制反轉主要是依靠依賴注入的方式來實現的。依賴注入是指所依賴的對象不是由自己new出來的,而是用別的方式像打針似的注入進來。 其實說白了不管是控制反轉還是依賴注入都說明了Spring採用動態、靈活的方式來管理各種對象。

Spring的依賴注入對調用者和被調用者幾乎沒有任何要求,完全支持對POJO之間依賴關係的管理。有以下幾種注入方式: 


1. Setter 注入

因爲對於javaBean來說,我們可以通過setter和getter方法分別改變對象和獲取對象的屬性。所以當前對象只要爲其依賴對象所對應的屬性添加setter方法,就可以通過setter方法將相應的依賴對象設置到被注入對象中。例如下面的代碼:


 . Person接口
 
package com.tgb.depency;
/**
 * 定義Person接口 
 * @author MINGXUANYUN
 *
 */
public interface Person {
	
	//定義一個吃東西的方法
	public void eatSomething(); 
	
	//定義一個個人信息方法
	public String personInfo();
}

.Eat接口

package com.tgb.depency;
public interface Eat {
	//定義一個吃蘋果的方法
	public String eatApple(); 
	
}

.Person的實現類

/**
 * Person的實現類
 * @author MINGXUANYUN
 */
public class Huohuo implements Person{
	
	private Eat eat;
	//默認的構造器
	public Huohuo(){}
	
	public Eat getEat() {
		return eat;
	}
	public void setEat(Eat eat) {
		this.eat = eat;
	}
	
	//實現Person吃東西的方法
	@Override
	public void eatSomething() {
		System.out.println(eat.eatApple());		
	}
	
	@Override
	public String personInfo() {
		return null;
	} 
}

.Eat的實現類

/**
 * 大口大口吃
 * @author MINGXUANYUN
 */
public class WideMouthEat implements Eat {

	@Override
	public String eatApple() {
		return"張大嘴巴吃蘋果很粗魯的!!!";
	}
}

.配置文件,將Person實例和Eat實例組織在一起

	<!--Setter注入測試實例 -->
		<bean id="Huohuo" class= "com.tgb.depencyimpl.Huohuo">
			<property name="eat" ref="WideMouthEat"></property>
		</bean>
		<bean id="WideMouthEat" class="com.tgb.depencyimpl.WideMouthEat">
		</bean>

從以上例子可以看出:Spring將bean與bean之間的依賴關係放到配置文件裏管理,而不是寫在代碼裏。通過配置文件,Spring精確的爲每個bean注入每個屬性。<bean>中的name屬性是class屬性的一個別名,class屬性指真正的實現類(類的全名)。Spring自動接管每個bean裏的 property元素定義。Spring再執行無參數的構造器,創建默認的bean後,調用對應的setter方法爲程序注入屬性值。


.主程序

public class SetterTest {

	public static void main(String[] args) {
		//實例化Spring的上下文
		ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
		//強制轉換接口類型
		Person person = (Person) ctx.getBean("Huohuo");
		//執行Person的eatSomething方法
		person.eatSomething();
	}

}

.運行結果輸出 :

                     張大嘴巴吃蘋果很粗魯的!!!


說明: 主程序調用person.eatSomething方法,該方法方法體裏需要有Eat的實例,但主程序裏並沒有任何地方爲Person實例傳入Eat實例,Eat實例是由Spring在運行期間動態注入的。Person實例不需要了解Eat實例的具體實現和創建過程。程序運行到Eat實例的時候,Spring自動創建Eat實例,然後注入給它的調用者,Person實例運行到需要Eat實例的時候,自動產生Eat實例。


這樣有什麼好處呢?如果我們需要另一個Eat實現類來爲Person類使用。Person和其實現類都無需改變。只需要增加一個Eat的實現類,並在配置文件中配置就可以。


例如,增加Eat實現類ChewslowlyEat

import com.tgb.depency.Eat;

public class ChewslowlyEat implements Eat {
	@Override
	public String eatApple() {
		return "細嚼慢嚥淑女的典範!!";
	}
}

修改配置文件:

<bean id="Huohuo" class= "com.tgb.depencyimpl.Huohuo">
   <property name="eat" ref="ChewslowlyEat"></property>
</bean>
<bean id="ChewslowlyEat" class="com.tgb.depencyimpl.ChewslowlyEat"></bean>	

輸出結果:

    細嚼慢嚥淑女的典範!!


2. 構造器注入

  指被注入對象通過在其構造函數中聲明依賴對象的參數列表。


.定義一個Person實現類,用於構造器注入

/**
 * 構造器注入測試
 * @author MINGXUANYUN
 */
public class Yunyun implements Person{
    private String userName;
    private String kind;
    private Eat eat; 
    //默認構造方法
    public Yunyun(){};
    //構造注入所需的帶參數構造器
    public Yunyun(Eat eat,String userName,String kind){
        this.eat = eat; 
        this.userName = userName;
        this.kind = kind; 
    }
    @Override
    public String personInfo() {
        return userName + "永遠" + kind +"歲" + "----" + eat.eatApple();
    }
    @Override
    public void eatSomething() {
        // TODO Auto-generated method stub    
    }
}

在構造Yunyun實例時,創建三個成員變量userName、kind和對象類型的eat。但是並沒有設置setter方法。在構造Person實例時,Spring爲Person實例注入所依賴的Eat實例並且爲兩個成員變量賦值。構造注入的配置文件如下:

.xml配置文件

<!--構造器注入配置實例 -->
        <bean id="Yunyun" class= "com.tgb.depencyimpl.Yunyun">
        <constructor-arg index="0" ref="ChewslowlyEat"></constructor-arg>
        <constructor-arg index="1">  
            <value>朱火雲 </value>  
          </constructor-arg>  
          <constructor-arg index="2">  
            <value>18</value>  
          </constructor-arg> 
        </bean>  
        <bean id="ChewslowlyEat" class="com.tgb.depencyimpl.ChewslowlyEat"/>

在配置文件中,不用<property>的形式,而是使用<constructor-arg>標籤。ref屬性指向其它<bean>標籤的name屬性。由於我們可能傳入多個類型一致的構造參數。所以可以用type和index確定我們使用的哪一個構造函數。


.主程序

public class ConstructorTest {

	public static void main(String[] args) {	
		 String fileName = "bean.xml";  
         ApplicationContext ac = new FileSystemXmlApplicationContext(fileName);  
         // 取得一個實例 
         Yunyun yunyun =  (Yunyun) ac.getBean("Yunyun");  
         System.out.println(yunyun.personInfo());  
	}
}

輸出結果:

              朱火雲 永遠18歲細嚼慢嚥淑女的典範!!

3.靜態工廠方法注入

    指通過調用靜態工廠的方法來獲取自己需要的對象。

.定義一個UserDao類

public class UserDao {
	public static UserDao getInstance(){
		return new UserDao("static factory method");
	}
	
	private String name=""; 
	public UserDao(String name){
		this.name = name; 
	}
	
	public void create(){
		System.out.println("create user from-" + name);
	}
}

.定義一個UserManager類

import com.tgb.depency.UserDao;

public class UserManager {
	//注入UserDao對象
	private UserDao userDao; 
	public void createUser(){
		userDao.create();
	}
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	public UserDao getUserDao() {
		return userDao;
	}
}

.xml配置文件

<!-- 靜態工廠方法注入 -->
		<bean name="userManager" class="com.tgb.depencyimpl.UserManager">
			<property name="userDao" ref="userDao"></property>
		</bean>
		<bean name="userDao" class="com.tgb.depency.UserDao" factory-method="getInstance">
		</bean>

factory-menthod定義了userDao 。Bean使用UserDao類的getInstance方法來創建自己的實例。

.主程序

public class StaticFactoryTest {

	public static void main(String[] args) {
		String fileName = "bean.xml";
		ApplicationContext ac = new FileSystemXmlApplicationContext(fileName);
		UserManager userManager = (UserManager) ac.getBean("userManager");
		userManager.createUser();
	}
}

輸出結果:

                create user from-static factory method

總結:我們原來學三層的時候,UI層調用BLL、BLL調用DAO層。各層與各層之間雖然抽象出了接口層,調用接口。但是在new的時候指向的還是具體的實現。而現在Spring有效的管理各層之間對象的調用。 不管是Action調用Services對象,還是Services調用Dao對象,Spring以鬆耦合的方式將它們組織在一起。各層之間不需要關心對象間的具體實現,以及如何創建,完全面向接口編程。





發佈了181 篇原創文章 · 獲贊 405 · 訪問量 34萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章