1. 程式人生 > >spring(3)------控制反轉(IOC)/依賴註入(DI)

spring(3)------控制反轉(IOC)/依賴註入(DI)

param mls 構造 上下文環境 麻煩 framework 中文 回調接口 可能

一。spring核心概念理解

控制反轉:

控制反轉即IoC (Inversion of Control)。它把傳統上由程序代碼直接操控的對象的調用權交給容器。通過容器來實現對象組件的裝配和管理。

所謂的“控制反轉”概念就是對組件對象控制權的轉移,從程序代碼本身轉移到了外部容器。


沒有控制反轉這樣的模式前。你創建一個對象。在什麽地方用。你得單獨通過keywordnew出來用,

但如今能夠不用這樣,你把new對象的權利交給spring配置文件。通過配置文件來‘new‘,

就是權利的反轉。你曾經幹的事,如今交給別人幹了。



IoC是一個非常大的概念,能夠用不同的方式來實現。


其主要實現方式有兩種:
(1)依賴查找(Dependency Lookup):容器提供回調接口和上下文環境給組件
(2)依賴註入(Dependency Injection):組件不做定位查詢,僅僅提供普通的Java方法讓容器去決定依賴關系。
後者是時下最流行的IoC類型,其又有接口註入(Interface Injection)。設值註入(Setter Injection)和構造子註入(Constructor Injection)三種方式。



控制反轉和依賴註入究竟是什麽關系?
控制反轉是個模式,是個大概念,而對控制反轉最詳細的解釋就是依賴註入,前者是說個大範圍,
後者是詳細到這件事情的實質上。

就好比你朋友說等你過生日一定要送你個礼物,等過生日時送了你一個手機,
而送礼物就是個大概念,好比控制反轉。詳細送了一個手機,就是依賴註入。可是兩者說明的是送礼物的這件事。
當然了,依賴註入有三種方式,就好比你朋友能夠選擇送你個蘋果手機或送你臺寶馬或送你套別墅三種註入方式,
概率沒有必要咬文嚼字的去理解,先把車會開了。慢慢就會知道車是怎麽組成的。


二,IoC概念實例理解

在寫一個程序。有多中實現方式,你假設每次都通過改程序代碼。通過new來實現不同的功能,耦合性太高,

IoC是為解決松耦合而生的。下面通過代碼理解。

(1)創建接口和實現類

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 講不同語言
	 * @return
	 */
	public String doSay();
}

說中文實現類:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講漢語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg;
	}

}

說英文實現類:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講英語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

(2)配置spring文件

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講英語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}

(3)測試程序

package com.lanhuigu.spring.test;

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

import com.lanhuigu.spring.impl.ISayHello;

public class TestHelloWorld {
	@Test
	public void testMyHelloWorld(){
		//1.讀取spring初始化的配置文件
		ApplicationContext acxt = 
				new ClassPathXmlApplicationContext("/applicationContext.xml");
		//2.依據bean獲取ISayHello實現類對象
		ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");
		//3.調用接口方法
		System.out.println(sayHello.doSay());
	}
}
通過以上代碼對IoC進行實例體會:

(1)在測試程序中,假設不使用IoC,你能夠通過new來實現講什麽語言

ISayHello sayHello = new ChHelloImpl();//講漢語

ISayHello sayHello = new EnHelloImpl();//講英語

也許會認為我就new就能夠了,領導讓我講英語,我就new講英語。領導要我講漢語,我就講漢語,來回凝視掉即可了,

可是。假設一個應用程序中有一萬個地方用到了這段代碼, 你是不是得凝視一萬個地方,心裏絕對10萬個對領導不滿。改來改去有病吧!

你也許還不服。我就凝視掉一萬個地方,假設有100萬個地方呢,你是不是100萬個‘草泥馬‘在草原上奔騰啊!


這個時候IoC來為你解決煩惱,一劑藥就能治好,再也不用操心領導讓你說啥你確有苦說不出口了。

通過配置文件控制該講什麽語言:

<bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl">
<!-- 將變量msg值依賴註入 -->
<property name="msg">
<value>測試</value>
</property>
</bean>

你的配置文件通過實現類配置讓講啥都行。class="com.lanhuigu.spring.impl.ChHelloImpl",

而應用代碼不受影響:

ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");

System.out.println(sayHello.doSay());

應用程序僅僅管拿到的bean對象是啥。我就說啥,至於你容器怎麽配置。應用程序不屌你,

而容器則配置自己的實現類。至於你應用程序實現成啥樣。容器才無論。通過這種關系

實現低耦合的實效,實現控制反轉的職能。

三,依賴註入(DI)

依賴註入的含義:

讓主鍵依賴於抽象,當組件要與其它實際對象發生關系時,通過抽象來註入依賴的實際對象。

依賴註入的三種實現方式各自是接口註入(interface injection),set方法註入(setter injection)。構造註入(constructor injection)。

(1)接口註入(interface injection):

定義一個將英語的接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 講英語
	 * @return
	 */
	public String doSay(EnHelloImpl en);
}
講英語接口實現類:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講英語
	 */
	@Override
	public String doSay(EnHelloImpl en) {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}
假設採用接口註入的方式。原本能說各種語言的接口,被定義為僅僅能講英語的接口。整個業務邏輯類依賴於這個接口。

在spring中接口註入是在IOC概念沒有確立時定義的,如今spring中不推薦用接口註入,接口註入業務邏輯類依賴於接口,不是重點。

