基于Spring AOP实现可控的请求日志保存,自定义注解

需求

  1. 对系统请求的敏感数据做记录
  2. 记录操作类型 如"更新","修改"
  3. 记录关键值
  4. 记录操作人

思路

基于AOP对controller方法进行操作,判断该controller方法是不是需要记录的操作类型;然后对方法参数进行筛选,记录参数中符合要求的关键值

实现

基于Spring boot aop做了实现

通过注解标注方法,参数,属性

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 用来判断该方法是否进行日志记录
 * Created by xuchonggao on 2017/2/21.
 */
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAble {
    String value();  //操作对象名称,操作类型 或者字段对应的名称
}

定义AOP类,进行操作


import com.jockiller.logable.annotion.LogAble;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Created by jockiller on 2017/2/25.
 */
@Aspect
@Component
public abstract class LogInterceptor {


    @Around("@annotation(logAble)")
    Object saveLog(ProceedingJoinPoint point, LogAble logAble) throws Throwable {
        StringBuffer accessContent = new StringBuffer();
        long userId = 0;
        HttpServletRequest request = null;
        Signature signature = point.getSignature();
        try {
            MethodSignature methodSignature = (MethodSignature) signature;
            Method targetMethod = methodSignature.getMethod();
            Annotation[][] paramAnnotions = targetMethod.getParameterAnnotations();
            String paramAnno = null;
            Class clazz;
            Object requestArg;
            LogAble classLog;
            for (int i = 0; i < point.getArgs().length; i++) {
                requestArg = point.getArgs()[i];
                paramAnno = isLogAbleParam(paramAnnotions, i);
                if (requestArg == null && paramAnno == null) {
                    continue;
                } else if (requestArg != null) {
                    clazz = requestArg.getClass();
                    if (requestArg instanceof HttpServletRequest) {
                        request = (HttpServletRequest) requestArg;
                        userId = getUserId(request);
                    } else if (clazz.isAnnotationPresent(LogAble.class)) {
                        classLog = (LogAble) clazz.getAnnotation(LogAble.class);
                        accessContent.append("--").append(classLog.value()).append("[");
                        for (Field field : clazz.getDeclaredFields()) {
                            if (field.isAnnotationPresent(LogAble.class)) {
                                LogAble fieldLog = (LogAble) field.getAnnotation(LogAble.class);
                                if (fieldLog != null) {
                                    field.setAccessible(true);
                                    Object val = field.get(requestArg);
                                    accessContent.append("--").append(fieldLog.value()).append(":").append(val == null ? null : val.toString());
                                }
                            }
                        }
                        accessContent.append("]");
                    } else if (!StringUtils.isEmpty(paramAnno)) {
                        accessContent.append("--").append(paramAnno).append('[').append(requestArg.toString()).append(']');
                    }
                } else {
                    accessContent.append("--").append(paramAnno).append('[').append(']');
                }
            }


            if (userId <= 0) {
                throw new RuntimeException("没有获取到用户信息,请确保controller方法有HttpServletRequest参数,并且用户已经登录");
            }
        } catch (Exception e) {
            //保证日志处理不影响正常业务
            //TODO 需要自己对异常进行处理
            if (e instanceof RuntimeException) {
                throw e;
            } else {
                System.out.println("处理操作日志出错");
            }
        }

        Object proceed = point.proceed();
        if (doSave(proceed)) {
            saveLog(accessContent, userId, getRequestIp(request));
        }

        return proceed;
    }

    /**
     * 判断参数是否被@LogAble修饰
     *
     * @param annotations
     * @param paramIndex
     * @return 返回LogAble的value
     */
    String isLogAbleParam(Annotation[][] annotations, int paramIndex) {
        assert annotations.length > paramIndex;
        String res = null;
        for (Annotation anno : annotations[paramIndex]) {
            if (anno instanceof LogAble) {
                res = ((LogAble) anno).value();
                break;
            }
        }
        return res;
    }

    /**
     * 获取操作人Id的方法
     *
     * @param request
     * @return
     */
    public abstract long getUserId(HttpServletRequest request);

    /**
     * 获取真实访问IP的方法
     *
     * @param request
     * @return
     */
    public abstract String getRequestIp(HttpServletRequest request);

    /**
     * 进行保存日志的方法
     *
     * @param saveparam
     */
    public abstract void saveLog(Object... saveparam);

    /**
     * 根据执行结果或者参数准备情况判断是否保存日志
     *
     * @param logObj
     * @return
     */
    public abstract boolean doSave(Object logObj);

}

demo

    @RequestMapping("/save")
    @LogAble("保存")
    public String saveTT(TestLog log, String abc, HttpServletRequest request,@LogAble("小白") String name) {
        return "aaa";
    }

package com.test.domain;

import com.jockiller.logable.annotion.LogAble;

/**
 * 用来测试的类
 * Created by jockiller on 2017/2/25.
 */
@LogAble("雷锋兔")
public class TestLog {
    @LogAble("名字")
    private String name;

    private String desc;

    public TestLog() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
}

总结

代码略显简陋,仅提供了基本实现

AOP是个好东西

码云地址

点我

转载于:https://my.oschina.net/razox/blog/846306

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值