AOP 日志简单实例

517 阅读2分钟

废话不多说直接看一下效果:

微信截图_20210707155103.png

1.自定义接口, 后续某方法使用就添加注解就可以

import java.lang.annotation.*;
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {

       /** 要执行的操作类型比如:add操作 **/
     String operationType() default "";

    /** 要执行的具体操作比如:添加用户 **/
     String operationName() default "";
}

2.切面

import java.util.Date;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


/**
 * @author win 10
 * @E-mail: email
 * @version 创建时间:2021-06-19 下午4:29:05
 * @desc 切点类
 */

@Aspect
@Component
public class SystemLogAspect {

    //注入Service用于把日志保存数据库
//    @Resource  //这里我用resource注解,一般用的是@Autowired,自行百度
//    private SystemLogService systemLogService;

    private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect. class);

    // 作用在Controller层切点 execution (* com.xx.controller..*.*(..))
    // 直接切点为自定义接口
    @Pointcut("@annotation(MyLog)")
    public  void controllerAspect() {
    }

    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("==========执行controller前置通知===============");
        if(logger.isInfoEnabled()){
            logger.info("before " + joinPoint);
        }
    }

    //配置controller环绕通知,使用在方法aspect()上注册的切入点
    @Around("controllerAspect()")
    public Object around(JoinPoint joinPoint){
        System.out.println("==========开始执行controller环绕通知===============");
        long start = System.currentTimeMillis();
        Object obj = new Object();
        try {
            obj = ((ProceedingJoinPoint) joinPoint).proceed(joinPoint.getArgs());
            long end = System.currentTimeMillis();
            if(logger.isInfoEnabled()){
                logger.info("around " + joinPoint + "\nUse time : " + (end - start) + " ms!");
            }
            System.out.println("==========结束执行controller环绕通知===============");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            if(logger.isInfoEnabled()){
                logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
        return obj;
    }

    /**
     * 后置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @After("controllerAspect()")
    public  void after(JoinPoint joinPoint) {

      
        try {
            String targetName = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            Object[] arguments = joinPoint.getArgs();
            Class targetClass = Class.forName(targetName);
            Method[] methods = targetClass.getMethods();
            String operationType = "";
            String operationName = "";
            for (Method method : methods) {
                if (method.getName().equals(methodName)) {
                    Class[] clazzs = method.getParameterTypes();
                    if (clazzs.length == arguments.length) {
                        operationType = method.getAnnotation(MyLog.class).operationType();
                        operationName = method.getAnnotation(MyLog.class).operationName();
                        break;
                    }
                }
            }
//            //*========控制台输出=========*//
            System.out.println("=====controller后置通知开始=====");
            System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()")+"."+operationType);
            System.out.println("方法描述:" + operationName);
            System.out.println("=====controller后置通知结束=====");
        }  catch (Exception e) {
            //记录本地异常日志
            logger.error("==后置通知异常==");
            logger.error("异常信息:{}", e.getMessage());
        }
    }

    //配置后置返回通知,使用在方法aspect()上注册的切入点
    @AfterReturning("controllerAspect()")
    public void afterReturn(JoinPoint joinPoint){
        System.out.println("=====执行controller后置返回通知=====");
        if(logger.isInfoEnabled()){
            logger.info("afterReturn " + joinPoint);
        }
    }

    /**
     * 异常通知 用于拦截记录异常日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "controllerAspect()", throwing="e")
    public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {

        System.out.println("AfterThrowing...");
        System.out.println("=====异常通知结束=====");
        }  catch (Exception ex) {
            //记录本地异常日志
            logger.error("==异常通知异常==");
            logger.error("异常信息:{}", ex.getMessage());
        }
        /*==========记录本地异常日志==========*/

    }

}

上面写完后,是这样的:

微信截图_20210707152317.png

在需要的方法上添加注解:

微信截图_20210707152616.png

3.关于jar包依赖:

首先,使用aop依赖包除了Spring提供给开发者的jar包 pom.xml:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.0.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.9</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.9</version>
        </dependency>

注: 我遇到的问题记录一下,实现日志后前端无法接收JSON数据问题

通过测试去掉注解后可以返回,添加上注解就不行, 刚开始怀疑后置方法的问题, 但是检查后,发现原来是环绕方法的问题;

原因: 开启环绕通知(@Around), 且 Around写的是void方法,这时候修改返回Object返回类型并且把obj给return回去,问题解决啦

微信截图_20210707153440.png