(2)set方法註入(setter injection):

set註入就是指在接受註入的類中定義一個set方法,並在參數中定義須要註入的參數。

為了可以將多種語言,接口註入方式是不適用的,我們把接口定義成開放的。這樣每一個實現類依據須要做自己

的業務邏輯,通過set方法傳入參數。

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 講不同語言
	 * @return
	 */
	public String doSay();
}

實現類:

漢語實現類:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講漢語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg;
	}

}

英語實現類:

package com.lanhuigu.spring.impl;

public class EnHelloImpl implements ISayHello {
	private String msg;
	
	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
	/**
	 * 講英語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "英文:"+msg;
	}

}
配置文件傳入參數:

<?xml version="1.0" encoding="UTF-8"?>

<!--
  - Application context definition for JPetStore's business layer.
  - Contains bean references to the transaction manager and to the DAOs in
  - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation").
  -->
<beans xmlns="http://www.springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xmlns:aop="http://www.springframework.org/schema/aop"
		xmlns:tx="http://www.springframework.org/schema/tx"
		xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
			http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<!-- 定義一個id為sayHello的bean,
	通過spring配置文件變換實現類。實現不同的功能,無需改動別的程序 -->
	<bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl">
		<!-- 將變量msg值依賴註入 -->
		<property name="msg">
			<value>測試</value>
		</property>
	</bean>
</beans>
每個接口實現類相應一種邏輯,spring配置文件負責調配。

配置文件bean的property有個默認屬性name,這個name相應的是bean的class類對象中的屬性名msg,

代碼樣式private String msg,value為屬性值,通過類中的set方法,將配置文件裏的屬性值註入,

在類中通過get獲取屬性值。


(3)構造註入(constructor injection):

構造註入就是在接受註入的類中定義一個構造方法,並在參數中定義須要註入的元素。

咱們將講英語改成構造方法註入:

接口:

package com.lanhuigu.spring.impl;

public interface ISayHello {
	/**
	 * 講不同語言
	 * @return
	 */
	public String doSay();
}
實現類:

package com.lanhuigu.spring.impl;

public class ChHelloImpl implements ISayHello {
	private String msg;
	private String two;
	/**
	 * 構造方法註入
	 * @param msg
	 * @param two
	 */
	public ChHelloImpl(String msg,String two){
		this.msg = msg;
		this.two = two;
	}
	/*public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}*/
	/**
	 * 講漢語
	 */
	@Override
	public String doSay() {
		// TODO Auto-generated method stub
		return "中文:"+msg+two;
	}

}
spring配置文件:

<?

xml version="1.0" encoding="UTF-8"?

> <!-- - Application context definition for JPetStore's business layer. - Contains bean references to the transaction manager and to the DAOs in - dataAccessContext-local/jta.xml (see web.xml's "contextConfigLocation"). --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 定義一個id為sayHello的bean, 通過spring配置文件變換實現類,實現不同的功能,無需改動別的程序 --> <bean id="sayHello" class="com.lanhuigu.spring.impl.ChHelloImpl"> <!-- 將變量msg值依賴註入 --> <!-- <property name="msg"> <value>測試</value> </property> --> <!-- 構造放入註入, 假設不寫index,默認構造方法僅僅有一個參數, 假設註入多個。就會報錯; 假設註入多個參數,能夠通過index的下標指定與構造方法參數的相應關系, 下標從0開始,0表示構造方法的第一個參數,依次類推 --> <constructor-arg index="0"> <value>測試</value> </constructor-arg> <constructor-arg index="1"> <value>two arg</value> </constructor-arg> </bean> </beans>

測試程序:

package com.lanhuigu.spring.test;

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

import com.lanhuigu.spring.impl.ISayHello;

public class TestHelloWorld {
	@Test
	public void testMyHelloWorld(){
		//1.讀取spring初始化的配置文件
		ApplicationContext acxt = 
				new ClassPathXmlApplicationContext("/applicationContext.xml");
		//2.依據bean獲取ISayHello實現類對象
		ISayHello sayHello = (ISayHello) acxt.getBean("sayHello");
		//3.調用接口方法
		System.out.println(sayHello.doSay());
	}
}
輸出結果:

中文:測試two arg

關於構造方法註入的註意點:

(1)在接受註入的構造方法中寫須要註入的參數

(2)配置文件裏構造屬性constructor-arg不寫index時默認一個參數,假設構造方法有多個參數,將報錯

(3)假設註入多個參數。需在配置文件裏constructor-arg通過index明白value相應構造方法中的參數情況

四。總結

依賴註入有三種註入方式,究竟哪種註入方式好。一般推薦使用set註入

接口註入不考慮。構造方法註入通過上面的實例能夠看出,假設註入參數太多,

構造方法中就會有一堆參數,看著都迷糊,而spring配置文件還得對參數做相應的傳值,

假設註入類太多,參數太多。構造器配置越多,就會迷糊。

而set則是依據名字註入,可以避免構造方法註入的麻煩。可是,使用set方法註入是動態的註入參數。

可能被人篡改。構造方法註入在類啟動時就完畢,就不會出現篡改的可能。相對安全性高。二者各有

千秋。可是通經常使用set註入。構造方法註入酌情處理。

來註入的,這一點比構造方法註入好


spring(3)------控制反轉(IOC)/依賴註入(DI)