Spring AOP 的實現方式(以日誌管理爲例)

Spring AOP 的實現方式(以日誌管理爲例)

2016年10月08日 00:13:57html

閱讀數:23198java

在學習Spring框架的歷程中,最重要的是要理解Spring的IOC和AOP了,不但要學會怎麼用,最好是知道它是怎麼實現的,經過這個國慶假期,好好地過了一下spring的AOP的皮毛,故記錄一下學習心得。spring

1、爲何須要AOPexpress

假如咱們應用中有n個業務邏輯組件,每一個業務邏輯組件又有m個方法,那如今咱們的應用就一共包含了n*m個方法,我會抱怨方法太多。。。如今,我有這樣一個需求,每一個方法都增長一個通用的功能,常見的如:事務處理,日誌,權限控制。。。最容易想到的方法,先定義一個額外的方法,實現該功能,而後再每一個須要實現這個功能的地方去調用這個額外的方法。這種作法的好處和壞處分別是。
好處:能夠動態地添加和刪除在切面上的邏輯而不影響原來的執行代碼。
壞處:一旦要修改,就要打開全部調用到的地方去修改。
好,如今咱們用AOP的方式能夠實如今不修改源方法代碼的前提下,能夠統一爲原多個方法增長橫切性質的「通用處理」。編程

2、什麼是AOPspring-mvc

都說AOP好用,那如今咱們來談談什麼是AOP。緩存

AOP(Aspect-OrientedProgramming,面向方面編程),能夠說是OOP(Object-Oriented Programing,面向對象編程)的補充和完善。本文提供Spring官方文檔出處:Aspect Oriented Programming with Springmvc

從官方文檔上摘抄的解釋就是:面向方面編程(AOP)是面向對象編程(OOP)補充的另外一種提供思考程序結構補充。在OOP中模塊化的關鍵單元是類,而在AOP模塊的單位是一個方面。面對關注點,如事務管理跨越多個類型和對象切模塊化。(這些關注常常被稱爲在AOP文學橫切關注點。)app

相關概念(只需作個大概的瞭解就好)----來自於官方文檔直譯(本人英文水平有限。。。):框架

Aspect:這橫切多個對象關心的模塊化。事務管理是企業Java應用程序的橫切關注點的一個很好的例子。在Spring AOP中,切面可使用類(基於模式)或@Aspect註解(@AspectJ風格)註解普通班實施。
Join point:程序在執行過程當中的一個點,如方法的執行或異常的處理。在Spring AOP中,一個鏈接點老是表明一個方法的執行。
Advice:在切面的某個特定的動做鏈接點。不一樣類型的意見,包括 "around," "before" and "after"的advice。 (通知的類型將在下面討論)。許多AOP框架,包括Spring都是以攔截器做爲通知模型,去維護一條圍繞着一個鏈接點的攔截器鏈。
Pointcut:匹配鏈接點的斷言。通知是跟一個切入點表達式,並在運行在切入點匹配的鏈接點相關聯(例如,一個方法的執行要有一個肯定的名字)。切入點表達式做爲匹配的鏈接點的概念是重要的對AOP和Spring缺省使用AspectJ切入點表達式語言。
Introduction:聲明表明的類型的額外的方法或字段。 Spring容許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,你可使用引入來使一個bean實現IsModified接口,以便簡化緩存。 (介紹被譽爲AspectJ的社會類型間的聲明。)
Target object:對象由一個或多個方面被建議。也被稱做被通知對象。既然Spring AOP是經過運行時代理實現的,這個對象永遠是一個被代理對象。
AOP proxy:AOP框架,以實現切面契約(例如通知方法執行等等)建立的對象。在Spring中,AOP代理能夠是JDK動態代理或者CGLIB代理。
Weaving:與鏈接其餘應用程序類型或對象方面來建立一個被通知的對象。這是能夠作到在編譯時(使用AspectJ編譯器,例如),加載時間,或在運行時。 Spring AOP中,像其餘純Java AOP框架,在運行時進行編織。

以上概念我的感受在作實驗的時候就差很少理解了,不須要咬文嚼字地區啃。

那問題來了,AOP是在何時去改咱們的代碼的?即給咱們加上額外的橫切性質的"通用處理"的?

兩個時機:

1.在編譯java源代碼的時候 ----編譯時加強
2.在運行時動態地修改類 ----運行時加強(動態代理)

咱們的Spring的AOP的實現原理就是基於動態代理。(以後我會嘗試一下跟隨源碼看看Spring在AOP方面作了哪些事情)

3、Spring AOP的3種實現方式

對於框架的學習,我以爲得先會用,而後再深刻原理。關於Spring AOP的實現我在這裏劃分紅3個方式(以日誌管理爲例)廢話很少說,直接上代碼了。(如下代碼是基於我以前所寫的SSM框架整合的例子,若是有須要可查看我以前的博客)

配置以前注意配置文件要加上命名空間:xmlns:aop="http://www.springframework.org/schema/aop"

1.基於xml配置的實現

