Spring框架系列(一)-整體架構

附上示例程序的github地址:https://github.com/bjtudujunlin/SpringDataExample


一、Spring設計目標

Spring設計的初衷在於提供一套輕量級的應用開發框架,解決開發者在應用開發中的共性問題。這句話有兩個關鍵字,一是“輕量級”,二是“共性問題”。爲啥叫“輕量級”呢,Spring框架本身不能給你解決業務問題,也沒有相關庫,只是提供了一個框架,讓你的程序更加健壯、易於維護,你看到的Spring MVC、Spring Data之類的都是基於Spring框架的組件,不屬於Spring框架內容。那麼“共性問題”有啥呢,耦合,還是耦合,日常程序開發中,有兩類耦合問題經常遇見,一是類與類關係的耦合,軟件開發中,類與類的“組合”關係普遍存在,比如B類有屬性B1是A類的實例,在B中需要對B1進行初始化操作,Spring中的IOC容器提供了配置文件的方式對B類進行管理,包括初始化操作,配置屬性B1的值等等。二是服務與服務關係的耦合,比如現在有一個數據庫操作類C,現在我們想爲這個類加上權限檢測的功能,只有具有特定權限的人羣才能調用數據庫操作類的方法,一般處理是在類C中調用權限檢測方法,這樣就導致了服務之間的耦合,違背了“單一職責原則”,Spring提供了AOP方法,以非侵入式的方式解決這種耦合問題,保持了服務的獨立性,又滿足了業務的需求。

二、Spring整體架構

Spring整體架構如下圖所示,核心模塊是Spring IOC和Spring AOP模塊,其它的模塊都是基於這兩個核心模塊進行開發,然後以組件的方式集成進來的。這裏先重點介紹核心的IOC和AOP模塊,其它組件留在後面的組件環節進行詳細介紹。


圖 1

 

三、Spring核心模塊

1.       IOC模塊

IOC模塊其實是一個IOC容器,提供了對Bean進行管理的功能,依賴於這個功能,解決了類與類關係的耦合。下圖比較適合解釋IOC容器。由於引進了中間位置的“第三方”,也就是IOC容器,使得A、B、C、D這4個對象沒有了耦合關係,齒輪之間的傳動全部依靠“第三方”了,全部對象的控制權全部上繳給“第三方”IOC容器,所以,IOC容器成了整個系統的關鍵核心,它起到了一種類似“粘合劑”的作用,把系統中的所有對象粘合在一起發揮作用,如果沒有這個“粘合劑”,對象與對象之間會彼此失去聯繫,這就是有人把IOC容器比喻成“粘合劑”的由來。


圖 2

 

 

有了前面的基礎,我們再來看看IOC(Inversion of Control)——控制反轉的意思。爲啥叫控制反轉呢?因爲它涉及到控制權在誰手裏的問題,舉個例子,引入IOC容器之前,對象A依賴於對象B,那麼對象A在初始化或者運行到某一點的時候,自己必須主動去創建對象B或者使用已經創建的對象B。無論是創建還是使用對象B,控制權都在A自己手上。那麼引入IOC容器之後呢,對象A與對象B之間失去了直接聯繫,所以,當對象A運行到需要對象B的時候,IOC容器會主動創建一個對象B注入到對象A需要的地方。可以看見創建對象B的控制權由A轉移到了IOC容器,控制權反轉了。

還有一個平時容易混淆的概念DI(Dependence Injection)——依賴注入,這個和IOC有什麼關係呢。前面我們說了IOC容器會主動創建一個對象B注入到對象A需要的地方,DI其實就是IOC實現控制反轉的方法。

下面舉一個IOC具體的例子,這裏先採用XML的方式,這種方式比較複雜,但是能更好的理解spring的IOC配置,後續會補充java配置方式。

a)      建立maven的java工程,並添加spring-core的依賴

    <dependency>

      <groupId>org.springframework</groupId>

      <artifactId>spring-core</artifactId>

      <version>4.3.3.RELEASE</version>

</dependency>

b)      建立Person類

public class Person {

    private Stringname;

    private Stringage;

    private Regionregion;

    public String getName() {

       returnname;

    }

 

    public void setName(String name) {

       this.name =name;

    }

 

    public String getAge() {

       returnage;

    }

 

    public void setAge(String age) {

       this.age =age;

    }

 

    private Stringdiscribe;

   

    public Person(Regionregion,Stringdiscribe){

       this.region =region;

       this.discribe =discribe;

    }

   

    public void Introduce(){

       System.out.println("I am " +name +",I comefrom " +region +"my age is" +age);

       System.out.println(discribe);

    }

}

c)       建立Region類

public class Region {

    private Stringprovince;

    private Stringcity;

   

    public StringgetProvince() {

       returnprovince;

    }

 

    public void setProvince(String province) {

       this.province =province;

    }

 

