Spring框架(一)
Spring:
Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由 Rod Johnson在其著作 Expert One-On-One J2EE Development and Design 中闡述的部分理念和原型衍生而來。它是為了解決企業應用開發的複雜性而建立的。框架的主要優勢之一就是其分層架構,分層架構允許使用者選擇使用哪一個元件,同時為 J2EE 應用程式開發提供整合的框架。Spring 使用基本的 JavaBean來完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不僅限於伺服器端的開發。從簡單性、可測試性和鬆耦合的角度而言,任何 Java 應用都可以從 Spring 中受益。Spring 的核心是控制反轉(IoC)和麵向切面(AOP)。簡單來說,Spring 是一個分層的 JavaSE/EEfull-stack( 一站式) 輕量級開源框架。
JavaEE 開發分成三層結構:
* WEB 層:Spring MVC. * 業務層:Bean 管理:(IOC) * 持久層:Spring 的 JDBC 模板.ORM 模板用於整合其他的持久層框架.
Spring的優勢:
方便解耦,簡化開發
Spring 就是一個大工廠,可以將所有物件建立和依賴關係維護,交給 Spring 管理
AOP 程式設計的支援
Spring 提供面向切面程式設計,可以方便的實現對程式進行許可權攔截、執行監控等功能
宣告式事務的支援
只需要通過配置就可以完成對事務的管理,而無需手動程式設計
方便程式的測試
Spring 對 Junit4 支援,可以通過註解方便的測試 Spring 程式
方便整合各種優秀框架
Spring 不排斥各種優秀的開源框架,其內部提供了對各種優秀框架(如:Struts、Hibernate、MyBatis、Quartz 等)的直接支援
降低 JavaEE API 的使用難度
Spring 對 JavaEE 開發中非常難用的一些 API(JDBC、JavaMail、遠端呼叫等),都提供了封裝,使這些 API 應用難度大大降低
SpringIOC底層實現原理:
相關概念:
依賴注入和 控制反轉
IOC inversion of control 控制反轉
DI Dependency Injection 依賴注入
IoC是什麼 Ioc—Inversion of Control,即“控制反轉”,不是什麼技術,而是一種設計思想。在Java開發中,Ioc意味著將你設計好的物件交給容器控制,而不是傳統的在你的物件內部直接控制。如何理解好Ioc呢?理解好Ioc的關鍵是要明確“誰控制誰,控制什麼,為何是反轉(有反轉就應該有正轉了),哪些方面反轉了”,那我們來深入分析一下: ●誰控制誰,控制什麼:傳統Java SE程式設計,我們直接在物件內部通過new進行建立物件,是程式主動去建立依賴物件;而IoC是有專門一個容器來建立這些物件,即由Ioc容器來控制物件的建立;誰控制誰?當然是IoC 容器控制了物件;控制什麼?那就是主要控制了外部資源獲取(不只是物件包括比如檔案等)。 ●為何是反轉,哪些方面反轉了:有反轉就有正轉,傳統應用程式是由我們自己在物件中主動控制去直接獲取依賴物件,也就是正轉;而反轉則是由容器來幫忙建立及注入依賴物件;為何是反轉?因為由容器幫我們查詢及注入依賴物件,物件只是被動的接受依賴物件,所以是反轉;哪些方面反轉了?依賴物件的獲取被反轉了。 用圖例說明一下,傳統程式設計如圖,都是主動去建立相關物件然後再組合起來:
傳統應用程式示意圖 當有了IoC/DI的容器後,在客戶端類中不再主動去建立這些物件了 IoC能做什麼 IoC不是一種技術,只是一種思想,一個重要的面向物件程式設計的法則,它能指導我們如何設計出鬆耦合、更優良的程式。傳統應用程式都是由我們在類內部主動建立依賴物件,從而導致類與類之間高耦合,難於測試;有了IoC容器後,把建立和查詢依賴物件的控制權交給了容器,由容器進行注入組合物件,所以物件與物件之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程式的整個體系結構變得非常靈活。 其實IoC對程式設計帶來的最大改變不是從程式碼上,而是從思想上,發生了“主從換位”的變化。應用程式原本是老大,要獲取什麼資源都是主動出擊,但是在IoC/DI思想中,應用程式就變成被動的了,被動的等待IoC容器來建立並注入它所需要的資源了。 IoC很好的體現了面向物件設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC容器幫物件找相應的依賴物件並注入,而不是由物件主動去找。 IoC和DI DI—Dependency Injection,即“依賴注入”:是元件之間依賴關係由容器在執行期決定,形象的說,即由容器動態的將某個依賴關係注入到元件之中。依賴注入的目的並非為軟體系統帶來更多功能,而是為了提升元件重用的頻率,併為系統搭建一個靈活、可擴充套件的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何程式碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。 理解DI的關鍵是:“誰依賴誰,為什麼需要依賴,誰注入誰,注入了什麼”,那我們來深入分析一下: ●誰依賴於誰:當然是應用程式依賴於IoC容器; ●為什麼需要依賴:應用程式需要IoC容器來提供物件需要的外部資源; ●誰注入誰:很明顯是IoC容器注入應用程式某個物件,應用程式依賴的物件; ●注入了什麼:就是注入某個物件所需要的外部資源(包括物件、資源、常量資料)。 IoC和DI由什麼關係呢?其實它們是同一個概念的不同角度描述,由於控制反轉概念比較含糊(可能只是理解為容器控制物件這一個層面,很難讓人想到誰來維護物件關係),所以2004年大師級人物Martin Fowler又給出了一個新的名字:“依賴注入”,相對IoC 而言,“依賴注入”明確描述了“被注入物件依賴IoC容器配置依賴物件”。
面向物件五大原則:
單一職責原則(SRP) 開放封閉原則(OCP) 里氏替換原則(LSP) 依賴倒置原則(DIP) 介面隔離原則(ISP) 單一職責原則(SRP) • 一個類應該僅有一個引起它變化的原因(最簡單,最容易理解卻最不容易做到的一個設計原則) 職員類例子: 比如在職員類裡,將工程師、銷售人員、銷售經理這些情況都放在職員類裡考慮,其結果將會非常混亂,在這個假設下,職員類裡的每個方法都要if else判斷是哪種情況,從類結構上來說將會十分臃腫,並且上述三種的職員型別,不論哪一種發生需求變化,都會改變職員類!這個是大家所不願意看到的! 開放封閉原則(OCP) • 既開放又封閉,對擴充套件是開放的,對更改是封閉的! • 擴充套件即擴充套件現行的模組,當我們軟體的實際應用發生改變時,出現新的需求,就需要我們對模組進行擴充套件,使其能夠滿足新的需求! 更改封閉即是在我們對模組進行擴充套件時,勿需對源有程式程式碼和DLL進行修改或重新編譯檔案! 這個原則對我們在設計類的時候很有幫助,堅持這個原則就必須儘量考慮介面封裝,抽象機制和多型技術! 里氏替換原則(LSP) • 子類可以替換父類並且出現在父類能夠出現的任何地方 • 這個原則也是在貫徹GOF倡導的面向介面程式設計! 在這個原則中父類應儘可能使用介面或者抽象類來實現! 子類通過實現了父類介面,能夠替父類的使用地方! 通過這個原則,我們客戶端在使用父類介面的時候,通過子類實現! 意思就是說我們依賴父類介面,在客戶端宣告一個父類介面,通過其子類來實現 這個時候就要求子類必須能夠替換父類所出現的任何地方,這樣做的好處就是,在根據新要求擴充套件父類介面的新子類的時候而不影響當前客戶端的使用! 依賴倒置原則(DIP) • 傳統的結構化程式設計中,最上層的模組通常都要依賴下面的子模組來實現,也 稱為高層依賴低層! 所以DIP原則就是要逆轉這種依賴關係,讓高層模組不要依賴低層模組,所以稱之為依賴倒置原則! ISP 介面隔離原則 • 這個原則的意思是:使用多個專門的介面比使用單個介面要好的多! 這個我有體會,在我實際程式設計中,為了減少介面的定義,將許多類似的方法都放在一個介面中,最後發現,維護和實現介面的時候花了太多精力,而介面所定義的操作相當於對客戶端的一種承諾,這種承諾當然是越少越好,越精練越好,過多的承諾帶來的就是你的大量精力和時間去維護!
反射機制:
1, 通過spring來獲取一個物件的例項 2, 通過spring進行屬性注入 setter方法注入 構造器注入 介面注入 p標記的使用 <bean p:username=""></bean> 3, 將一個物件注入到另一個物件<ref bean="..."> 4, AutoWired(byType, byName) 5, scope, lazy-init, init-method, destroy-method(相當的不重要) scope="singleton(單例) / prototype(原型)" lazy-init="true" // 延遲載入 ___init-method="" destory-method=""(不要和prototype一起使用) autowire
例子:
下載並引入相關jar包:
結構:
配置相關檔案:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5
6 <bean id="user" class="com.model.User">
7 <!-- 通過Spring給類注入屬性,通過空參構造方法 -->
8 <property name="username">
9 <value>hanqi</value>
10 </property>
11 <property name="password">
12 <value>123</value>
13 </property>
14
15 <!--
16 另一種通過帶參構造方法
17 <constructor-arg index="0" value="name1"></constructor-arg>
18 <constructor-arg index="1" value="pwd1"></constructor-arg>
19 -->
20 </bean>
21
22 </beans>
model包:
1 package com.model;
2
3 public class User {
4 private String username;
5 private String password;
6
7
8
9 public User() {
10 super();
11 System.out.println("呼叫User類的空參構造方法");
12 // TODO Auto-generated constructor stub
13 }
14 public User(String username, String password) {
15 super();
16 this.username = username;
17 this.password = password;
18 System.out.println("呼叫User類的帶參構造方法 ");
19 }
20 public String getUsername() {
21 return username;
22 }
23 public void setUsername(String username) {
24 this.username = username;
25 }
26 public String getPassword() {
27 return password;
28 }
29 public void setPassword(String password) {
30 this.password = password;
31 }
32 @Override
33 public String toString() {
34 return "User [username=" + username + ", password=" + password + "]";
35 }
36
37
38
39 }
測試1:
1 package com.test;
2
3 import org.springframework.context.ApplicationContext;
4 import org.springframework.context.support.ClassPathXmlApplicationContext;
5
6 import com.model.User;
7
8 public class Test {
9 public static void main(String[] args) {
10 ApplicationContext ac=new ClassPathXmlApplicationContext("Spring-all.xml");//呼叫此方法時類已經例項化好了
11 User u=(User)ac.getBean("user");//拿到例項化的類,帶有預設的值
12
13 System.out.println(u);
14 ((ClassPathXmlApplicationContext)ac).close();
15
16 }
17 }
測試2:
1 package com.test;
2
3 import org.springframework.context.ApplicationContext;
4 import org.springframework.context.support.ClassPathXmlApplicationContext;
5
6 import com.model.User;
7
8 public class Test {
9 public static void main(String[] args) {
10 ApplicationContext ac=new ClassPathXmlApplicationContext("Spring-all.xml");//呼叫此方法時類已經例項化好了
11
12 User u=(User)ac.getBean("user");//拿到例項化的類
13
14 u.setUsername("u");
15 u.setPassword("p");
16 System.out.println(u);
17 ((ClassPathXmlApplicationContext)ac).close();
18
19 }
20 }
測試3:
1 package com.test;
2
3 import java.lang.reflect.Method;
4
5 import com.model.User;
6
7 public class Test2 {
8 public static void main(String[] args) {
9 try {
10 //例項化一個類,<?>代表可以是任何型別
11 Class<?> cla=Class.forName("com.model.User");
12 //cla.getConstructor(parameterTypes);獲取類裡的構造方法
13 //cla.getFields();//獲取類裡的成員變數
14
15 //獲得物件例項
16 User u=(User)cla.newInstance();
17 //u.setUsername("直接呼叫set設定的");
18
19 //獲取類裡的所有方法
20 Method[] me=cla.getMethods();
21
22 Object obj=null;
23 Object obj1=null;
24
25 for(Method m:me){
26 //找到方法,回撥方法反射
27 if("setUsername".equals(m.getName())){
28 obj=m.invoke(u, "回撥反射");
29 }
30 }
31 for(Method m:me){
32 if("getUsername".equals(m.getName())){
33 obj1=m.invoke(u);
34 }
35 }
36 //set方法沒有返回值,列印null
37 System.out.println("setUsername方法的返回值:"+obj);
38 //直接列印物件,屬性null
39 System.out.println("物件:"+u);
40 //
41 System.out.println("getUsername方法的返回值:"+obj1);
42
43 } catch (Exception e) {
44 // TODO Auto-generated catch block
45 e.printStackTrace();
46 }
47 }
48 }