天天看點

spring系列(二):依賴注入

一   初識Spring

1.1   企業級應用開發

       在學習Spring前,首先讓我們了解一下企業級應用。企業級應用是指那些為商業組織、大型企業而建立并部署的解決方案及應用。這些大型企業級應用的結構複雜,涉及的外部資源衆多,事務密集,資料規模大,使用者數量多,有較強的安全性考慮和較高的性能要求。

     當代的企業級應用絕不可能是一個個的獨立系統。在企業中,一般都會部署多個進行互動的應用,同時這些應用又都有可能與其他企業的相關應用連接配接,進而構成一個結構複雜的、跨越Internet的分布式企業應用叢集。此外,作為企業級應用,不但要有強大的功能,還要能夠滿足未來業務需求的變化,易于擴充和維護。

     傳統JavaEE解決企業級應用問題時的“重量級”架構體系,使它的開發效率、開發難度和實際的性能都令人失望。正在人們苦苦尋找解決辦法的時候,Spring以一個“救世主”的形象出現在廣大的Java程式員面前。

     說到Spring就得提到它的作者Rod Johnson,2002年他編寫了《Expert One-on-One Java EE設計與開發》一書。在書中,他對傳統的Java EE技術日益臃腫和低效提出了質疑,他覺得應該有更便捷的做法,于是提出了Interface 2.1,也就是Spring架構的雛形。他提出了技術以實用為準的主張,引發了人們對“正統”Java E.E的反思。2003年2月Spring架構正式成為一個開源項目,并釋出于SourceForge中。

     Spring緻力于JavaEE應用的各種解決方案,而不是僅僅專注于某一層的方案。可以說,Spring是企業應用開發的“一站式”選擇,Spring貫穿表現層、業務層、持久層。然而,Spring并不想取代那些已有的架構,而是以高度的開放性與它們無縫整合。

1.2 Spring的“綠草叢”

        Spring确實給人一種格外清新、爽朗的感覺,仿佛微雨後的綠草叢,讨人喜歡,又蘊藏着勃勃生機。Spring是一個輕量級架構,它大大簡化了Java企業級開發,提供了強大、穩定的功能,又沒有帶來額外的負擔,讓人們在使用它做每一件事情的時候都有得體和優雅的感覺。Spring有兩個主要目标:一是讓現有技術更易于使用,二是促進良好的程式設計習慣(或者稱為最佳實踐)。

        Spring是一個全面的解決方案,但它堅持一個原則:不重新發明輪子。已經有較好解決方案的領域,Spring絕不做重複性的實作。例如,對象持久化和ORM,Spring隻是對現有的JDBC、Hibernate等技術提供支援,使之更易用,而不是重新做一個實作。

       Spring架構由大約20個功能子產品組成。這些子產品被分組成6個部分,分别是Core Container、 Data Access/lntegration、Web、AOP(Aspect Oriented Programming)、Instrumentation及Test,如下圖所示。

spring系列(二):依賴注入

        Spring Core是架構的最基礎部分,提供了IoC特性。Spring Context為企業級開發提供了便利和內建的工具。Spring AOP是基于Spring Core的符合規範的面向切面程式設計的實作。Spring JDBC提供了JDBC的抽象層,簡化了JDBC編碼,同時使代碼更健壯。Spring ORM部分對Hibernate等ORM架構提供了支援。Spring Web為Spring在Web應用程式中的使用提供了支援。關于Spring的其他功能子產品在開發中的作用,可以查閱Spnng的文檔進行了解,這裡不再贅述。

二   spring IOC   

2.1  IOC簡介   

      控制反轉(Inversionof Control,IoC),也被稱為依賴注入(Dependency Injection,DI),是面向對象程式設計中的一種設計理念,用來減低程式代碼之間的耦合度。

首先考慮什麼是依賴。依賴,在代碼中一般指通過局部變量、方法參數、傳回值等建立的對于其他對象的調用關系。例如,在A類的方法中,執行個體化了B類的對象并調用其方法以完成特定的功能,我們就說A類依賴于B類。

        幾乎所有的應用都是由兩個或更多的類通過彼此合作來實作完整的功能。類與類之間的依賴關系增加了程式開發的複雜程度,我們在開發一個類的時候,還要考慮對正在使用該類的其他類的影響。

2.2  IOC入門案例

環境: jdk1.7   spring3.2.2

1.      建立工程,導入包

spring系列(二):依賴注入

2.   建立核心配置檔案ApplicationContext.xml

spring系列(二):依賴注入

命名空間的編寫

<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-3.0.xsd">

可以參考web.xml  和下圖

spring系列(二):依賴注入

3.   入門程式

=================HelloSpring.java======================

package com.obtk.spring;

public class HelloSpring {
	private String who;
	
	private String greet;
	
	public HelloSpring() {
		System.out.println("預設構造方法執行了");
	}
	public void setGreet(String greet) {
		this.greet = greet;
	}
	
	public void setWho(String who) {
		this.who = who;
	}
	
	public void print(){
		System.out.println("hello:"+who+","+greet);
	}
}
           

4.  配置

<?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-3.0.xsd">
	<bean id="hello" class="com.obtk.spring.HelloSpring">
		<property name="who" value="麻子"></property>
		<property name="greet" value="歡迎來到spring的世界"></property>
	</bean>
		
</beans>
           

5.  測試代碼

package com.obtk.test;

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

import com.obtk.spring.HelloSpring;

public class TestSpring2 {
	public static void main(String[] args) {
		ApplicationContext context=
			new ClassPathXmlApplicationContext("ApplicationContext.xml");
		HelloSpring hello=(HelloSpring)context.getBean("hello");
		hello.print();
	}
}
           