    public String getCity() {

       returncity;

    }

 

    public void setCity(String city) {

       this.city =city;

    }

 

    @Override

    public String toString(){

       returnprovince +" " +city;

    }

}

 

d)      在resource下面新建spring的xml配置文件spring-config.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="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.xsd

           http://www.springframework.org/schema/aop/spring-aop.xsd

           http://www.springframework.org/schema/context">

<!-- 配置細節 -->

 

<!-- bean如果沒有指定ID,會生成默認ID,形式爲類名#0”0是計數 -->

<beanid="person"class="iscas.springstudy.Person">

<!--     配置構造函數,按照配置函數從左到右的順序,ref標籤表示參數爲指定idbeanvalue標籤表示參數值 -->

    <constructor-argref="region-a"></constructor-arg>

    <constructor-argvalue="a funny man"></constructor-arg>

<!--    配置屬性,name對應屬性名,value對應屬性值,屬性中也可以右ref標籤,跟構造函數是一樣的意思

        需要注意的是配置屬性要求該屬性必須右getter方法和setter方法-->

    <propertyname="name"value="liming"></property>

    <propertyname="age"value="19"></property>

</bean>

 

<beanid="region-a"class="iscas.springstudy.Region">

    <propertyname="province"value="beijing"></property>

    <propertyname="city"value="haidian"></property>

</bean>

</beans>

e)      加載xml文件,生成beanfactory,獲取bean,並調用其中方法

XmlBeanFactory factory =newXmlBeanFactory(newClassPathResource("spring-config.xml"));

        Person person = (Person)factory.getBean("person");

       

person.Introduce();

f)        applicationcontext方式加載xml文件

beanfactory類現在已經過期了,applicationcontext類繼承自beanfactory,能夠提供比beanfactory更多的支持,所以在看看applicationcontext怎麼調用。首先需要在maven中添加applicationcontext依賴。

        <dependency>

            <groupId>org.springframework</groupId>

            <artifactId>spring-context</artifactId>

              <version>4.3.3.RELEASE</version>

    </dependency>

代碼中用applicationcontext取代beanfactory即可,改變如下

ApplicationContext application =newClassPathXmlApplicationContext("spring-config.xml");

       

        Person person = (Person)application.getBean("person");

       

person.Introduce();

2.       AOP模塊

在軟件開發中,散佈於應用中多處的功能被稱爲橫切關注點(cross-cutting concern)。通常來講,這些橫切關注點從概念上是與應用的業務邏輯相分離的(但是往往會直接嵌入到應用的業務邏輯之中)。把這些橫切關注點與業務邏輯相分離正是面向切面編程(AOP)所要解決的問題。下面這個圖可以加深理解。


圖 3

上圖中,CourseService、StudentService和MiscService都屬於業務服務,比如CourseService就是課程管理服務。現在需要爲CourseService服務加上安全校驗功能,傳統的做法是創建一個安全類,然後在CourseService服務中需要安全校驗的地方進行調用,這樣邏輯簡單,但是該方式是侵入式的,改變了CourseService服務本身的代碼,CourseService服務和安全類耦合起來,比如我以後將CourseService服務換到別的地方去用,要麼刪除安全功能,要麼把安全類也帶上。同樣的StudentService、MiscService都用到了同樣的安全類。這裏的安全類就是橫切關注點。要解決前面這個侵入式的問題,我們就需要引入AOP的思想,既滿足上述功能需求,又是非侵入式的。

理解AOP需要理解以下幾個概念:

a)      通知(Advice),切面必須要完成的工作。在AOP術語中,切面的工作被稱爲通知。Spring切面支持的最小粒度是方法,可以應用5種類型的通知:

前置通知(Before):在目標方法被調用之前調用通知功能;

後置通知(After):在目標方法完成之後調用通知,此時不會關心方法的輸出是什麼;

返回通知(After-returning):在目標方法成功執行之後調用通知;

異常通知(After-throwing):在目標方法拋出異常後調用通知;

環繞通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和調用之後執行自定義的行爲。

b)      連接點(Join point),連接點是在應用執行過程中能夠插入切面的一個點。比如Spring AOP只能支持到方法粒度,所以類型的方法可以作爲連接點,AOP可以在方法上插入切面,監聽方法的調用,並觸發通知。

c)       切點(Poincut),一個切面並不需要通知應用的所有連接點,選擇的連接點就是切點。連接點表示有這個能力,而切點就是已經選擇的連接點。

d)      切面(Aspect),切面是通知和切點的結合。通知和切點共同定義了切面的全部內容。

總結起來,AOP無非就幹了兩件事情,選擇連接點作爲切點,插入切面對切點進行監聽。當切點滿足通知條件時,通知觸發。

Spring AOP的基本概念到這兒解釋完了,考慮AOP內容還有些,放在下一章節結合示例程序敘述。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章