spring-mvc.xml

 

 
  1. <!-- 使用xml配置aop -->

  2. <!-- 強制使用cglib代理,若是不設置,將默認使用jdk的代理,可是jdk的代理是基於接口的 -->

  3. <aop:config proxy-target-class="true" />

  4. <aop:config>

  5. <!--定義切面-->

  6. <aop:aspect id="logAspect" ref="logInterceptor">

  7. <!-- 定義切入點 (配置在com.gray.user.controller下全部的類在調用以前都會被攔截)-->

  8. <aop:pointcut expression="execution(* com.gray.user.controller.*.*(..))" id="logPointCut"/>

  9. <!--方法執行以前被調用執行的-->

  10. <aop:before method="before" pointcut-ref="logPointCut"/><!--一個切入點的引用-->

  11. <aop:after method="after" pointcut-ref="logPointCut"/><!--一個切入點的引用-->

  12. </aop:aspect>

  13. </aop:config>

LogInterceptor.java

 

 

 
  1. package com.gray.interceptor;

  2.  
  3. import org.springframework.stereotype.Component;

  4. import org.slf4j.Logger;

  5. import org.slf4j.LoggerFactory;

  6.  
  7. @Component

  8. public class LogInterceptor {

  9. private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

  10. public void before(){

  11. logger.info("login start!");

  12. }

  13.  
  14. public void after(){

  15. logger.info("login end!");

  16. }

  17. }

在這裏我沒有配bean是由於我在以前的配置文件裏面寫了自動掃描組件的配置了
要加入日誌管理邏輯的地方

 

 

 
  1. @RequestMapping("/dologin.do") //url

  2. public String dologin(User user, Model model){

  3. logger.info("login ....");

  4. String info = loginUser(user);

  5. if (!"SUCC".equals(info)) {

  6. model.addAttribute("failMsg", "用戶不存在或密碼錯誤!");

  7. return "/jsp/fail";

  8. }else{

  9. model.addAttribute("successMsg", "登錄成功!");//返回到頁面說夾帶的參數

  10. model.addAttribute("name", user.getUsername());

  11. return "/jsp/success";//返回的頁面

  12. }

  13. }

 

結果截圖:

2.基於註解的實現

spring-mvc.xml

 

 
  1. <aop:aspectj-autoproxy proxy-target-class="true">

  2. </aop:aspectj-autoproxy>

 

LogInterceptor.java

 

 
  1. @Aspect

  2. @Component

  3. public class LogInterceptor {

  4. private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

  5. @Before(value = "execution(* com.gray.user.controller.*.*(..))")

  6. public void before(){

  7. logger.info("login start!");

  8. }

  9. @After(value = "execution(* com.gray.user.controller.*.*(..))")

  10. public void after(){

  11. logger.info("login end!");

  12. }

  13. }

要加入邏輯的地方同上。

結果截圖:

3.基於自定義註解的實現

基於註解,因此spring-mvc.xml也是和上面的同樣的。

LogInterceptor.java(這裏我只加入前置日誌)

 

 
  1. package com.gray.interceptor;

  2.  
  3. import java.lang.reflect.Method;

  4.  
  5. import org.aspectj.lang.JoinPoint;

  6. import org.aspectj.lang.annotation.Aspect;

  7. import org.aspectj.lang.annotation.Before;

  8. import org.aspectj.lang.annotation.Pointcut;

  9. import org.aspectj.lang.reflect.MethodSignature;

  10. import org.slf4j.Logger;

  11. import org.slf4j.LoggerFactory;

  12. import org.springframework.stereotype.Component;

  13.  
  14. import com.gray.annotation.Log;

  15.  
  16. @Aspect

  17. @Component

  18. public class LogInterceptor {

  19. private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);

  20.  
  21. @Pointcut("@annotation(com.gray.annotation.Log)")

  22. public void controllerAspect() {

  23.  
  24. }

  25. @Before("controllerAspect()")

  26. public void before(JoinPoint joinPoint){

  27. logger.info(getOper(joinPoint));

  28. }

  29. private String getOper(JoinPoint joinPoint) {

  30. MethodSignature methodName = (MethodSignature)joinPoint.getSignature();

  31. Method method = methodName.getMethod();

  32. return method.getAnnotation(Log.class).oper();

  33. }

  34. }

同時,加入邏輯的地方須要加入Log註解

 
  1. @RequestMapping("/dologin.do") //url

  2. @Log(oper="user login")

  3. public String dologin(User user, Model model){

  4. logger.info("login ....");

  5. String info = loginUser(user);

  6. if (!"SUCC".equals(info)) {

  7. model.addAttribute("failMsg", "用戶不存在或密碼錯誤!");

  8. return "/jsp/fail";

  9. }else{

  10. model.addAttribute("successMsg", "登錄成功!");//返回到頁面說夾帶的參數

  11. model.addAttribute("name", user.getUsername());

  12. return "/jsp/success";//返回的頁面

  13. }

  14. }

 

結果截圖:

以上就是我總結的SpringAOP的3種實現方式,對於這我還有點話要說,常見的基於註解的方式除了before和after,還有around和AfterThrowing等,在作第三種方式實驗的時候還遇到這種錯誤:error at ::0 can't find referenced pointcut。

個人解決辦法是:由於個人jdk是1.7,與原來在pom.xml中配的那個aspectjweaver的jar包版本不匹配,因此我須要升級,由1.5.4改爲1.7.4。

此外多說一句,對於日誌,權限等業務邏輯不少人其實也喜歡用攔截器來實現的。

相關文章
相關標籤/搜索