SpringBoot切面访问

import com.rum.logger.exception.BusinessException;
import com.corporate.info.common.domain.SubscribeSystem;
import com.corporate.info.domain.InterfaceVisitRecord;
import com.corporate.info.service.InterfaceVisitRecordService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Base64Utils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import static com.corporate.info.domain.InterfaceVisitConstants.FAIL_STATUS;
import static com.corporate.info.domain.InterfaceVisitConstants.SUCCESS_STATUS;


/**
 * 统计接口访问记录处理切面
 */
@Aspect
@Slf4j
@Component
public class InterfaceVisitRecordAspect {

    private static final String SIGN = "sign";

    private static final String MATCH_PATTERN_NAME = "org.springframework.web.servlet.HandlerMapping.bestMatchingPattern";

    @Value("${server.servlet.context-path}")
    private String baseUrl;

    @Autowired
    private InterfaceVisitRecordService service;

    @Resource
    private ObjectMapper objectMapper;

    private ThreadLocal<InterfaceVisitRecord> recordEntityThreadLocal = new ThreadLocal<>();

    /**
     * 定义切点表达式:统计所有的controller的接口
     */
    @Pointcut("execution(* com.cxb.info.controller.*.*(..))")
    public void interfaceVisitRecord() {

    }

/**
     * 通知方法会将目标方法封装起来
     *
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("interfaceVisitRecord()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            InterfaceVisitRecord visitRecordEntity = new InterfaceVisitRecord();
            visitRecordEntity.setCreateTime(LocalDateTime.now());
            recordEntityThreadLocal.set(visitRecordEntity);
        } catch (Exception e) {
            log.error("接口访问统计doAround访问接口前异常,{}", e.getMessage());
        }
        // 获取访问的结果
        Object result = joinPoint.proceed();
        try {
            InterfaceVisitRecord visitRecordEntity = recordEntityThreadLocal.get();
            handleResultInfo();
            service.saveRecordToRedisByHour(visitRecordEntity);
            recordEntityThreadLocal.remove();
        } catch (Exception e) {
            log.error("接口访问统计doAround访问接口后异常,{}", e.getMessage());
        }
        return result;
    }

    /**
     * 异常返回通知,记录异常请求日志
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(pointcut = "interfaceVisitRecord()", throwing = "e")
    public void handleException(JoinPoint joinPoint, Throwable e) {
        try {
            // 每次都会先进入around然后有异常才会进入
            handleResultInfo();
            handelExceptionResultInfo(joinPoint, e);
            InterfaceVisitRecord visitRecordEntity = recordEntityThreadLocal.get();
            service.saveRecordToRedisByHour(visitRecordEntity);
            recordEntityThreadLocal.remove();
        } catch (Exception resultException) {
            log.error("接口访问统计切面保存日志异常,{}", resultException.getMessage());
        }
    }

/**
     * 处理接口返回记录的产品信息和请求的信息等
     *
     * @return
     */
    private void handleResultInfo() {
        try {
            InterfaceVisitRecord visitRecordEntity = recordEntityThreadLocal.get();
            //获取当前请求对象
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            String sign = request.getHeader(SIGN);
            SubscribeSystem subscribeSystem = objectMapper.readValue(Base64Utils.decodeFromString(sign), SubscribeSystem.class);
            visitRecordEntity.setProductId(subscribeSystem.getProductId());
            visitRecordEntity.setValidateWay(subscribeSystem.getValidateWay());
            visitRecordEntity.setBusinessType(subscribeSystem.getBusinessType());
            // 设置请求的方法
            visitRecordEntity.setRequestMethod(request.getMethod());
            String bestMatchingPattern = request.getAttribute(MATCH_PATTERN_NAME).toString();
            visitRecordEntity.setRequestOriginUri(baseUrl + bestMatchingPattern);
            String actualUri = StringUtils.isEmpty(request.getQueryString()) ? request.getRequestURI() : request.getRequestURI() + "?" + request.getQueryString();
            visitRecordEntity.setRequestActualUri(actualUri);
            LocalDateTime visitEndTime = LocalDateTime.now();
            // 计算时间差
            Duration between = Duration.between(visitRecordEntity.getCreateTime(), visitEndTime);
            visitRecordEntity.setConsumeTime((int) between.toMillis());
            visitRecordEntity.setEndTime(visitEndTime);
            // 默认设置为成功,异常会赋值1
            visitRecordEntity.setStatus(SUCCESS_STATUS);
            recordEntityThreadLocal.set(visitRecordEntity);
        } catch (Exception e) {
            log.error("接口访问统计切面保存日志handleResultInfo方法异常,{}", e.getMessage());
        }
    }

/**
     * 处理异常的请求参数信息和错误信息
     */
    public void handelExceptionResultInfo(JoinPoint joinPoint, Throwable e) {
        try {
            InterfaceVisitRecord visitRecordEntity = recordEntityThreadLocal.get();
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 失败状态
            visitRecordEntity.setStatus(FAIL_STATUS);
            // 异常信息
            if (e instanceof BusinessException) {
                BusinessException businessException = (BusinessException) e;
                String errorMessage = businessException.getErrorDetails().get(0).getErrorMessage();
                visitRecordEntity.setErrorMessage(errorMessage);
            } else {
                visitRecordEntity.setErrorMessage(e.getMessage());
            }
            // 请求参数
            String[] parameterNames = signature.getParameterNames();
            Object[] args = joinPoint.getArgs();
            Map<String, Object> paramMap = new HashMap<>();
            for (int i = 0; i < parameterNames.length; i++) {
                paramMap.put(parameterNames[i], args[i]);
            }
            visitRecordEntity.setRequestContent(paramMap.toString());
        } catch (Exception e1) {
            log.error("接口访问统计切面保存日志handelExceptionResultInfo方法异常,{}", e1.getMessage());
        }
    }

}


猜你喜欢

转载自blog.csdn.net/qq_33371766/article/details/114226756