java使用注解记录异常方法日志

一,背景

在开发过程,如订单创建逻辑,如果方法异常,需要记录相关的日志信息,比如一些重要的业务信息,及异常信息

二,实现技术方案

1,自定义注解

2,AOP拦截注解,后置异常通知

3,使用ThreadLocal,定义,异常单 全局变量,负责不同类的参数传递(应该是方法的参数传递,同一个方法多线程访问,记录线程级别访问数据)

三,代码

1,自定义注解

package com.smcv.yl.order.service.annotation;


import com.smcv.yl.order.service.constant.OrderExceptionType;

import java.lang.annotation.*;

/**
 * @author zjq
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OrderExceptionAnnotation {


    /**
     * 异常单类型
     * 可以不用指定,默认其他异常
     */
    OrderExceptionType orderException() default OrderExceptionType.OTHER_EXCEPTION;

    /**
     * 请求快照入参
     */
    String requestSnapshot() default "";

    /**
     * 运力订单号
     */
    String ylOrderNo() default "";

    /**
     * 第三方订单号
     */
    String outOrderNo() default "";

    /**
     * 订单来源
     */
    String sourceApp();

}

2.上下文参数传递类

package com.smcv.yl.order.service.service.other;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

/**
 *
 * 描述:异常单 全局变量,负责不同类的参数传递
 */
public class OrderThreadLocalContext {

    private static final ThreadLocal> MAP_THREAD_LOCAL = ThreadLocal.withInitial(HashMap::new);
    public static final String YL_ORDER_NO = "ylOrderNo";
    public static final String OUT_ORDER_NO = "outOrderNo";
    public static final String REQUEST_SNAPSHOT = "requestSnapshot";

    protected static ThreadLocal> getMapThreadLocal() {
        return MAP_THREAD_LOCAL;
    }

    public static Map getMap() {
        return MAP_THREAD_LOCAL.get();
    }

    public static void put(String key, String value) {
        getMap().put(key, value);
    }

    public static String get(String key) {
        return getMap().get(key);
    }

    public static void remove() {
        MAP_THREAD_LOCAL.remove();
    }

    public static void setYlOrderNo(String ylOrderNo) {
        setOrderNoAndRequestSnapshot(ylOrderNo, null, null);
    }

    public static void setOutOrderNo(String outOrderNo) {
        setOrderNoAndRequestSnapshot(null, outOrderNo, null);
    }

    public static void setRequestSnapshot(Object requestSnapshot) {
        setOrderNoAndRequestSnapshot(null, null, requestSnapshot);
    }

    public static void setOrderNoAndRequestSnapshot(String ylOrderNo, String outOrderNo, Object requestSnapshot) {
        try {
            if (StringUtils.isNotBlank(ylOrderNo)) {
                put(YL_ORDER_NO, ylOrderNo);
            }
            if (StringUtils.isNotBlank(outOrderNo)) {
                put(OUT_ORDER_NO, outOrderNo);
            }
            if (null != requestSnapshot) {
                put(REQUEST_SNAPSHOT, JSON.toJSONString(requestSnapshot));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.AOP类

package com.smcv.yl.order.service.aop;

import com.smcv.yl.order.service.service.async.AsyncOrderExceptionHandle;
import com.smcv.yl.order.service.service.other.OrderThreadLocalContext;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.expression.spel.standard.SpelExpressionParser;

/**
 * @author zjq
 */
@Order
@Aspect
@Configuration
@Slf4j
public class OrderExceptionAspect {
    public static final String YL_ORDER_NO = "ylOrderNo";
    public static final String OUT_ORDER_NO = "outOrderNo";
    public static final String REQUEST_SNAPSHOT = "requestSnapshot";

    @Autowired
    private AsyncOrderExceptionHandle asyncExceptionHandle;

    public OrderExceptionAspect() {
    }

    @Pointcut("@annotation(com.smcv.yl.order.service.annotation.OrderExceptionAnnotation)")
    public void orderExceptionAnnotation() {
    }


    /**
     * 后置异常通知
     */
    @AfterThrowing(value = "orderExceptionAnnotation()", throwing = "throwable")
    public void exceptionHandle(JoinPoint joinPoint, Throwable throwable) throws Exception {
        String ylOrderNo = OrderThreadLocalContext.get(YL_ORDER_NO);
        String outOrderNo = OrderThreadLocalContext.get(OUT_ORDER_NO);
        String s = OrderThreadLocalContext.get(REQUEST_SNAPSHOT);
        //这里异步处理防止异常影响主业务
        asyncExceptionHandle.asyncExceptionHandle(joinPoint, throwable, ylOrderNo, outOrderNo, s);
        //清除当前线程绑定的ThreadLocal,防止可能存在的内存泄露
        // 防止web服务线程复用导致前一个请求的副本变量被下一个线程所获取
        OrderThreadLocalContext.remove();
    }

    // 在目标方法返回结果时后执行(当方法抛出异常时,无法执行)。
    // 调用成功无异常请求最后调用此方法清除线程绑定的副本变量
    @AfterReturning(value = "orderExceptionAnnotation()")
    public void afterReturningMethod() {
        //清除当前线程绑定的ThreadLocal,防止可能存在的内存泄露
        // 防止web服务线程复用导致前一个请求的副本变量被下一个线程所获取
        OrderThreadLocalContext.remove();
    }


    @Bean
    @ConditionalOnMissingBean(SpelExpressionParser.class)
    public SpelExpressionParser spelExpressionParser() {
        return new SpelExpressionParser();
    }


}

4.业务方法

  @Override
    @OrderExceptionAnnotation(requestSnapshot = "#receiveOrder.orderInfo",sourceApp = Constant.WX)
    public OrderResponse commonCreateOrder(ReceiveOrder receiveOrder) {
        ESBOrderInfoReq orderInfo = JSON.parseObject(JSON.toJSONString(receiveOrder.getOrderInfo()), new TypeReference() {
        });
        ESBRoute route = orderInfo.getRoute();
        ESBOrderDTO esbOrder = orderInfo.getEsbOrder();
        //异常单业务逻辑,异常感知,全局变量输入
        OrderThreadLocalContext.setOutOrderNo(esbOrder.getOutOrderNo());

你可能感兴趣的:(java,开发语言)