aop+controllerAdvice+token验证+事物回滚+保存日志

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言 springBoot 开发

提示:这里可以添加本文要记录的大概内容:

参考了论坛大神的代码,写的不好请见谅,废话不多说,直接上代码


提示:以下是本篇文章正文内容,下面案例可供参考

一、AOP

先加入依赖

    <!--全局请求日志处理-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>

启动配置:@ComponentScan({“com.test”}),改成自己项目的包名

@SpringBootApplication
@EnableScheduling
@ComponentScan({"com.github.jackpanz.json","com.test"})
public class DingApplication {

    public static void main(String[] args) {
        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
        SpringApplication.run(DingApplication.class, args);
    }

    /*自定义过滤字段*/
    @Order(0)
    @Bean
    public JFilterHttpMessageConverter jFilterHttpMessageConverter(ObjectMapper objectMapper) {
        JFilterHttpMessageConverter messageConverter = new JFilterHttpMessageConverter(objectMapper);
        return messageConverter;
    }
}

aop工具

/**
 * 日志切面
 */
@Aspect
@Component
public class LogAspect {

    private Logger logger = LoggerFactory.getLogger(LogAspect.class);

	//日志记录
    @Autowired
    RequestLogService requestLogService;
    /*事物回滚*/
    @Autowired
    PlatformTransactionManager platformTransactionManager;
    @Autowired
    TransactionDefinition transactionDefinition;

    //统计请求的处理时间
    Long startTime = null;

	//controller包的路径
    @Pointcut("execution(public * com.test.controller.*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void deBefore(JoinPoint joinPoint) throws Throwable {
//        System.out.println("方法的开始执行.....");
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) throws Throwable {
        // 处理完请求,返回内容
//        System.out.println("方法的返回值 : " + ret.toString());
    }

    //后置异常通知,有@Around无法执行这个方法,
    @AfterThrowing(pointcut = "webLog()", throwing = "e")
    public void throwing(JoinPoint joinPoint, Exception e) {
//        System.out.println("方法异常时执行.....");
    }

    //后置最终通知,final增强,不管是抛出异常或者正常退出都会执行
    @After("webLog()")
    public void after(JoinPoint joinPoint) {
//        System.out.println("方法最后执行.....");
    }


    //环绕通知,环绕增强,相当于MethodInterceptor
    @Around("webLog()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
//        System.out.println("方法环绕start.....");
        startTime = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        //开启事务
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        Object o = null;
        Exception exception = null;
        try {
            //获取token并验证token
            validToken(request.getHeader("Authorization"), methodSignature.getMethod());
            //请求过程
            o = pjp.proceed();
//            System.out.println("方法环绕proceed,结果是 :" + o!=null?o.toString():"");
        } catch (Exception e) {
            //错误,事物回滚
            platformTransactionManager.rollback(transactionStatus);
            //抛出到全局统一异常,GlobalExceptionHandler
            exception = e;
            throw e;
        } finally {
            //保存请求日志
            RequestLogUtil.saveLog(request, response, pjp, startTime, exception);
        }
        //成功,提交事务
        platformTransactionManager.commit(transactionStatus);

        return o;
    }

    /**
     * 获取token并验证token
     */
    private void validToken(String token, Method method) {
        // 判断接口是否是token验证
        if (method.isAnnotationPresent(StaffLoginToken.class)) {
            StaffLoginToken staffLoginToken = method.getAnnotation(StaffLoginToken.class);
            if (staffLoginToken.required()) {
                // 执行认证
                if (token == null || token.length() != 32) {
                    throw new JwtException(401, "无token,请重新登录");
                }
                // 验证方法自己定义....
                if (验证失败就throw) {
                    throw new JwtException(401, "用户不存在,请重新登录");
                }
            }
        }
    }
}

二、统一异常处理

1.针对controller层的异常
2.还有一些错误是不进入aop处理的,这块我用的是Advice捕获异常,异常类型可以自己补充

