Spring學習手冊(11)—— SpringAOP實戰

Spring學習手冊(10)—— Spring AOP配置我們學習了XML方式配置Spring AOP的方式。我們學習了AOP幾本知識點,學會了配置切面、切點以及增強方法。本篇文章我們將以實戰的形式來鞏固所學知識點。

一、環境準備

由于Spring AOP重用了部分AspectJ項目的代碼,因此我們需要在項目中導入aspectjrt.jaraspectjweaver.jar兩個jar包。因此我們需要在我們的項目工程的build.gradle文件中添加如下配置信息:

dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.11'
    compile 'org.springframework:spring-context:4.3.7.RELEASE'    
    compile group: 'org.aspectj', name: 'aspectjrt', version: '1.8.9'
    compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.9'    
}

這樣IDEA會自動從maven倉庫下載依賴jar包并且引入到工程中。

提示:可能由于網絡問題,我配置完成依賴關系后,IDEA一直無法將依賴jar包引入到工程(雖然在Gradle的目錄里又jar包),各種關鍵詞查詢也沒有找到解決的辦法,最后將要放棄的時候它又莫名的好了。所以在國內開發者如果出現相似問題,建議配置國內的maven倉庫的地址。而Gradle的配置學習超出了文章范圍,讀者可自行查詢相關文章進行學習。

二、學生成績管理系統模擬實戰

為了更好的學習Spring AOP的代碼實戰,我們假設我們需要開發一個學生成績管理系統,該系統為學生提供查詢成績、查詢所修課程等服務,并且為教師提供查詢班級學生成績信息以及所授課班級學生信息。

2.1 領域模型

通過一系列分析,該系統中領域模型應包括:學生信息、課程信息、分數信息......因此我們先需要對該領域模型創建相應的類表示:

分數模型類

public class Score {

    /* 分數 */
    private int  score;

    /* 課程名稱 */
    private String courseName;

    /*課程ID*/
    private int courseId;

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public int getCourseId() {
        return courseId;
    }

    public void setCourseId(int courseId) {
        this.courseId = courseId;
    }
}

課程模型類

public class Course {

    //課程ID
    private int courseId;

    //課程名
    private String courseName;

    public int getCourseId() {
        return courseId;
    }

    public void setCourseId(int courseId) {
        this.courseId = courseId;
    }

    public String getCourseName() {
        return courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }
}

學生模型類

//不同角色人的父類,如新增教師類時只需繼承該類
public abstract class Person {

    /* 全名包括姓和名 */
    private String fullName;

    /* 年齡 */
    private int  age;

    /* 性別 */
    private String gender;

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

public class Student extends Person {

    /* 學號 */
    private int  studentId;

    public int getStudentId() {
        return studentId;
    }

    public void setStudentId(int studentId) {
        this.studentId = studentId;
    }

如上我們完成了該系統的領域模型的設計和創建,接下來我們將設計所需提供的服務。

Tip: 本文項目為模擬項目,因此領域模型設計可能考慮并不完善,設計并不十分合理。但這并不影響我們對Spring AOP的學習。

2.2 服務設計

完成了領域模型的定義,我們需要根據具體的用戶需求設計并提供相應的服務,該項目第一期我們提供學生查詢自己各科成績和所修科、為教師提供查詢班級人數等服務。

學生查詢服務接口

public interface StudentQueryService {
    /**
     * 根據學生ID查詢學生所有的課程考試信息
     * @param studentId 學生ID
     * @return 所有課程考試信息
     */
    List<Score> queryAllScoreByStudentId(int studentId);

    /**
     * 根據學生ID查詢學生所有課程
     * @param studentId 學生ID
     * @return  學生所選課程
     */
    List<Course> queryAllCourseByStudentId(int studentId);


    Student  queryStdent(String name,int age);
    //...
}

學生查詢服務實現類

public class StudentQueryServiceImpl implements StudentQueryService {

    public List<Score> queryAllScoreByStudentId(int studentId) {

        System.out.println("queryAllScoreByStudentId");

        return null;
    }

    public List<Course> queryAllCourseByStudentId(int studentId) {
        return null;
    }

    public Student queryStdent(String name, int age) {

        //return  new Student(name,age);
        return null;
    }
}

教師查詢服務接口

public interface TeacherQueryService {

    /* 根據班級ID查詢該班級人數*/
    int  queryStudentNumByCourseId(int courseId);
}

教師查詢服務實現類

public class TeacherQueryServiceImpl implements TeacherQueryService {

