【java】使用注解加ASPECT实现拦截方法打印方法日志

        为了熟悉AOP中的ASPECTJ的使用,为了方便我们在写数据搬运型代码时候观测的方便,自己学习并定义了一组切面方法和注解,来实现这个功能,啥都不说了,先上代码:

首先是注解定义:

import java.lang.annotation.*;

/**
 * @author wangxiao
 * @date 2020-05-26 10:38
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Documented
public @interface ServiceAroundLog {

    /**
     * 定义方法名称
     * @return
     */
    String methodName() default "";

    /**
     * 是否打印方法执行时间
     * @return
     */
    boolean isTimeInterval() default true;

    /**
     * 是否打印方法参数
     * @return
     */
    boolean isPrintParam() default true;

}

注解定义完成之后,我们开始定义使用这个注解的切面方法:

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wangxiao
 * @date 2020-05-26 10:50
 */
@Aspect
@Order(2)
@Component
public class ServiceAroundLogAspect {

    @Around("@annotation(ServiceAroundLog)")
    public Object beforeReturnValue(ProceedingJoinPoint point) throws Throwable {
        Method methodSignart = ((MethodSignature) point.getSignature()).getMethod();
        ServiceAroundLog methodAnnotation = methodSignart.getAnnotation(ServiceAroundLog.class);

        if (methodAnnotation != null) {

            Logger logger = LoggerFactory.getLogger(point.getThis().getClass());
            boolean isTimeInterval = methodAnnotation.isTimeInterval();
            String name = methodAnnotation.methodName();
            if (StringUtils.isEmpty(name)) {
                name = methodSignart.getName();
            }
            logger.info("----------方法{}开始执行----------", name);
            long s = 0;
            if (isTimeInterval) {
                s = System.currentTimeMillis();
            }

            boolean isPrintParam = methodAnnotation.isPrintParam();

            if (isPrintParam) {
                Parameter[] parameters = methodSignart.getParameters();
                Object[] args = point.getArgs();
                if (parameters != null || parameters.length > 0) {
                    StringBuilder sb = new StringBuilder();
                    sb.append("方法参数列表:[");
                    for (int i = 0; i < parameters.length; i++) {
                        sb.append("{参数名:");
                        sb.append(parameters[i].getName());
                        sb.append("-");
                        sb.append("值:");
                        sb.append(parameters[i] == null ? "null" : JSON.toJSONString(args[i]));
                        sb.append("}");
                    }
                    sb.append("]");
                    logger.info(sb.toString());
                }
            }

            Object proceed = point.proceed();
            // 返回值为list类型,打印返回值list数量
            if (proceed instanceof ArrayList) {
                final List list = (List) proceed;
                logger.info("-----返回值为list集合,大小为:{}-----", list.size());
            }

            if (isTimeInterval) {
                logger.info("--------方法{}执行耗时:{}ms--------", name, (System.currentTimeMillis() - s));
            }
            logger.info("----------方法{}执行结束----------", name);
            return proceed;
        }

        return point.proceed();
    }
}

这样当我们在要使用的方法上打上@ServiceAroundLog注解的时候,他就会在方法执行的时候,打印方法执行的日志了。

例如我们是如上图这样使用的。

关于这个AOP的写法,我也要说一下:

切面的编写有三种:before,around,after这三种位置,在before和after我们都会get到我们的joinpoint,获得方法的一些信息,我们可以再方法执行前,方法执行全局和方法执行后做这些事情,之所以我们选择的是around这个切面层进行日志的输出,主要的是有两点:

        1、around方法既可以获得到方法执行前的参数,又可以获得方法执行后的return值。

        2、before和after,一个在执行前,一个在执行后,根本不能测量方法的执行时间,所以只能放在around中。

另外我在这个过程中学习到的东西有:

       1、spring自带了反射的util,是ReflectionUtils还有代理的util是ProxyUtils,并且如果直接通过反射是拿不到具体的值的,只能通过代理。

       2、before,around,after的一些作用和AOP具体能够做到什么的一个认识(使用before可以加全局拦截入口认证,使用around可以拦截方法执行过程并且添加额外的东西,使用after可以定义统一关闭或者还原操作等等)。

       3、真实的AOP的切入点有五种,分别是Before,Around,After,AfterReturning,AfterThrowing,不同情况下会使用到不同的地方,如下图:

 

        上面的切面,只是我用来学习用的小程序,如果里面存在什么问题,欢迎大家予以指正,同时大家也不要轻易用在生产环境的项目里面,用之前要三思。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值