/**
 * 统一异常处理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * aop抛出的异常,判断错误是否是已定义的已知错误,不是则由未知错误代替
     */
    @ExceptionHandler(value = Exception.class)
    public ResultJson<Object> exceptionController(Exception e) {
//        System.out.println("exceptionGet系统异常:" + e);
        ResultJson<Object> resultJson = new ResultJson<>();
        if (e instanceof JwtException) {
            JwtException exception = (JwtException) e;
            resultJson = resultJson.diy(exception.getStatusCode(), exception.getMessage());
        } else if (e instanceof DescribeException) {
            DescribeException exception = (DescribeException) e;
            resultJson = resultJson.diy(exception.getStatusCode(), exception.getMessage());
        } else if (e instanceof SocketException) {
            resultJson = resultJson.diy(500, "无网络连接");
        } else if (e instanceof SocketTimeoutException) {
            resultJson = resultJson.diy(500, "连接网络超时");
        } else if (e instanceof HttpException) {
            resultJson = resultJson.diy(500, "http错误");
        } else if (e instanceof NullPointerException) {
            resultJson = resultJson.diy(500, "空指针异常");
        } else if (e instanceof JsonSyntaxException) {
            resultJson = resultJson.diy(500, "Json数据解析异常");
        } else if (e instanceof ArrayIndexOutOfBoundsException) {
            resultJson = resultJson.diy(500, "数组下标越界");
        } else if (e instanceof ClassCastException) {
            resultJson = resultJson.diy(500, "数据类型转换错误");
        } else if (e instanceof ClientException) {
            resultJson = resultJson.diy(500, "人脸识别失败");
        } else if (e instanceof ArithmeticException) {
            resultJson = resultJson.diy(500, "算术运算异常");
        } else if (e instanceof ConstraintViolationException) {
            ConstraintViolationException exception = (ConstraintViolationException) e;
            Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
            List<String> list = new ArrayList<>();
            for (ConstraintViolation<?> constraintViolation : constraintViolations) {
                String messageTemplate = constraintViolation.getMessageTemplate();
                list.add(messageTemplate);
            }
            String message = list.stream().collect(Collectors.joining(","));
            resultJson = resultJson.diy(500, message);
        } else {
            resultJson = resultJson.diy(500, "系统错误");
        }
        return resultJson;
    }

    /**
     * 不进入controller层的异常捕获,保存记录,这里保存的记录信息没有aop详细
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class, MissingServletRequestParameterException.class, IllegalArgumentException.class})
    public ResultJson<Object> exceptionExceptController(Exception e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        HttpServletResponse response = attributes.getResponse();
        //保存请求日志
        RequestLogUtil.saveLog2(request, response, e);

        ResultJson<Object> resultJson = new ResultJson<>();
        String errmsg = "";
        String message = "";
        if (e instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) e;
//            MethodParameter parameter = exception.getParameter();
//            errmsg = "参数转换失败,方法:"+ Objects.requireNonNull(exception.getParameter().getMethod()).getName()
//                    +",期望参数类型:"+exception.getParameter().getParameterType()
//                    +",参数:"+exception.getName()
//                    +",信息:"+exception.getMessage();
            message = "参数类型转换异常";
        } else if (e instanceof MissingServletRequestParameterException) {
            message = "参数信息异常";
        } else if (e instanceof IllegalArgumentException) {
            message = "不合法的参数异常";
        } else {
            message = "未知错误";
        }
//        System.out.println("errmsg = " + errmsg);
        return resultJson.diy(400, message);
    }

}

三、请求接口

注意

1.接口要加public修饰,否则不能进入aop
2.不用加try…catch…
3.回滚不用加注解@Transactional
这些在aop里都已处理好了

    @ApiOperation(value = "测试异常回滚后保存日志")
    @PostMapping(value = "/test", produces = "application/json; charset=UTF-8")
    public ResultJson<Object> test() {
        ResultJson<Object> result = new ResultJson<>();
        Appellation log = new Appellation();
        log.setAppellationName("测试");
        log.setAddTime(DateUtil.getNowDatetime());
        appellationService.save(log);

        int s = 3/0;

        return result.success();
    }

四、日志保存

@Component
public class RequestLogUtil {

    public static RequestLogUtil util;

    @PostConstruct
    public void init() {
        util = this;
    }

    @Autowired
    private RequestLogService requestLogService;

    /**
     * aop-保存请求日志
     */
    public static void saveLog(HttpServletRequest request, HttpServletResponse response, ProceedingJoinPoint joinPoint, Long startTime, Exception e) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = methodSignature.getMethod();
        String requestUrl = request.getRequestURL().toString();
        String requestMethod = request.getMethod();
        String ip = WxPayBusiness.self().getIpAddr(request);
        String path = methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();
        // 记录下请求内容
