1. 程式人生 > >Spring AOP 基於註解實現日誌記錄+自定義註解

Spring AOP 基於註解實現日誌記錄+自定義註解

一、寫一個自定義註解

        註解中包括配置方法所在模組名稱,以及功能名稱,當然我們在註解裡可以自定義。

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,其他的配置方向差異不大,讀者可自行配置