实现一个系统切面

开启切面

在启动类上加上注解@EnableAspectJAutoProxy

@EnableAspectJAutoProxy(proxyTargetClass=true)

编写切面类

  • 切面需要的信息存储在ThreadLocal里面
@Data
public class MethodRuntimeContext {
    /**
     * 开始时间
     */
    private Long beginTimestamp;
    /**
     * 结束时间
     */
    private Long endTimestamp;

    /**
     * 方法调用时间
     */
    private String actionTime;

    /**
     * 操作结果0失败1成功
     */
    private Integer result;
    /**
     * 调用地址
     */
    private String url;
    /**
     * 客户端ip
     */
    private String clientIp;
    /**
     * 请求参数
     */
    private String requestParams;

    /**
     * 如果有异常, 保存异常信息
     */
    private String errorMsg;

    /**
     * 调用的用户id
     */
    private Integer userId;
}
  • 切面实现类
@Aspect
@Component
@Slf4j
public class SystemLogAspect {
    /**
     * 调用开始时间
     */
    private final static ThreadLocal<MethodRuntimeContext> CONTEXT_THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 拦截controller层所有方法调用
     */
    @Pointcut("execution(* com.xxx.controller.api-Controller.*(..))")
    public void controllerMethodAspect() {
    }

    /**
     * 方法调用前逻辑
     */
    @Before("controllerMethodAspect()")
    public void doBefore(){
        MethodRuntimeContext context = new MethodRuntimeContext();
        long currentTime = System.currentTimeMillis();
        context.setBeginTimestamp(currentTime);
        context.setActionTime(DateUtil.date(currentTime).toString());
        context.setClientIp(RequestHeaderInfoThreadLocal.getRequestHeaderInfo().getClientIp());
        context.setUrl(RequestHeaderInfoThreadLocal.getRequestHeaderInfo().getUrl());
        context.setUserId(RequestHeaderInfoThreadLocal.getRequestHeaderInfo().getUserId());
        CONTEXT_THREAD_LOCAL.set(context);
    }

    @AfterReturning(value = "controllerMethodAspect()", returning = "result")
    public void doAfter(JoinPoint joinPoint, Object result) {
        Map<String, Object> paramMap = this.getFieldsMap(joinPoint);
        CONTEXT_THREAD_LOCAL.get().setRequestParams(JSONUtil.toJsonStr(paramMap));
        CONTEXT_THREAD_LOCAL.get().setEndTimestamp(System.currentTimeMillis());
        if(result instanceof JsonObject){
            if(((JsonObject) result).isSuccess()) {
                CONTEXT_THREAD_LOCAL.get().setResult(1);
            }else{
                CONTEXT_THREAD_LOCAL.get().setResult(0);
                CONTEXT_THREAD_LOCAL.get().setErrorMsg(((JsonObject) result).getMsg());
            }
        }
        try {
            systemLogService.addSystemLog(CONTEXT_THREAD_LOCAL.get());
        }catch (Exception ignored){
        }finally {
            CONTEXT_THREAD_LOCAL.remove();
        }
    }
    /**
     * 获取调用参数
     * @param joinPoint
     * @return
     */
    @SneakyThrows
    private Map<String, Object> getFieldsMap(JoinPoint joinPoint){
        String classType = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        // 参数值
        Object[] args = joinPoint.getArgs();
        Class<?>[] classes = new Class[args.length];
        for (int k = 0; k < args.length; k++) {
            // 对于接受参数中含有MultipartFile,ServletRequest,ServletResponse类型的特殊处理,我这里是直接返回了null。(如果不对这三种类型判断,会报异常)
            if (args[k] instanceof MultipartFile || args[k] instanceof ServletRequest || args[k] instanceof ServletResponse) {
                return new HashMap<>();
            }
            if (!args[k].getClass().isPrimitive()) {
                // 当方法参数是封装类型
                Class s = args[k].getClass();
                classes[k] = s == null ? args[k].getClass() : s;
            }
        }
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        // 获取指定的方法,第二个参数可以不传,但是为了防止有重载的现象,还是需要传入参数的类型
        Method method = Class.forName(classType).getMethod(methodName, classes);
        // 参数名
        String[] parameterNames = pnd.getParameterNames(method);
        // 通过map封装参数和参数值
        Map<String, Object> paramMap = new HashMap<>();
        if(parameterNames != null) {
            for (int i = 0; i < parameterNames.length; i++) {
                paramMap.put(parameterNames[i], args[i]);
            }
        }
        return paramMap;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值