    public int queryStudentNumByCourseId(int courseId) {

        return 100;
    }
}

Tip: 服務實現以模擬為主,我們并沒有實現數據DAO層信息。另外,由于項目較小且為模擬展示為主,因此我們直接將領域模型對外提供。實際項目開發中,盡量不要將領域模型直接放回,以保證未來領域模型的變動不對外產生影響或盡量減少影響。

2.3 新需求增加

以上我們完成了領域模型設計、服務設計和開發工作,項目聯調順利,項目基本進入完結階段。但忽然提出需要記錄每個服務消耗時間,以及日志記錄返回結果等。
可能我們大多數人會立馬想到直接對每個服務的實現進行改動:每個具體的查詢服務在進入時記下開始時間、返回前記錄結束時間,然后計算出該服務消耗時間,然后將時間消耗和返回結果記錄日志。
當然在小型項目中該方式可以快捷方便的完成任務,且不會造成很大影響。但讓我們試想另幾種情況:

  • 忽然又新增需要校驗每個服務的調用者身份是否允許執行該查詢;
  • 當服務增加到一定量時,我們會發現記錄日志、記錄調用時間消耗等代碼遍布在每塊代碼里,對日志格式等信息的改變工作量將是巨大的。
  • ......

2.4 Spring AOP 引入

AOP技術就是為了解決這一類問題而誕生的,為滿足新需求我們引入Spring AOP來完成項目。
創建Aspect類(攔截器類)

Tip:一般我們常稱Aspect類為攔截器類

public class ServiceInterceptor {




    public Object serviceInterceptor(ProceedingJoinPoint proceedingJoinPoint){

        System.out.println("記錄開始時間");
        Object result = null;
        try {
           result =  proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("記錄結束時間,并計算時間間隔");
        System.out.println("將返回結果寫入日志,并將該服務消耗時間寫入日志");
        return result;
    }
}

配置攔截器

<bean id="serviceInterceptor" class="com.liangwei.learnspring.aop.ServiceInterceptor"/>

 <!--配置攔截器-->
    <aop:config>

        <!-- 定義切面 -->
        <aop:aspect id="logInterceptor" ref="serviceInterceptor">
            <!--定義切點-->
            <aop:pointcut id="allStudentQueryService" expression="execution(* com.liangwei.learnspring.service.StudentQueryService.*(..))"/>
            <!--定義增強advice-->
            <aop:around pointcut-ref="allStudentQueryService"
                        method="serviceInterceptor"/>

        </aop:aspect>
    </aop:config>

我們以XML方式配置了AOP信息,該環繞增強方法會在StudentQueryService接口的任何一個方法被調用時執行。

我們實現程序模擬服務被調用:

public class Application {

    public static void main(String[] args){


        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[]{"bean.xml","interceptor.xml"});

        StudentQueryService service = applicationContext.getBean("studentService",StudentQueryService.class);
        service.queryAllCourseByStudentId(1234);
        service.queryAllScoreByStudentId(314);
        TeacherQueryService teacherQueryService = applicationContext.getBean("teacherQueryService",TeacherQueryService.class);
        int studentNum = teacherQueryService.queryStudentNumByCourseId(10100);
        System.out.println("query student num:"+studentNum);
    }
}

執行上面方法控制中斷返回如下信息:

記錄開始時間
記錄結束時間,并計算時間間隔
將返回結果寫入日志,并將該服務消耗時間寫入日志
記錄開始時間
queryAllScoreByStudentId
記錄結束時間,并計算時間間隔
將返回結果寫入日志,并將該服務消耗時間寫入日志
query student num:100

由于我們只配置了攔截StudentQueryService接口的方法,因此當我們調用TeacherQueryService方法時,并沒有被攔截。

本文源代碼下載

四、總結

我們花了三篇文章來講述學習Spring AOP,至此我們已經完成了Spring AOP的基本知識學習,并且使用項目模擬的形式進行了Spring AOP實戰。當然,該三篇文章并沒有涵蓋所有Spring AOP的知識點,但在日常項目使用中已經足夠了。如果讀者想要更深入的學習AOP技術,可以自行學習研究AspectJ項目。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,797評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,179評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,628評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,642評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,444評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,948評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,040評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,185評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,717評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,794評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,418評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,414評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,750評論 2 370

推薦閱讀更多精彩內容