Spring精講課程
Spring4 框架技術講義
第1章 Spring 概述
1.1 為什麼使用 Spring?
Spring 是一個框架,是一個半成品的軟體。有 20 個模組組成。它是一個容器管理物件,
物件之間的依賴關係。
(1) 輕量
Spring 框架使用的 jar 都比較小,一般在 1M 以下或者幾百 kb。Spring 核心功能的所需
的 jar 總共在 3M 左右。
Spring 框架執行佔用的資源少,執行效率高。不依賴其他 jar
(2) 針對介面程式設計,解耦合
Spring 提供了 Ioc 控制反轉,由容器管理物件,物件的依賴關係。原來在程式程式碼中的
物件建立方式,現在由容器完成。物件之間的依賴解耦合。
(3) AOP 程式設計的支援
通過 Spring 提供的 AOP 功能,方便進行面向切面的程式設計,許多不容易用傳統 OOP 實現
的功能可以通過 AOP 輕鬆應付
在 Spring 中,開發人員可以從繁雜的事務管理程式碼中解脫出來,通過宣告式方式靈活地
進行事務的管理,提高開發效率和質量。
(4) 方便整合各種優秀框架
Spring 不排斥各種優秀的開源框架,相反 Spring 可以降低各種框架的使用難度,Spring
提供了對各種優秀框架(如 Struts,Hibernate、MyBatis)等的直接支援。簡化框架的使用。
(5) 非侵入式
所謂非侵入式是指,Spring 框架的 API 不會在業務邏輯上出現,由於業務邏輯中沒有Spring 的 API,所以業務邏輯可以從 Spring 框架快速的移植到其他框架,即與環境無關。
(6) 容器
Spring 作為一個容器,可以管理物件的生命週期、物件與物件之間的依賴關係。可以通
過配置檔案,來定義物件,以及設定與其他物件的依賴關係。
1.2 Spring 框架是什麼(掌握)
Spring 是於 2003 年興起的一個輕量級的 Java 開發框架,它是為了解決企業應用開發的複雜性而建立的。Spring 的核心是控制反轉(IoC)和麵向切面程式設計(AOP)。簡單來說,Spring 是一個分層的 Java SE/EE 輕量級開源框架 。
Spring 的主要作用就是為程式碼“解耦”,降低程式碼間的耦合度。就是讓物件和物件(模組和模組)之間關係不是使用程式碼關聯,而是通過配置來說明。即在 Spring 中說明物件(模組)的關係。
Spring 根據程式碼的功能特點,使用 Ioc 降低業務物件之間耦合度。IoC 使得主業務在相互呼叫過程中,不用再自己維護關係了,即不用再自己建立要使用的物件了。而是由 Spring容器統一管理,自動“注入”,注入即賦值。 而 AOP 使得系統級服務得到了最大複用,且不用再由程式設計師手工將系統級服務“混雜”到主業務邏輯中了,而是由 Spring 容器統一完成“織入”。
1.3 Spring 體系結構(瞭解)
Spring 由 20 多個模組組成,它們可以分為資料訪問/整合(Data Access/Integration)、Web、面向切面程式設計(AOP, Aspects)、應用伺服器裝置管理(Instrumentation)、訊息傳送(Messaging)、核心容器(Core Container)和測試(Test)。
1.5 Spring4 框架解壓目錄及 Jar 包說明(瞭解)
第2章 IoC 控制反轉
控制反轉(IoC,Inversion of Control),是一個概念,是一種思想。指將傳統上由程式程式碼直接操控的物件呼叫權交給容器,通過容器來實現物件的裝配和管理。控制反轉就是對物件控制權的轉移,從程式程式碼本身反轉到了外部容器。通過容器實現物件的裝配和管理。
IoC 是一個概念,是一種思想,其實現方式多種多樣。當前比較流行的實現方式是依賴注入。應用廣泛。
依賴:classA 類中含有 classB 的例項,在 classA 中呼叫 classB 的方法完成功能,即 classA對 classB 有依賴。
Ioc 的實現:
1> 依賴查詢:DL ( Dependency Lookup ), 容器提供回撥介面和上下文環境給元件。
2>依賴注入:DI (Dependency Injection),程式程式碼不做定位查詢,這些工作由容器自行完成。
依賴注入 DI 是指程式執行過程中,若需要呼叫另一個物件協助時,無須在程式碼中建立被呼叫者,而是依賴於外部容器,由外部容器建立後傳遞給程式。
Spring 的依賴注入對呼叫者與被呼叫者幾乎沒有任何要求,完全支援 POJO 之間依賴關係的管理。
依賴注入是目前最優秀的解耦方式。依賴注入讓 Spring 的 Bean 之間以配置檔案的方式組織在一起,而不是以硬編碼的方式耦合在一起的。
2.1 Spring 的第一個程式(掌握)
在普通三層架構的基礎上,將程式修改為 Spring 框架程式
舉例:01-primay
2.1.1 匯入 Jar 包
首先,匯入 Spring 程式開發的四個基本 jar 包。
其次,匯入日誌相關的 Jar 包。commons.logging.jar 該檔案只是日誌記錄的實現規範,並沒有具體的實現(相當於日誌操作的介面定義)。
這裡日誌的實現使用 log4j,故還需要 log4j.jar。
最後,匯入 JUnit 測試 Jar 包 junit-4.9.jar。
Spring 基本程式設計,共需 7 個 Jar 包即可。
定義介面與實體類
2.1.3 建立 Spring 配置檔案
Spring 配置檔案的檔名可以隨意,但 Spring 建議的名稱為 applicationContext.xml。檔案約束在%SPRING_HOME%\docs\spring-framework-reference\html\xsd-configuration.html 檔案中。
注意,Spring 配置檔案中使用的約束檔案為 xsd 檔案。若 Eclipse 中沒有自動提示功能,則需要將約束要查詢的域名地址指向本地的 xsd 檔案。相應的 xsd 檔案在 Spring 框架解壓目錄下的 schema 目錄的相應子目錄中。
約束檔案:xsd(xml schema definition) xml 文件結構定義。
作用:驗證 xml 文件的邏輯結構是否正確。
1.定義一個 xml 文件中都有什麼元素
2.定義一個 xml 文件中都有什麼屬性
3.定義一個 xml 文件中元素可以有哪些子元素,以及元素的順序。
4.定義一個 xml 文件中元素和屬性的資料型別。
這裡需要的是 spring-beans.xsd 約束檔案,故需要在 beans 子目錄中查詢相應版本的約束檔案。
:用於定義一個例項物件。一個例項對應一個 bean 元素。
id:該屬性是 Bean 例項的唯一標識,程式通過 id 屬性訪問 Bean,Bean 與 Bean 間的依賴關係也是通過 id 屬性關聯的。
class:指定該 Bean 所屬的類,注意這裡只能是類,不能是介面。
2.1.4 配置不能聯網,本機的約束檔案
先拷貝約束檔案的 url 地址, 然後點選 Eclipse 的 windows 選單下面的 Perferences
2.1.5 定義測試類
2.1.6 容器介面和實現類
(1) ApplicationContext 介面(容器)
ApplicationContext 用於載入 Spring 的配置檔案,在程式中充當“容器”的角色。其實現類有兩個。通過 Ctrl +T 檢視:
A、配置檔案在類路徑下
若 Spring 配置檔案存放在專案的類路徑下,則使用 ClassPathXmlApplicationContext 實現類進行載入。
B、 配置檔案在本地目錄中
若 Spring 配置檔案存放在本地磁碟目錄中,則使用 FileSystemXmlApplicationContext 實現類進行載入。
C、 配置檔案在專案根路徑下
若 Spring 配置檔案存放在專案的根路徑下,同樣使用 FileSystemXmlApplicationContext實現類進行載入。
下面是存放在專案根路徑下的情況,該配置檔案與 src 目錄同級,而非在 src 中。
D、ApplicationContext 容器中物件的裝配時機
ApplicationContext 容器,會在容器物件初始化時,將其中的所有物件一次性全部裝配好。以後程式碼中若要使用到這些物件,只需從記憶體中直接獲取即可。執行效率較高。但佔用記憶體。
2.2 Bean 的裝配
舉例:beanAssemble 專案
Bean 的裝配,即 Bean 物件的建立。容器根據程式碼要求建立 Bean 物件後再傳遞給程式碼的過程,稱為 Bean 的裝配。
2.2.1 預設裝配方式(掌握)
程式碼通過 getBean()方式從容器獲取指定的 Bean 例項,容器首先會呼叫 Bean 類的無參構造器,建立空值的例項物件。
2.2.2 容器中 Bean 的作用域(掌握)
當通過 Spring 容器建立一個 Bean 例項時,不僅可以完成 Bean 的例項化,還可以通過scope 屬性,為 Bean 指定特定的作用域。Spring 支援 5 種作用域。
(1)singleton:單態模式。即在整個 Spring 容器中,使用 singleton 定義的 Bean 將是單例的,只有一個例項。預設為單態的。
(2)prototype:原型模式。即每次使用 getBean 方法獲取的同一個的例項都是一個新的例項。
(3)request:對於每次 HTTP 請求,都將會產生一個不同的 Bean 例項。
(4)session:對於每個不同的 HTTP session,都將產生一個不同的 Bean 例項。
注意:
(1)對於 scope 的值 request、session 只有在 Web 應用中使用 Spring 時,該作用域才有效。
(2)對於 scope 為 singleton 的單例模式,該 Bean 是在容器被建立時即被裝配好了。
(3)對於 scope 為 prototype 的原型模式,Bean 例項是在程式碼中使用該 Bean 例項時才進行裝配的。
舉例:
2.2.3 定製 Bean 的生命始末(掌握)
可以為 Bean 定製初始化後的生命行為,也可以為 Bean 定製銷燬前的生命行為。
舉例:
首先,這些方法需要在 Bean 類中事先定義好:是方法名隨意的 public void 方法。
其次,在配置檔案的標籤中增加如下屬性:
init-method:指定初始化方法的方法名
destroy-method:指定銷燬方法的方法名
注意,若要看到 Bean 的 destroy-method 的執行結果,需要滿足兩個條件:
(1)Bean 為 singleton,即單例
(2)要確保容器關閉。介面 ApplicationContext 沒有 close()方法,但其實現類有。所以,可以將 ApplicationContext 強轉為其實現類物件,或直接建立的就是實現類物件。
2.3 基於 XML 的 DI
舉例:專案 di-xml
2.3.1 注入分類
bean 例項在呼叫無參構造器建立了空值物件後,就要對 bean 物件的屬性進行初始化。
初始化是由容器自動完成的,稱為注入。
根據注入方式的不同,常用的有兩類:設值注入、構造注入。
(1) 設值注入(掌握)
設值注入是指,通過 setter 方法傳入被呼叫者的例項。這種注入方式簡單、直觀,因而在 Spring 的依賴注入中大量使用
對於其它 Bean 物件的引用,除了標籤的 ref 屬性外,還可以使用標籤。
(2) 構造注入(理解)
構造注入是指,在構造呼叫者例項的同時,完成被呼叫者的例項化。即,使用構造器設定依賴關係。
舉例:標籤中用於指定引數的屬性有:
1> name:指定引數名稱。
2>index:指明該引數對應著構造器的第幾個引數,從 0 開始。不過,該屬性不要也行,
但要注意,若引數型別相同,或之間有包含關係,則需要保證賦值順序要與構造器中的引數
順序一致。
2.3.2 具有集合性質的屬性注入(掌握)
/**MyCollections **/
package com.bernode.ba04;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class MyCollections {
private String [] mystr;
private Set<String> myset;
private List<Student> mylist;
private Map<String,Integer> mymap;
// Properties也是key-value的結構, key-value都是String型別。
private Properties myprop;
//複雜的集合型別
private List<Map<String,String>> mylistmap;
public void setMylistmap(List<Map<String, String>> mylistmap) {
this.mylistmap = mylistmap;
}
public void setMystr(String[] mystr) {
this.mystr = mystr;
}
public void setMyset(Set<String> myset) {
this.myset = myset;
}
public void setMylist(List<Student> mylist) {
this.mylist = mylist;
}
public void setMymap(Map<String, Integer> mymap) {
this.mymap = mymap;
}
public void setMyprop(Properties myprop) {
this.myprop = myprop;
}
@Override
public String toString() {
return "MyCollections [mylistmap=" + mylistmap + "]";
}
}
/MyTest**/
package com. de.ba04;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MyTest {
/**
*/
@Test
public void test01(){
String configLocation=“com/bjpowernode/ba04/applicationContext.xml”; //類路徑的根目錄
ApplicationContext ctx = new ClassPathXmlApplicationContext(configLocation);
MyCollections coll = (MyCollections) ctx.getBean("myCollections");
System.out.println("coll:"+coll);
}
}
/***applicationContext.xml/
<?xml version="1.0" encoding="UTF-8"?> <!-- 給具有集性質的屬性賦值
什麼型別的屬性,就用什麼型別的子標籤,
集合中是簡單型別就用value, 是物件型別就用ref
-->
<bean id="myCollections" class="com.bjpowernode.ba04.MyCollections">
<!-- Array<String> -->
<property name="mystr">
<array>
<value>大興區</value>
<value>朝陽區</value>
</array>
</property>
<!-- Set<String> -->
<property name="myset">
<set>
<value>北京</value>
<value>上海</value>
<value>杭州</value>
</set>
</property>
<!-- List<Student> -->
<property name="mylist">
<list>
<ref bean="myStudent"/>
<ref bean="myStudent1"/>
<ref bean="myStudent2"/>
</list>
</property>
<!-- Map<String,Integer> -->
<property name="mymap">
<map>
<entry key="weight" value="80" /> <!-- key-value -->
<entry key="height" value="180" />
</map>
</property>
<!-- Properties -->
<property name="myprop">
<props>
<prop key="tel">010-14678979</prop> <!-- key-value -->
<prop key="phone">1234578978</prop>
</props>
</property>
<!-- List<Map<String,String>> -->
<property name="mylistmap">
<list>
<map> <!-- 0 -->
<entry key="weight" value="80kg" />
<entry key="height" value="180cm" />
</map>
<map> <!-- 1 -->
<entry key="tel" value="1534879" />
<entry key="phone" value="1649879" />
</map>
</list>
</property>
</bean>
/********************************************************/
2.3.3 對於引用型別屬性的自動注入
對於引用型別屬性的注入,也可不在配置檔案中顯示的注入。可以通過為標籤
設定 autowire 屬性值,為引用型別屬性進行隱式自動注入(預設是不自動注入引用型別屬
性)。根據自動注入判斷標準的不同,可以分為兩種:
byName:根據名稱自動注入
byType:根據型別自動注入
(1) byName 方式自動注入(理解)
當配置檔案中被呼叫者 bean 的 id 值與程式碼中呼叫者 bean 類的屬性名相同時,可使用byName 方式,讓容器自動將被呼叫者 bean 注入給呼叫者 bean。容器是通過呼叫者的 bean類的屬性名與配置檔案的被呼叫者 bean 的 id 進行比較而實現自動注入的。
舉例:
(2) byType 方式自動注入(理解)
使用 byType 方式自動注入,要求:配置檔案中被呼叫者 bean 的 class 屬性指定的類,要與程式碼中呼叫者 bean 類的某引用型別屬性型別同源。即要麼相同,要麼有 is-a 關係(子類,或是實現類)。但這樣的同源的被呼叫 bean 只能有一個。多於一個,容器就不知該匹配哪一個了。
舉例:
2.3.4 為應用指定多個 Spring 配置檔案(掌握)
在實際應用裡,隨著應用規模的增加,系統中 Bean 數量也大量增加,導致配置檔案變得非常龐大、臃腫。為了避免這種情況的產生,提高配置檔案的可讀性與可維護性,可以將Spring 配置檔案分解成多個配置檔案。
(1) 平等關係的配置檔案(掌握)
將配置檔案分解為地位平等的多個配置檔案,並將所有配置檔案的路徑定義為一個String 陣列,將其作為容器初始化引數出現。其將與可變參的容器構造器匹配。
各配置檔案間為並列關係,不分主次。
舉例:
(2) 包含關係的配置檔案(掌握)
各配置檔案中有一個總檔案,總配置檔案將各其它子檔案通過引入。在 Java程式碼中只需要使用總配置檔案對容器進行初始化即可。
舉例:
也可使用萬用字元*。但,此時要求父配置檔名不能滿足所能匹配的格式,否則將出現迴圈遞迴包含。就本例而言,父配置檔案不能匹配 spring-.xml 的格式,即不能起名為spring-total.xml。
2.4 基於註解的 DI
舉例:di-annotation 專案
對於 DI 使用註解,將不再需要在 Spring 配置檔案中宣告 bean 例項。Spring 中使用註解,需要在原有 Spring 執行環境基礎上再做一些改變,完成以下三個步驟。
(1)匯入 AOP 的 Jar 包。因為註解的後臺實現用到了 AOP 程式設計。
(3)需要在 Spring 配置檔案中配置元件掃描器,用於在指定的基本包中掃描註解。
3-1)使用多個 context:component-scan 指定不同的包路徑
3-2)指定 base-package 的值使用分隔符
分隔符可以使用逗號(,)分號(;)還可以使用空格,不建議使用空格。
逗號分隔:
3-3)base-package 是指定到父包名
base-package 的值表是基本包,容器啟動會掃描包及其子包中的註解,當然也會掃描到子包下級的子包。所以 base-package 可以指定一個父包就可以。
2.4.1 定義 Bean 的註解@Component(掌握)
需要在類上使用註解@Component,該註解的 value 屬性用於指定該 bean 的 id 值。
舉例:di01
另外,Spring 還提供了 3 個功能基本和@Component 等效的註解:
1> @Repository 用於對 DAO 實現類進行註解
2> @Service 用於對 Service 實現類進行註解
3> @Controller 用於對 Controller 實現類進行註解
之所以建立這三個功能與@Component 等效的註解,是為了以後對其進行功能上的擴充套件。
@Component 不指定 value 屬性,bean 的 id 是類名的首字母小寫。
2.4.2 簡單型別屬性注入@Value(掌握)
需要在屬性上使用註解@Value,該註解的 value 屬性用於指定要注入的值。
使用該註解完成屬性注入時,類中無需 setter。當然,若屬性有 setter,則也可將其加到 setter 上。
舉例:2.4.3 byType 自動注入@Autowired(掌握)
需要在引用屬性上使用註解@Autowired,該註解預設使用按型別自動裝配 Bean 的方式。
使用該註解完成屬性注入時,類中無需 setter。當然,若屬性有 setter,則也可將其加到 setter 上。
舉例:
2.4.4 byName 自動注入@Autowired 與@Qualifier(掌握)
需要在引用屬性上聯合使用註解@Autowired 與@Qualifier。@Qualifier 的 value 屬性用於指定要匹配的 Bean 的 id 值。同樣類中無需 setter,也可加到 setter 上。
舉例:
@Autowired 還有一個屬性 required,預設值為 true,表示當匹配失敗後,會終止程式運
行。若將其值設定為 false,則匹配失敗,將被忽略,未匹配的屬性值為 null。
2.4.5 JDK 註解@Resource 自動注入 (掌握)
Spring提供了對 jdk中@Resource註解的支援。@Resource 註解既可以按名稱匹配Bean,也可以按型別匹配 Bean。預設是按名稱注入。使用該註解,要求 JDK 必須是 6 及以上版本。
@Resource 可在屬性上,也可在 set 方法上。
(1) byType 注入引用型別屬性
@Resource 註解若不帶任何引數,採用預設按名稱的方式注入,按名稱不能注入 bean,則會按照型別進行 Bean 的匹配注入。
舉例:
(2) byName 注入引用型別屬性
@Resource 註解指定其 name 屬性,則 name 的值即為按照名稱進行匹配的 Bean 的 id。
2.4.6 Bean 的生命始末@PostConstruct 與@PreDestroy(掌握)
在方法上使用@PostConstruct 與原來的 init-method 等效。在方法上使用@PreDestroy,與 destroy-method 等效。
舉例:
2.4.7 註解與 XML 的對比
註解的好處是,配置方便,直觀。但其弊端也顯而易見:以硬編碼的方式寫入到了 Java
程式碼中,其修改是需要重新編譯程式碼的。
XML 配置方式的最大好處是,對其所做修改,無需編譯程式碼,只需重啟伺服器即可將新
的配置載入。
註解的特點是高效(程式碼少,沒有配置檔案的書寫那麼複雜),XML 配置方式的特點是
靈活(無需修改原始碼),
再有如果要使用的類不是自己編寫的,沒有原始碼。那也使用不了@Compoent 等註解,
此時使用 XML 進行配置 bean.