zoukankan      html  css  js  c++  java
  • Spring MVC 处理异常的3种方式

    使用Spring MVC开发的博客网站时,遇到了如何处理业务层抛出的异常的问题,查阅到了spring官方博客-spring MVC中异常的处理,以下将会以登录模块为示例。

    愚蠢的处理方式

    处理异常遵循“早抛出,晚捕获"的原则,在controller中统一处理异常,调用业务逻辑service时使用try-catch包围。

    输入图片说明

    然而这样需要每个controller方法中会编写模版代码,自然Spring MVC的设计者也会想到这个问题!于是去查阅资料。

    优雅的解决方案

    Spring MVC提供的3类处理方式,实现在控制层controller的外围处理异常,大概示意图如下:

    输入图片说明

    • 基于异常类(自定义),即针对某类异常;
    • 基于控制器(controller),即针对某个控制器;
    • 全局异常处理;

    Spring MVC异常处理

    LoginController控制器中处理登录的方法login,代码如下:

    /**
     * 登录
     * @param username
     * @param password
     * @param session
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ResponseBody
    public LuoblogResult<Author> login(String username, String password, HttpSession session) {
        // 登录的业务逻辑
        // 当用户名或密码错误将抛出异常
        Author author = authorService.login(username, password);
        return new LuoblogResult<Author>(true, author);
    }
    

    为了和Spring异常处理产生对比,这里不使用任何异常处理,浏览器中输入不存在的用户,执行结果如下:

    输入图片说明

    注:作者名不存在,业务层会抛出BusinessException异常;

    1.基于自定异常类处理

    是时候让我们尝尝Spring异常处理这个香饽饽,只需要使用@ResponseState注解对自定义异常类进行标注,Spring在处理异常的时候会利用反射对该注解标记的异常特殊处理(个人推测):

    /**
     * 业务异常
     * Created by luokaiqiongmou on 2016/12/6.
     */
    @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "业务异常,伪装为错误请求")
    public class BusinessException extends RuntimeException{
        // 一些内容
    }
    

    现在再来登录一次,输入不存在的用户名,执行结果如下:

    输入图片说明

    巧妙的将原本的500错误,修改为400错误,那这有什么用了,这样你就可以控制对应异常发生时转化为其他的状态码,便于前端处理。

    2.基于控制器,处理异常

    现在我们可以针对某个控制器处理异常,只需要在控制器中增加一个异常处理方法,并使用@ExceptionHandler标注:

        @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "业务异常,针对控制器处理")
        @ExceptionHandler(BusinessException.class)
        public void conflict() {
            // 不做任何事或者可以做任何事
        }
    

    同样登录一次,结果如下:

    输入图片说明

    注:@ExceptionHandler标注的方法,方法签名灵活、多变。被@ResponseStatus注解的方法将会修改相应状态码,而使用@ResponseBody可以返回json格式的数据,再供前端处理。参考:示例

    3. 全局的异常处理

    针对某个控制器处理异常的方式会造成代码入侵

    方式一:使用注解

    现在创建一个专门处理异常的类,并添加@ControllerAdvice注解,如下:

    /**
     * 异常处理
     * Created by luokaiqiongmou on 2016/12/17.
     */
    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    
        @ExceptionHandler(BusinessException.class)
        @ResponseBody
        public LuoblogResult<Object> handle(BusinessException e) {
            logger.warn("GloabalExceptionHandler handing a Exception: " + e.getMessage());
            // 业务失败返回
            return new LuoblogResult<Object>(false, e.getDescription());
        }
    
    }
    

    再次登录,效果如下:
    输入图片说明

    方式二:实现HandlerExceptionResolver,并注入到spring容器中

    创建一个类实现HandlerExceptionResolver,并在配置文件中注入bean,参考:自定义异常处理器,以下方式未做测试

    Spring已经为我们预定义了一个处理异常的解析类SimpleMappingExceptionResolve,添加如下配置文件即可:

    <bean id="simpleMappingExceptionResolver" class=
         "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
          <map>
            <!-- key:异常类别(非全称), 视图名称 -->
             <entry key="DatabaseException" value="databaseError"/>
             <entry key="InvalidCreditCardException" value="creditCardError"/>
          </map>
        </property>
    
        <!-- 默认的错误处理页面,异常的名称 -->
        <property name="defaultErrorView" value="error"/>
        <property name="exceptionAttribute" value="ex"/>
            
        <!-- Name of logger to use to log exceptions. Unset by default, 
               so logging is disabled unless you set a value. -->
        <property name="warnLogCategory" value="example.MvcLogger"/>
      </bean>
    

    总结

    三种异常处理方式,主要涉及3个注解和1个接口,@ExceptionHandler标注的方法被定义为处理指定类型异常;@ResponseStatus标注的方法执行,会修改响应头中的状态码;Spring会把@ControllerAdvice的类内部使用@ExceptionHandler方法应用到所有的 @RequestMapping注解的方法上。

    • 基于异常的处理方式,第一种无法实现json数据返回
    • 基于控制器的方式
    • 全局的方式!
  • 相关阅读:
    c++中string类中的函数
    二进制
    快速幂
    substring
    hdu 4678
    扩展欧几里得算法
    欧几里得算法
    Floyd_Warshall(任意两点之间的最短路)
    带结构体的优先队列
    php获得远程信息到本地使用的3个函数:file_get_contents和curl函数和stream_get_contents
  • 原文地址:https://www.cnblogs.com/lkqm/p/6435130.html
Copyright © 2011-2022 走看看