java

spring boot - getReader() has already been called for this request

最近有这样一个想法,当程序出错时,将请求体上传到 sentry,至于 sentry 的集成,可以参考我以前写的博客:自建 sentry 并与 spring boot 集成,sentry 是一个开源的崩溃跟踪平台吧,类似Android开发中常用的 bugly 等。但是,我遇到了:java.lang.IllegalStateException: getReader() has already been called for this request 这样的错误。这个错误的原因是:ServletRequest 里的 getReader 和 getInputStream 两个方法只能被调用一次,且是二选一,不是每个都可调用一次,是两个加起来只能调用一次,对此,网上有太多的解决方案。而我今天要提出的方案,却并不是重写某些方法等,而是使用 aop,没错,就是切面编程啦。请看如下代码:

@Aspect
@Component
public class ServiceAspect {
    public static final String POINT = "execution(public * top.kpromise..mapper..*.*(..))";

    @Around(POINT)
    public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            joinPoint.proceed();
        } catch (Throwable e) {
            rollBack(joinPoint);
            throw new KpException(getArgs(joinPoint, joinPoint.getArgs()).toString(), e);
        }
        return result;
    }

    private void rollBack(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Annotation[] annotations = signature.getMethod().getAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof Transactional) {
                TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                break;
            }
        }
    }

    private StringBuilder getArgs(ProceedingJoinPoint joinPoint, Object[] args) {
        if (args.length <= 0) return new StringBuilder();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        StringBuilder stringBuilder = new StringBuilder();
        String[] names = signature.getParameterNames();
        int length = args.length;
        ObjectMapper mapper = new ObjectMapper();
        for (int i = 0; i < length; i++) {
            if (i > 0) {
                stringBuilder.append(",");
            }
            Object arg = args[i];
            if (arg == null) continue;
            try {
                String value = mapper.writeValueAsString(arg);
                stringBuilder.append(names[i]).append("=").append(value);
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        return stringBuilder;
    }
}

这里,使用了 Around 方法,然后又用到了 try catch,使用 try catch 后会导致 controller 里的 事物不回滚,此处,我进行了手动回滚。最后,getArgs 这个方法演示了我如何获取到入参。

你也可以不用再抛异常,直接调用 sentry 的方法进行上传,当然了,如果你喜欢,你也可以抛出异常,丢给 有 @RestControllerAdvice 注解的崩溃处理类来处理。

full-stack-trip

Share
Published by
full-stack-trip

Recent Posts

Android 自定义 View 入门

说来惭愧,工作数年,连基本的自…

4 年 ago

retrofit 同时支持 xml 和 json

retrofit 解析 jso…

4 年 ago

mysql - 存储过程 从入门到放弃

最近有个报表的需求,于是乎用了…

4 年 ago

奶嘴战略 - 你不得不知道的扎心真相(一)

一句:英雄枯骨无人问,戏子家事…

4 年 ago

acme.sh 的简单使用

acme.sh 是纯 shel…

4 年 ago

wrk -更现代化的http压测工具

wrk 是一款更现代化的 ht…

4 年 ago