天天看點

【AOP】利用aop,實作方法的環繞日志(包括注解日志)背景需求步驟過程運作拓展

背景

在工作中,日志是查找BUG的重要手段之一,正常的方式就是代碼中通過面向過程的方式,即在代碼中将想要顯示的變量輸出。

然而,在是調試當中,經常需要檢視方法的傳入參數,但是如果代碼中沒有log,那查日志就GG了;但是在每個方法都log一段代碼,那就有點累贅了,是以接下來将用Spring中的AOP(切面程式設計)的方式,實作方法環繞日志。

需求

用切面程式設計的方式,記錄代碼運作中傳入變量的值。

步驟

1.maven依賴

2.編寫切面

3.注入bean和配置Pointcut

過程

  1. maven依賴
<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>${springframework.version}</version>
</dependency>
           
  1. 編寫切面(用于記錄方法參數的日志操作)
package com.liushiyao.common;

import java.util.Arrays;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class LogAspect {

    private final Logger logger = Logger.getLogger(this.getClass());

    /**
     * 輸出調用的參數
     *
     * @param joinPoint
     */
    public void after(JoinPoint joinPoint) {
        //擷取參數清單
        Object[] arguments = joinPoint.getArgs();
        //方法名
        String methodName = joinPoint.getSignature().getName();
        String targetName = joinPoint.getTarget().getClass().getName();
        logger.debug("{refer}" + targetName + "." + methodName + ",{arg}" + Arrays.toString(arguments));
    }

}

           
  • 類LogAspect即為AOP程式設計中的Aspect,裡面用來聲明before(略),after,around(略)
  • after方法即為AOP程式設計中的Advice,是AOP程式的具體操作。
  • after方法是調用方法之後執行的,通過JoinPoint對象,可以獲得方法的參數清單已經對應的名字等等
  1. 注入bean和配置Pointcut

    在dispatcher-servlet.xml增加以下代碼

<!--指定掃描目錄-->
    <aop:aspectj-autoproxy proxy-target-class="true" />

    <!--将日志類注入到bean中。-->
    <bean id="logAspect" class="com.liushiyao.common.LogAspect"></bean>

    <aop:config>
        <!--調用日志類-->
        <aop:aspect id="LogAspect" ref="logAspect">
            <!--配置在service包下所有的類在調用之前都會被攔截-->
            <aop:pointcut id="log" expression="execution(* com.liushiyao.blog.service..*.*(..))"/>
			<!--方法前觸發 <aop:before pointcut-ref="log" method="before"/>-->
            <!-- 方法後觸發 --><aop:after pointcut-ref="log" method="after"/>
            <!-- 環繞觸發  <aop:around pointcut-ref="log" method="around"/>  -->
        </aop:aspect>
    </aop:config>
           
  • pointcut(切點),比對service下的所有方法,當service中的方法被調用的時候,就會執行after方法。

運作

寫個測試的controller

@RequestMapping("/aop")
    @CrossOrigin
    private void aop(Integer integer,String string){

        testService.aopLogTest(integer,string);

    }

           
  • controller使用到了testService中的aopLogTest的方法

而aopLogTest方法隻是個空方法,

public void aopLogTest(int arg,String strArg){
    }

           

調用接口,輸入日志

[DEBUG] 2019-10-11 00:08:58,739 method:com.liushiyao.common.LogAspect.after(LogAspect.java:30)
{refer}com.liushiyao.blog.service.TestService.aopLogTest,{arg}[1, 我是字元串]
           

從日志可以看出,通過AOP的方式被調用的方法的參數清單自動的以日志的方式記錄了下來(即使被調用的方法中沒有手動把方法參數列印出來),這就是面向切面程式設計。

拓展

pointcut是通過配置檔案比對的,那能不能通過注解的方式(當然,比對的方式也可以實作,隻是注解更加的靈活),想要記錄哪些方法就記錄哪些方法?

當然可以!

  1. 先定義一個注解
import com.liushiyao.common.def.Log;
import java.lang.annotation.*;

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AutoLog {

    //日志類型
    public int type () default Log.Type.DEBUG;


}
           
  1. 定義日志類型
public class Log {

    public static final class Type{

        public static final int DEBUG = 1;
        public static final int INFO = 2;
        public static final int ERROR = 3;

    }

}

           
  1. 對after方法進行改造
/**
     * 輸出調用的參數
     *
     * @param joinPoint
     */
    public void after(JoinPoint joinPoint) {

        //擷取參數清單
        Object[] arguments = joinPoint.getArgs();
        //方法名
        String methodName = joinPoint.getSignature().getName();
        String targetName = joinPoint.getTarget().getClass().getName();

        Class targetClass = null;
        try {
            targetClass =
                Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method methods [] = targetClass.getMethods();
        for (Method method:methods){
            //找到對應的方法
            if(method.getName().equals(methodName)){
                //是否有注解
                AutoLog autoLog = method.getAnnotation(AutoLog.class);
                if(autoLog != null){
                    if(autoLog.type() == Log.Type.DEBUG){
                        logger.debug("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
                    }else if(autoLog.type() == Log.Type.INFO){
                        logger.info("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
                    }else if(autoLog.type() == Log.Type.ERROR){
                        logger.error("{refer}" + targetName +"." + methodName + ",{arg}" + Arrays.toString(arguments));
                    }
                }
            }

        }

    }
           
  1. 對需要打日志的方式加注解
  • TestService
public void aopLogTest(int arg,String strArg) {
    }
	@AutoLog(type = Log.Type.INFO)
    public void aopLogTest2(String strArg){
    }
           
@RequestMapping("/aop")
    private void aop(Integer integer,String string){

        testService.aopLogTest(integer,string);
		testService.aopLogTest2(string);

    }

           
  1. 運作結果
[INFO ] 2019-10-12 22:16:17,020 method:com.liushiyao.common.LogAspect.after(LogAspect.java:52)
{refer}com.liushiyao.blog.service.testService.aopLogTest2,{arg}[555]
           

可以看出隻有加了@AutoLog的方法才會記錄日志。

以上就是對環繞日志應用和注解日志的介紹,如有錯誤,歡迎指正

繼續閱讀