2.3  P标簽注入

        Spring配置檔案從2.0版本開始采用schema形式,使用不同的命名空間管理不同類型的配置,使得配置檔案更具擴充性。例如我們可以使用p命名空間簡化屬性的注入。p命名空間的特點是使用屬性而不是子元素的形式配置Bean的屬性,進而簡化了Bean的配置。

spring系列(二):依賴注入

如上圖所示,p标簽注入需要導入p命名空間。p标簽注入是set注入的另一種形式,具體代碼省略。

2.4 構造注入 

       在前面的案例中,我們使用Spring通過setter通路器實作了對屬性的指派,這種做法被稱為設值注入。除此之外,Spring還提供了通過構造方法指派的能力,稱為構造注入。

構造注入案例1

package com.obtk.spring;

public class HelloSpring {
	private String who;
	
	private String greet;
	
	public HelloSpring() {
		System.out.println("預設構造方法執行了");
	}
	//用于構造注入
	public HelloSpring(String who,String greet){
		this.who=who;
		this.greet=greet;
	}
	
	public void setGreet(String greet) {
		this.greet = greet;
	}
	
	public void setWho(String who) {
		this.who = who;
	}
	
	public void print(){
		System.out.println("hello:"+who+","+greet);
	}
}
           

配置

<!-- 構造注入 -->
	<bean id="hello" class="com.obtk.spring.HelloSpring">
	    <constructor-arg value="歡迎來到spring的世界" index="1"></constructor-arg>
		<constructor-arg value="麻子" index="0"></constructor-arg>
	</bean>
           

構造注入案例2

package com.obtk.biz;

import com.obtk.dao.IUserDao;
import com.obtk.entitys.UserEntity;

public class UserBiz {
	private IUserDao userDao;
	
	public UserBiz() {
	}
	
    //用于構造注入
	public UserBiz(IUserDao userDao){
		this.userDao=userDao;
	}
	
	public void setUserDao(IUserDao userDao) {
		this.userDao = userDao;
	}
	
	public int saveUser(UserEntity theUser) {
		return userDao.saveUser(theUser);
	}
	
}
           

配置

<bean id="userDao" class="com.obtk.dao.UserDaoImpl"></bean>
	<bean id="userBiz" class="com.obtk.biz.UserBiz">
		<constructor-arg ref="userDao"></constructor-arg>
	</bean>
           

構造注入注意事項

(1) 一個<constructor-arg>元素表示構造方法的一個參數,且使用時不區分順序。當構造方法的參數出現混淆,無法區分時,可以通過<constructor-arg>元素的index屬性指

定該參數的位置索引,位置從0開始。<construc tor-arg>元素還提供了type屬性用來指定參數的類型,避免字元串和基本資料類型的混淆。

    (2)構造注入的時效性好,在對象執行個體化時就得到所依賴的對象,便于在對象的初始化方法中使用依賴對象;但受限于方法重載的形式,使用靈活性不足。設值注入使用靈活,但時效性不足,并且大量的setter通路器增加了類的複雜性。Spring并不傾向于某種注入方式,學生應該根據實際情況進行合理的選擇。

    當然Spring提供的注入方式不隻這兩種,隻是這兩種方式用得最普遍,感興趣的話可以通過Spring的相關資料了解其他注入方式。

2.5   注入其它類型資料

<!-- local在目前配置檔案裡面搜尋,bean是在整個項目的配置檔案裡面搜尋-->
	<bean id="userBiz3" class="com.obtk.biz.UserBiz">
		<property name="userDao">
			<ref bean="userDao"/>
		</property>
	</bean>
	
	<!-- 其它資料類型注入 -->
	<bean id="stu1" class="com.obtk.entitys.StudentEntity">
		<property name="sno" value="1101"></property>
		<property name="sname" value="周星<>星"></property>
		<property name="age" value="12"></property>
		<property name="gender" value="男"></property>
		<property name="score" value="93.3"></property>
		<property name="hobbyList">
			<list>
				<value>爬山</value>
				<value>遊泳</value>
				<value>打球</value>
			</list>
		</property>
		<property name="contryMap">
			<map>
			 <entry> <key><value>cn</value> </key> 
			         <value>中華人民共和國</value> 
			 </entry>
			 <entry> <key><value>us</value> </key> 
			         <value>美利堅合衆國</value> 
			 </entry>
			 <entry> <key><value>en</value> </key> 
			         <value>英格蘭</value> 
			 </entry>
			</map>
		</property>
		<property name="props">
			<props>
				<prop key="cn">中華人民共和國</prop>
				<prop key="us">美利堅合衆國</prop>
			</props>
		</property>
	</bean>
           

在XML中有5個預定義的實體引用,如表所示。

符  号 實體引用 符  号 實體引用
&lt; &apos;
&gt; &quot;
& &amp;

             嚴格地講,在XML中僅有字元“<”和“&”是非法的,其他3個符号是合法的,但是把它們替換為實體引用是個好習慣。

如果一個Bean元件僅在一處需要使用,可以把它定義為内部Bean。關鍵代碼如下示例所示。

<!-- 定義StudentBiz對象,并指定id為studentBiz -->
	<bean id="studentBiz" class="biz.impl.StudentBiz">
		<!-- 為studentBiz的dao屬性指派,需要注意的是,這裡要調用setDao()方法 -->
		<property name="studentDao">
			<!-- 定義StudentDao -->
			<bean class="dao.impl.StudentDao" />
		</property>
</bean>
           

這樣,這個UserDao類型的Bean就隻能被studentBiz使用,無法被其他的Bean引用。