Spring AOP 基於註解實現日誌記錄+自定義註解
阿新 • • 發佈:2019-02-01
一、寫一個自定義註解
註解中包括配置方法所在模組名稱,以及功能名稱,當然我們在註解裡可以自定義。
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Log { /** 模組 */ String title() default ""; /** 功能 */ String action() default ""; }
二、建切面類
切面類裡面定義好切點配置,以及所有的需要實現的通知方法。
import java.lang.reflect.Method; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component("logAspect") public class LogAspect { private static final Logger log = LoggerFactory.getLogger(LogAspect.class); // 配置織入點 @Pointcut("@annotation(com.test.aspact.Log)") public void logPointCut() { } /** * 前置通知 用於攔截操作,在方法返回後執行 * @param joinPoint 切點 */ @AfterReturning(pointcut = "logPointCut()") public void doBefore(JoinPoint joinPoint) { handleLog(joinPoint, null); } /** * 攔截異常操作,有異常時執行 * * @param joinPoint * @param e */ @AfterThrowing(value = "logPointCut()", throwing = "e") public void doAfter(JoinPoint joinPoint, Exception e) { handleLog(joinPoint, e); } private void handleLog(JoinPoint joinPoint, Exception e) { try { // 獲得註解 Log controllerLog = getAnnotationLog(joinPoint); if (controllerLog == null) { return; } // 獲得方法名稱 String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); String action = controllerLog.action(); String title = controllerLog.title(); //列印日誌,如有需要還可以存入資料庫 log.info(">>>>>>>>>>>>>模組名稱:{}",title); log.info(">>>>>>>>>>>>>操作名稱:{}",action); log.info(">>>>>>>>>>>>>類名:{}",className); log.info(">>>>>>>>>>>>>方法名:{}",methodName); } catch (Exception exp) { // 記錄本地異常日誌 log.error("==前置通知異常=="); log.error("異常資訊:{}", exp.getMessage()); exp.printStackTrace(); } } /** * 是否存在註解,如果存在就獲取 */ private static Log getAnnotationLog(JoinPoint joinPoint) throws Exception { Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); if (method != null) { return method.getAnnotation(Log.class); } return null; } }
三、spring.xml配置
在spring的配置檔案中,開啟註解的掃描,開啟切面掃描。
<context:component-scan base-package="com.test.**"/>
<mvc:annotation-driven />
<aop:aspectj-autoproxy />
<aop:config proxy-target-class="true"></aop:config>
四、測試Controller
@Controller public class HelloController { private static final Logger log = LoggerFactory.getLogger(HelloController.class); @RequestMapping("/hello") //對應的自定義註解,當方法上寫這個註解時,就會進入切面類中 @Log(title="哈嘍模組",action="say哈嘍") public String sayHello() { log.info("HelloController sayHello:{}","hello world!"); return "hello"; } }
測試結果:
輸入網址http://localhost/test-demo/hello,進入hello.jsp
控制檯日誌
看結果可知,先輸出了方法裡的日誌,在返回之後進入了切面方法,列印一出了自定義註解的資訊,以及方法具體資訊。
五、切面類註解
@Aspect 宣告切面,修飾切面類,從而獲得 通知。
@Before 前置通知,在目標方法開始之前執行
@AfterReturning 後置通知
@Around 環繞 注意:環繞通知內部一定要確保執行proceed()該方法,如果不執行該方法,業務bean中被攔截的方法就不會被執行。當執行該方法,如果後面還有切面的話,它的執行順序應該是這樣的:先執行後面的切面,如果後面沒有切面了,再執行最終的目標物件的業務方法。若不執行該方法,則後面的切面,業務bean的方法都不會被執行。
其實我們僅使用環繞通知就可以實現前置通知、後置通知、異常通知、最終通知等的效果。
@AfterThrowing 丟擲異常
@After 最終
@PointCut ,切入點,修飾方法 private void xxx(){} 之後通過“方法名”獲得切入點引用
上面例子中用到了 @AfterReturning @AfterThrowing,其他的配置方向差異不大,讀者可自行配置