Aop实现注解日志的记录

日志实体

@Data
public class ServiceLog implements Serializable {

    /**
     * 创建时间
     */
    private String createTime;

    /**
     * 方法名
     */
    private String method;
    /**
     * 参数
     */
    private String params;

    /**
     * 日志描述
     */
    private String description;

    /**
     * 方法运行时间
     */
    private Long runTime;

    /**
     * 方法返回值
     */
    private String response;
}


注解类:
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceInvoker {

    /**
     * 方法描述,可使用占位符获取参数:{{tel}}
     */
    String service() default "";

}

aop类:
@Slf4j
@Aspect
@Component
public class ServiceInvokerLog {

    /**
     * 环绕增强,相当于MethodInterceptor
     */
    @Around(value = "@annotation(com.htsc.kh.extend.annotation.ServiceInvoker)")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        long time = System.currentTimeMillis();
        try {
            result = joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            return result;
        } finally {
            try {
                //方法执行完成后增加日志
                addOperationLog(joinPoint, result, time);
            } catch (Exception e) {
                throw e;
            }
        }
    }

    private void addOperationLog(JoinPoint joinPoint, Object res, long time) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        ServiceLog operationLog = new ServiceLog();
        operationLog.setRunTime(time);
        operationLog.setResponse(JSON.toJSONString(res));
        operationLog.setParams(JSON.toJSONString(joinPoint.getArgs()));
        operationLog.setCreateTime(Util.formatDetailDate2(new Date()));
        operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        ServiceInvoker annotation = signature.getMethod().getAnnotation(ServiceInvoker.class);
        if (annotation != null) {
            operationLog.setDescription(getDetail(((MethodSignature) joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs(), annotation));
        }
        //TODO 这里保存日志
        log.warn("【"+operationLog+"】");
    }

    /**
     * 对当前登录用户和占位符处理
     *
     * @param argNames   方法参数名称数组
     * @param args       方法参数数组
     * @param annotation 注解信息
     * @return 返回处理后的描述
     */
    private String getDetail(String[] argNames, Object[] args, ServiceInvoker annotation) {

        Map<Object, Object> map = new HashMap<>(4);
        for (int i = 0; i < argNames.length; i++) {
            map.put(argNames[i], args[i]);
        }

        String detail = annotation.service();
        try {
            detail = "'" + "MethodName" + "'===>" + annotation.service();
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
            }
        } catch (Exception e) {
            throw e;
        }
        return detail;
    }
}

实现效果:

2019-09-24 16:03:40,093 [ WARN] c.h.k.e.annotation.ServiceInvokerLog - 【ServiceLog(createTime=2020-09-24 16:03:40, method=com.hanwe.aop.InvokerService.register, params=[{"cId":"*******************","clientIp":"127.0.0.1"}], description='MethodName'===>register, runTime=39, response={"code":-500,"message":"'cid' field is illegal"})】

提示:如果是类中方法中的方法,需要单独把这个子方法拿出来建一个通用的类来管理他们。

 

2.补充 自定义注解 Spring AOP解析

匹配符:访问修饰符(可省略) 返回值 包名.包名.包名.类名.⽅法名(参数列表)

package com.dahan.annoation;

import java.lang.annotation.*;

/**
 * 日志类注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface LogAnnoation {
    String description();

    String dateTime() default "19970101";
}
package com.dahan.aop;

import com.dahan.annoation.LogAnnoation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 记录日志
 *
 * @author csh
 */
@Aspect
@Component
public class LogAop {
    /**
     * 第⼀步:编写⼀个⽅法
     * 第⼆步:在⽅法使⽤@Pointcut注解
     * 第三步:给注解的value属性提供切⼊点表达式
     * 细节:
     * 1.在引⽤切⼊点表达式时,必须是⽅法名+(),例如"pointcut()"。
     * 2.在当前切⾯中使⽤,可以直接写⽅法名。在其他切⾯中使⽤必须是全限定⽅法名。
     */
    // @Pointcut("execution(* com.dahan.*.*(..))") 这种方式需要详细到方法
    @Pointcut("@annotation(com.dahan.annoation.LogAnnoation)")
    public void pointcut() {
    }


    /*@Before("pointcut()")
    public void beforePrintLog(JoinPoint jp) {
        Object[] args = jp.getArgs();
        System.out.println("前置通知:beforePrintLog,参数是:" + Arrays.toString(args));
    }

    @AfterReturning(pointcut = "pointcut()", returning = "ret")
    public void afterReturningPrintLog(Object ret) {
        System.out.println("后置通知:afterReturningPrintLog,参数是:" + ret);
    }

    @AfterThrowing(value = "pointcut()", throwing = "tw")
    public void afterThrowingPrintLog(Throwable tw) {
        System.out.println("异常通知:afterThrowingPrintLog,异常是:" + tw);
    }

    @After("pointcut()")
    public void afterPrintLog() {
        System.out.println("最终通知:afterPrintLog");
    }*/

    /**
     * 环绕通知
     *
     * @param pjp pjp
     * @return Object
     */
    @Around("pointcut()")
    public Object aroundPrintLog(ProceedingJoinPoint pjp) {
        //定义返回值
        Object result = null;
        try {
            //前置通知
            System.out.println("前置通知");

            //1.获取参数
            Object[] args = pjp.getArgs();
            MethodSignature signature = (MethodSignature) pjp.getSignature();
            Method method = signature.getMethod();
            String methodName = method.getName();
            System.out.println("methodName: " + methodName);
            LogAnnoation annotation = method.getAnnotation(LogAnnoation.class);
            String dateTime = annotation.dateTime();
            String description = annotation.description();
            System.out.println("dateTime: " + dateTime + "---description: " + description);

            //2.执⾏切⼊点⽅法
            result = pjp.proceed(args);

            //后置通知
            System.out.println("后置通知");
        } catch (Throwable e) {
            //异常通知
            System.out.println("异常通知");
            e.printStackTrace();
        } finally {
            //最终通知
            System.out.println("最终通知");
        }
        return result;
    }
}

 

posted @ 2020-09-24 16:14  土木转行的人才  阅读(126)  评论(0编辑  收藏  举报