//        System.out.println("请求URL = " + requestUrl);
//        System.out.println("请求方法 = " + requestMethod);
//        System.out.println("请求的IP = " + ip);
//        System.out.println("请求的包和方法名 = " + path);
        // 输出请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, String> stringMap = new HashMap<>();
        for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
            String key = stringEntry.getKey();
            String[] value = stringEntry.getValue();
            String valueStr = "";
            if (value != null && value.length > 0) {
                ArrayList<String> strings = Lists.newArrayList(value);
                valueStr = StringUtils.strip(strings.toString(), "[]");
            }
            stringMap.put(key, valueStr);
        }

        RequestLog log = new RequestLog();
        //获取操作
        ApiOperation requestMapping = method.getAnnotation(ApiOperation.class);
        //获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取请求的方法名
        String methodName = method.getName();
        if (requestMapping != null) {
            String value = requestMapping.value();
            log.setName(value);
        }
        log.setIp(ip);
        log.setMethod(requestMethod);
        log.setRequestUrl(requestUrl);
        log.setRequestPath(className + "-" + methodName);
//        log.setJsonResponse(JSONObject.toJSONString(ret));
        log.setJsonRequest(JSONObject.toJSONString(stringMap));
        if (startTime != null) {
            double l = System.currentTimeMillis() - startTime;
            log.setUserTime(DoubleUtil.div(l, 1000, 2));
        } else {
            log.setUserTime(0d);
        }
        if (e != null) {
            if (e instanceof JwtException) {
                JwtException jwtException = (JwtException) e;
                log.setHttpStatus(jwtException.getStatusCode());
            } else {
                log.setHttpStatus(500);
            }
            log.setThrowException(e.toString());
        } else {
            log.setHttpStatus(response != null ? response.getStatus() : 0);
        }
        log.setAddTime(DateUtil.getNowDatetime());
        util.requestLogService.save(log);
    }

    /**
     * ControllerAdvice-保存请求日志
     */
    public static void saveLog2(HttpServletRequest request, HttpServletResponse response, Exception e) {
        //获取切入点所在的方法
        String requestUrl = request.getRequestURL().toString();
        String requestMethod = request.getMethod();
        String pathInfo = request.getPathInfo();
        String ip = WxPayBusiness.self().getIpAddr(request);
        // 记录下请求内容
//        System.out.println("请求URL = " + requestUrl);
//        System.out.println("请求方法 = " + requestMethod);
//        System.out.println("请求的IP = " + ip);
//        System.out.println("请求的包和方法名 = " + path);
        // 输出请求参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, String> stringMap = new HashMap<>();
        for (Map.Entry<String, String[]> stringEntry : parameterMap.entrySet()) {
            String key = stringEntry.getKey();
            String[] value = stringEntry.getValue();
            String valueStr = "";
            if (value != null && value.length > 0) {
                ArrayList<String> strings = Lists.newArrayList(value);
                valueStr = StringUtils.strip(strings.toString(), "[]");
            }
            stringMap.put(key, valueStr);
        }

        RequestLog log = new RequestLog();
        //获取操作
        log.setName(null);
        log.setIp(ip);
        log.setMethod(requestMethod);
        log.setRequestUrl(requestUrl);
        log.setRequestPath(null);
//        log.setJsonResponse(JSONObject.toJSONString(ret));
        log.setJsonRequest(JSONObject.toJSONString(stringMap));
        log.setHttpStatus(400);
        log.setUserTime(0d);
        if (e != null) {
            log.setThrowException(e.toString());
        }
        log.setAddTime(DateUtil.getNowDatetime());
        util.requestLogService.save(log);
    }
}

总结

1.非controller异常开始没有思路搞了好久
2.回滚后的日志保存,看了好多文章还是没搞定,最后瞎搞搞出来了,哈哈哈…
这些基本就够用了,验证方法和统一返回类可以改成自己的

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bao122412

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值