`

spring boot 异常处理扩展(返回格式protofuf)

阅读更多

spring boot 提供了统一的异常处理机制,@ControllerAdvice, @ExceptionHandler两个注解,可以处理controller里抛出的异常,不要对每个controller方法进行try catch,如果你使用json或者返回固定的错误页面作为传输数据格式,普通的使用方法即可解决,由于我们服务端使用protobuf与客户端进行交互,每个controller方法的返回类型的泛型都是不固定的,于是做了一定的扩展,以适应每个controller的返回:

 

/**
 * 全局异常处理
 * Created by mxl on 2016/9/2.
 */
@ControllerAdvice
public class GlobalExceptionHandler {


    private static volatile Map<String, HandlerMapping> matchingBeans = null;

    private static volatile ApplicationContext context = null;

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

    /**
     * 获取map
     * @return
     */
    public static Map<String, HandlerMapping> getMatchingBeans (HttpServletRequest request) {
        if (matchingBeans != null) {
            return matchingBeans;
        }
        synchronized (GlobalExceptionHandler.class) {
            if (matchingBeans == null) {
                ApplicationContext context = getContext(request);
                matchingBeans =
                        BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
            }
            return matchingBeans;
        }
    }

    /**
     * 获取context
     * @param request
     * @return
     */
    public static ApplicationContext getContext (HttpServletRequest request) {
        if (context != null) {
            return context;
        }
        synchronized (GlobalExceptionHandler.class) {
            if (context == null) {
                context = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext());
            }
            return context;
        }
    }

    @ExceptionHandler(value = GenericException.class)
    public ResponseEntity<?> GenericExceptionHandler(HttpServletRequest request, GenericException genericException) {
        try {
            HandlerMethod handlerMethod = null;
            //根据request请求解析出目标方法
            Map<String, HandlerMapping> matchingBeans = getMatchingBeans(request);
            for (HandlerMapping hm : matchingBeans.values()) {
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    handlerMethod = (HandlerMethod)handler.getHandler();
                    break;
                }
            }
            //根据目标方法,根据反射,获取controller方法返回的protobuf对应的builder,并填装固定的异常返回格式
            MessageOrBuilder builder = getBuilder(getReturnType(handlerMethod));
            if (builder == null) {
                return null;
            }
            return createResult(builder, genericException);
        } catch (Exception ee) {
            ee.printStackTrace();
        }

        return null;
    }

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<?> defaultErrorHandler(HttpServletRequest request, Exception e) throws Exception {
        try {
            HandlerMethod handlerMethod = null;
            //根据request请求解析出目标方法
            Map<String, HandlerMapping> matchingBeans = getMatchingBeans(request);
            for (HandlerMapping hm : matchingBeans.values()) {
                HandlerExecutionChain handler = hm.getHandler(request);
                if (handler != null) {
                    handlerMethod = (HandlerMethod)handler.getHandler();
                    break;
                }
            }
            //根据目标方法,根据反射,获取controller方法返回的protobuf对应的builder,并填装固定的异常返回格式
            MessageOrBuilder builder = getBuilder(getReturnType(handlerMethod));
            return createResult(builder, new OperateFailureException("系统异常", Constants.Code.SERVER_ERROR));
        } catch (Exception ee) {
            ee.printStackTrace();
            logger.error(ee.getMessage());
        }

        return null;
    }

    //利用反射获取填充builder
    private ResponseEntity<?> createResult (MessageOrBuilder messageOrBuilder, GenericException e) {
        try {
            Method successMethod = messageOrBuilder.getClass().getDeclaredMethod("setSuccess", boolean.class);
            successMethod.invoke(messageOrBuilder, false);
            Method msgMethod = messageOrBuilder.getClass().getDeclaredMethod("setMsg", String.class);
            msgMethod.invoke(messageOrBuilder, e.getMessage());
            Method codeMethod = messageOrBuilder.getClass().getDeclaredMethod("setCode", int.class);
            codeMethod.invoke(messageOrBuilder, e.getErrorCode());
            Method builderMethod = messageOrBuilder.getClass().getDeclaredMethod("build");
            return ResponseEntity.ok(builderMethod.invoke(messageOrBuilder));
        } catch (Exception ee) {
            e.printStackTrace();
            logger.error(ee.getMessage());
        }
        return null;
    }

    /**
     * 获取builder
     * @param returnType
     * @return
     */
    public static MessageOrBuilder getBuilder (Type returnType) {
        try {
            Class returnClass = Class.forName(returnType.getTypeName());
            Method builderMethod = returnClass.getDeclaredMethod("newBuilder");
            return (MessageOrBuilder)builderMethod.invoke(returnClass);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 返回类型
     * @param handlerMethod
     * @return
     */
    public static Type getReturnType (HandlerMethod handlerMethod) {
        Type type = handlerMethod.getMethod().getAnnotatedReturnType().getType();
        if (type instanceof ParameterizedType)/* 如果是泛型类型 */{
            Type[] types = ((ParameterizedType) type)
                    .getActualTypeArguments();// 泛型类型列表
            return types[0];
        }
        return null;
    }

}

 我们controller里是这样做返回的:

 

  

    @RequestMapping(value = "creative/list", method = RequestMethod.POST)
    public ResponseEntity<FeedResponse> listCreative(RequestEntity<FeedRequest> requestEntity) {
        FeedResponse.Builder builder = FeedResponse.newBuilder();

        FeedRequest feed = requestEntity.getBody();
        PaginationParam paginationParam = new PaginationParam(feed.getStartCount(), feed.getMaxCount() + 1);
        SortParam sortParam;
        if (feed.hasOrderColumn() && feed.hasSort()) {
            sortParam = PagiSortUtils.createSortParam(feed.getOrderColumn(), feed.getSort());
        } else {
            sortParam = new SortParam("update_time", "desc");
        }
        List<Creative> creativeList = creativeService.findByUser(feed.getUid(), sortParam, paginationParam, PubStatus.DELETE.value);

        builder.addAllDataList(
          convertCreativeFeedList(creativeList.subList(0, Math.min(creativeList.size(), feed.getMaxCount())), true,
              null));
        builder.setSuccess(true).setCode(Constants.Code.OK).setHasMore(creativeList.size() > feed.getMaxCount());
        return ResponseEntity.ok(builder.build());
    }

 

 

   扩展的具体思路是, 异常捕捉方法中暴露了两个参数request, exception,扩展点在于如何获取每个controller方法返回值的泛型,以创建具体的protobuf返回到客户端, spring boot有一个SimpleMappingExceptionResolver可以扩展,重新此方法, 此方法暴露了HandlerMethod,但是返回格式固定为ModelAndView了,不符合我们的要求;使用主机的方式,去解析request,获取目标方法handlerMethod, 然后利用反射获取目标方法的返回值类型,然后创建返回值类型,做出返回值构建,返回

分享到:
评论

相关推荐

    spring boot 异常处理方案

    Spring Boot支持i18n,可以在处理异常时结合使用以提供多语言的错误信息。 通过研究和实践这个开源项目,开发者能够掌握Spring Boot中的异常处理机制,提升应用程序的健壮性和用户体验。了解并运用这些知识点,可以...

    Spring Boot统一异常处理类

    Spring Boot统一异常处理类,BaseResponse类就两个字段code和message。经测试,可以捕获所以异常,并返回指定json数据

    Spring Boot异常处理静止trace

    Spring Boot 提供了多种方式来处理异常,即使是在生产环境中也可以轻松地处理异常。 二、静止 trace 介绍 静止 trace 是一种特殊的异常处理机制,它可以帮助我们在应用程序中静止 trace 信息。静止 trace 信息可以...

    spring boot资料以及项目

    Spring Boot是Java开发领域中的一款热门框架,它简化了基于Spring的应用程序的初始设置和配置。这个压缩包包含了丰富的Spring Boot学习资料以及实际项目案例,是深入理解和掌握Spring Boot技术的宝贵资源。 首先,...

    Java异常介绍及Spring Boot统一异常处理

    Spring Boot 提供了统一的异常处理机制,通过使用 @ControllerAdvice 和 @ExceptionHandler 注解,可以实现对控制器层、Service 层、Dao 层以及 Spring 系统内定义的部分异常的统一处理。 在 Spring Boot 中,可以...

    Spring Boot整合Spring Batch,实现批处理

    Spring Boot以其简洁的配置和快速的启动能力深受开发者喜爱,而Spring Batch作为Spring框架的一部分,专注于批量处理任务,提供了强大的数据处理能力和事务管理功能。下面我们将深入探讨这个主题。 首先,**Spring ...

    最新Spring Boot Admin 官方参考指南-中文版-2.x

    例如,你可以创建自定义的Actuator端点或者扩展Spring Boot Admin的UI,以适应你的业务需求。Spring Boot Admin提供了丰富的API和事件监听机制,使得二次开发变得相对简单。 总的来说,Spring Boot Admin是一个强大...

    《Vue Spring Boot前后端分离开发实战》源码Vue+Spring Boot前后端分离开发实战教学课件(PPT)

    在现代Web应用开发中,Vue.js和Spring Boot的结合已经成为了一种常见的前后端分离架构模式。这本《Vue Spring Boot前后端分离开发实战》的源码提供了深入学习和实践这一技术栈的机会。以下是对其中涉及知识点的详细...

    后端框架Spring Boot:全面解析异常处理机制

    内容概要:本文详细介绍了Spring Boot的异常处理机制,包括...③在事务管理中有效处理异常。 其他说明:文章提供的实例代码和技术细节,可以帮助开发者在实际项目中更好地实现异常处理,提高系统的健壮性和用户体验。

    《Spring Boot企业级开发教程》配套资料

    这些问题可能涵盖Spring Boot的配置、API使用、异常处理、测试等多个方面。 最后,源码是实践环节的重要组成部分。"chapter01-chapter09"代表了从基础到进阶的9个章节的示例代码,每个章节可能对应一个特定的技术点...

    Spring boot 示例 官方 Demo

    spring-boot-helloWorld:spring-boot的helloWorld版本 spring-boot-mybaits-annotation:注解版本 spring-boot-mybaits-xml:xml配置版本 spring-boot-mybatis-mulidatasource:springboot+mybatis多数据源最简解决...

    Spring Boot Json 返回格式 + 全局异常处理 + 参数验证注解

    Spring Boot 统一返回结果集,内容包括在了 spring aop、spring boot 全局异常处理、自定义异常、注解开发

    spring boot 异常处理学习

    异常处理demo

    Spring Boot 2.X 实战教程.pdf

    本课程内容包括Spring简介、Spring Boot简介、安装JDK、安装Maven、第一个Spring Boot程序(使用Spring Initializr构建、Spring Boot代码讲解、安装Notepad++)、构建系统、代码、配置、三种方式运行程序、安装...

    Spring Boot自定义异常内容展示代码.zip

    Spring Boot自定义异常内容代码,用来自定义异常展示信息,添加需要展示的信息等,并满足自适应显示。浏览器访问出现异常返回 Hmtl页面,客户端访问出现异常返回 Json 数据。该 Demo 仅用作备份

    Spring Boot 全局异常处理整理.docx

    Spring Boot 全局异常处理整理

    Spring Cloud Gateway的全局异常处理

    Spring Cloud Gateway默认使用`DefaultErrorWebExceptionHandler`类来处理异常,该类继承自`AbstractErrorWebExceptionHandler`,并在内部使用了`ErrorAttributes`和`ResourceProperties`等组件来收集错误信息。...

    spring-boot-2.7.0.zip源码

    通过深入阅读和分析Spring Boot 2.7.0的源码,我们可以了解到Spring Boot是如何实现其核心特性的,以及如何利用Spring Framework进行扩展和定制。同时,这也有助于我们更好地利用Spring Boot进行微服务开发,提高...

    基于spring boot实现根据经纬度坐标返回所在行政区域

    Spring Boot提供了全局异常处理器,可以通过`@ControllerAdvice`和`@ExceptionHandler`注解来统一处理这些异常。 5. **安全考虑**:如果项目需要对外开放,那么安全性是必须要考虑的。Spring Boot提供了Spring ...

Global site tag (gtag.js) - Google Analytics