【AOP】利用aop,实现方法的环绕日志(包括注解日志)

背景

在工作中,日志是查找BUG的重要手段之一,常规的方式就是代码中通过面向过程的方式,即在代码中将想要显示的变量输出。

        logger.debug("id="+id);

然而,在是调试当中,经常需要查看方法的传入参数,但是如果代码中没有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的方法才会记录日志。

以上就是对环绕日志应用和注解日志的介绍,如有错误,欢迎指正


版权声明:本文为lsy1072原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。