使用aspectj的@Around注解实现用户操作和操作结果日志

自定义注解,将需要记录日志的方法进行标记

/*
 * 常用注解说明:
 * 1. RetentionPolicy(保留策略)是一个enum类型,有三个值
 * SOURCE        --  这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译后,Annotation的数据就会消失,并不会保留在编译好的.class文件里
 * CLASS         --  这个Annotation类型的信息保留在程序源码中,同时也会保留在编译好的.class文件里面,在执行的时候,并不会把这一些信息加载到虚拟 机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS。
 * RUNTIME       --  在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的。
 *
 * 2.ElementType @Target中的ElementType用来指定Annotation类型可以用在哪些元素上
 * TYPE(类型)    -- 在Class,Interface,Enum和Annotation类型上
 * FIELD        -- 属性上
 * METHOD       -- 方法上
 * PARAMETER    -- 参数上
 * CONSTRUCTOR  -- 构造函数上
 * LOCAL_VARIABLE -- 局部变量
 * ANNOTATION_TYPE   -- Annotation类型上
 * PACKAGE           -- 包上
 *
 * 3.Documented    -- 让这个Annotation类型的信息能够显示在API说明文档上;没有添加的话,使用javadoc生成的API文件找不到这个类型生成的信息
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface TestAnnotation {

    //操作内容
    String operation() default "";
}

配置Aspect,创建规则和方法

package com.consumer.interceptor;

import com.consumer.annotation.TestAnnotation;
import com.consumer.entity.LogMessage;
import com.consumer.entity.ReturnMessage;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import org.springframework.stereotype.Component;

import java.util.Date;


/*
 * 特别注意: Spring的配置文件中添加:
 *
 * <aop:aspectj-autoproxy />
 * spring-mvc-dispatcher.xml中天机
 * <!--通知spring使用cglib而不是jdk的来生成代理方法 AOP可以拦截到Controller-->
 * <aop:aspectj-autoproxy proxy-target-class="true"/>
 *
 * <aop:config>节点中proxy-target-class="true"不为true时。
 * 当登录的时候会报这个异常java.lang.NoSuchMethodException: $Proxy54.login(),
 */
@Aspect
@Component
public class LogInterceptor {
    /**
     * 环绕通知 用于拦截指定内容,记录用户的操作
     * pj:ProceedingJoinPoint 是切入点对象
     * annotation:TestAnnotation 自定义的注解对象
     * object:Object 方法的第一个参数
     */
    @Around(value = "@annotation(annotation) && args(object,..) ", argNames = "pj,annotation,object")
    public Object interceptorTest(ProceedingJoinPoint pj,
                                      TestAnnotation annotation, Object object) throws Throwable {


        System.out.println("执行方法 "+pj.getSignature().getName());
        // 初始化日志数据
        LogMessage logMessage = new LogMessage();
        // 获取操作的参数
        Object[] args = pj.getArgs();
        if(args.length>=1){
            // 写入id
            logMessage.setManId(args[0].toString());
        }
        // 写入操作时间
        logMessage.setDate(new Date().getTime());
        // 写入操作名
        logMessage.setOperation(annotation.operation());

        // 执行操作,获取操作的返回结果
        ReturnMessage returnMessage = (ReturnMessage) pj.proceed();
        // 写入操作结果
        logMessage.setSuccess(returnMessage.getStatus());
        // 如果操作结果失败,写入失败原因
        if(!logMessage.isSuccess()){
            logMessage.setReason(returnMessage.getMsg());
        }
        //输出日志信息
        System.out.println(logMessage.toString());
        // 输出结束标识
        System.out.println("执行结束 "+pj.getSignature().getName());

        // 返回操作的原本结果
        return returnMessage;
    }
}

添加到配置文件xml

<context:annotation-config/>    
<aop:aspectj-autoproxy />  
<context:component-scan base-package="com.consumer" />  

日志封装

操作结果封装

记录controller的日志记录,单纯返回固定的测试数据

@RequestMapping("aopTest")
    @ResponseBody
    @TestAnnotation(operation = "测试AOP日志记录")
    public ReturnMessage aopTest(
            @RequestParam(name = "manId")String manId){
        return new ReturnMessage(false, "草泥马", null);
    }

使用PostMan测试接口,数据返回无误

查看控制台日志信息,操作名,参数,结果和时间都被记录

附上参考代码,地址:https://www.cnblogs.com/softidea/p/6123307.html

import java.util.Arrays;

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.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class AdviceTest {
    @Around("execution(* com.abc.service.*.many*(..))")
    public Object process(ProceedingJoinPoint point) throws Throwable {
        System.out.println("@Around:执行目标方法之前...");
        //访问目标方法的参数:
        Object[] args = point.getArgs();
        if (args != null && args.length > 0 && args[0].getClass() == String.class) {
            args[0] = "改变后的参数1";
        }
        //用改变后的参数执行目标方法
        Object returnValue = point.proceed(args);
        System.out.println("@Around:执行目标方法之后...");
        System.out.println("@Around:被织入的目标对象为:" + point.getTarget());
        return "原返回值:" + returnValue + ",这是返回结果的后缀";
    }
    
    @Before("execution(* com.abc.service.*.many*(..))")
    public void permissionCheck(JoinPoint point) {
        System.out.println("@Before:模拟权限检查...");
        System.out.println("@Before:目标方法为:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@Before:参数为:" + Arrays.toString(point.getArgs()));
        System.out.println("@Before:被织入的目标对象为:" + point.getTarget());
    }
    
    @AfterReturning(pointcut="execution(* com.abc.service.*.many*(..))", 
        returning="returnValue")
    public void log(JoinPoint point, Object returnValue) {
        System.out.println("@AfterReturning:模拟日志记录功能...");
        System.out.println("@AfterReturning:目标方法为:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@AfterReturning:参数为:" + 
                Arrays.toString(point.getArgs()));
        System.out.println("@AfterReturning:返回值为:" + returnValue);
        System.out.println("@AfterReturning:被织入的目标对象为:" + point.getTarget());
        
    }
    
    @After("execution(* com.abc.service.*.many*(..))")
    public void releaseResource(JoinPoint point) {
        System.out.println("@After:模拟释放资源...");
        System.out.println("@After:目标方法为:" + 
                point.getSignature().getDeclaringTypeName() + 
                "." + point.getSignature().getName());
        System.out.println("@After:参数为:" + Arrays.toString(point.getArgs()));
        System.out.println("@After:被织入的目标对象为:" + point.getTarget());
    }
}

触发

String result = manager.manyAdvices("aa", "bb");
System.out.println("Test方法中调用切点方法的返回值:" + result);

控制台结果

@Around:执行目标方法之前...
@Before:模拟权限检查...
@Before:目标方法为:com.abc.service.AdviceManager.manyAdvices
@Before:参数为:[改变后的参数1, bb]
@Before:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
方法:manyAdvices
@Around:执行目标方法之后...
@Around:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@After:模拟释放资源...
@After:目标方法为:com.abc.service.AdviceManager.manyAdvices
@After:参数为:[改变后的参数1, bb]
@After:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
@AfterReturning:模拟日志记录功能...
@AfterReturning:目标方法为:com.abc.service.AdviceManager.manyAdvices
@AfterReturning:参数为:[改变后的参数1, bb]
@AfterReturning:返回值为:原返回值:改变后的参数1 、 bb,这是返回结果的后缀
@AfterReturning:被织入的目标对象为:com.abc.service.AdviceManager@1dfc617e
Test方法中调用切点方法的返回值:原返回值:改变后的参数1 、bb,这是返回结果的后缀

 

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值