使用控制器通知- SpringBoot(12)

控制器通知

  在编写代码时,需要对异常进行处理。进行异常处理的普通的代码是try-catch结构。但在开发业务时,只想关注业务正常的代码,对于catch语句中的捕获异常,希望交给异常捕获来处理,不单独在每个方法中编写。这样不仅可以减少冗余代码,还可以减少因忘记写catch而岀现错误的概率。Spring正好提供了一个非常方便的异常处理方案——控制器通知@ControllerAdvice或 @RestcontrollerAdvice),它将所有控制器作为一个切面,利用切面技术来实现。通过基于@ControllerAdvice或@RestControllerAdvice的注解可以对异常进行全局统一处理,默认对所有的Controller有效。如果要限定生效范围,则可以使用ContrallerAdvice支持的限定范围方式。

  • 按注解:@ControllerAdvice(annotations = RestController.class) 
  • 按包名:@ControllerAdvice("org.example.controller")
  • 按类型:@ControllerAdvice(assignableTypes = {Controllerinterface.class, Abstract- Controller.class})

这是ControllerAdvice进行统一异常处理的优点,它能够细粒度地控制该异常处理器针对娜些 Controller、包或类型有效。

可以利用这一特性在一个系统实现多个异常处理器,然后Controller可以有选择地决定使用哪 个,使得异常处理更加灵活,降低侵入性。

异常处理类会包含以下一个或多个方法。

  • @lnitBinder:对表单数据进行绑定,用于定义控制器参数绑定规则。如转换规则、格式化 等。可以通过这个注解的方法得到WebDataBinder对象,它在参数转换之前被执行。
  • @ModelAttribute:在控制器方法被执行前,对所有Controller的Model添加属性进行操作。
  • @ExceptionHandler:定义控制器发生异常后的操作,可以拦截所有控制器发生的异常。
  • @ControllerAdvice:统一异常处理,通过 @ExceptionHandler(value = class) 来指定捕获的异常。"@ControllerAdvice + @ExceptionHandle"可以处理除“404” 以外的运行异常。

  实例:自定义错误处理控制器

package com.itheima.controller;

import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("${server.error.path:${error.path:/error}}")
public class TestErrorController implements ErrorController {
    @Override
    public String getErrorPath() {
        return null;
    }
    @RequestMapping
    public Map<String,Object> handleError(){
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",404);
        map.put("msg","不存在");
        return map;
    }
    @RequestMapping("ok")
    @ResponseBody
    public Map<String,Object> noError(){
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",200);
        map.put("msg","正常");
        return map;
    }
}

启动项目,访问一个不存在的网址,则返回下方信息:

{"msg":"不存在",”code":404}

访问正确定义的映射"http://localhost:8080/error/ok",则返回下方正确信息:

{"msg":"正常","code":200}

  实例:自定义业务异常类

  (1)自定义异常类

  自定义异常类需要继承Exception (异常)类。这里继承RuntimeException,

package com.itheima.exception;

public class BusinessException extends RuntimeException {
    private Integer code;
    public BusinessException(int code, String msg) {
        super(msg);
        this.code = code;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
}

  (2)自定义全局捕获异常

package com.itheima.handler;

import com.itheima.exception.BusinessException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class CustomerBusinessExceptionHandler {
    @ResponseBody
    @ExceptionHandler(BusinessException.class)
    public Map<String,Object> businessExceptionHandler(BusinessException e){
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("code",e.getCode());
        map.put("message",e.getMessage());
        return map;
    }
}

     @ControllerAdvice:

   对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不止于此。ControllerAdvice拆开来就是Controller Advice,关于Advice,在Spring的AOP中,是用来封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ControllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行切面环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。

3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

   从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。

   @Component用于声明@ExceptionHandler 、 @InitBinder或@ModelAttribute方法的类的特化,以便在多个@Controller类之间共享。
   使用@ControllerAdvice注释的类可以显式声明为 Spring bean 或通过类路径扫描自动检测。所有此类 bean 都基于Ordered语义或@Order / @Priority声明进行排序, Ordered语义优先于@Order / @Priority声明。然后在运行时按该顺序应用@ControllerAdvice bean。但是请注意,实现PriorityOrdered的@ControllerAdvice bean 的优先级不高于实现Ordered的@ControllerAdvice bean。此外, Ordered不适用于作用域@ControllerAdvice bean — 例如,如果此类 bean 已配置为请求作用域或会话作用域 bean。为了处理异常, @ExceptionHandler将在第一个通知中选择一个匹配的异常处理程序方法。对于模型属性和数据绑定初始化,@ @ModelAttribute和@InitBinder方法将遵循@ControllerAdvice顺序。
   注意:对于@ExceptionHandler方法,在特定通知 bean 的处理程序方法中,根异常匹配将优于仅匹配当前异常的原因。但是,高优先级通知上的原因匹配仍将优先于低优先级通知 bean 上的任何匹配(无论是根级别还是原因级别)。因此,请在具有相应顺序的优先通知 bean 上声明您的主根异常映射。
默认情况下, @ControllerAdvice ControllerAdvice 中的方法全局应用于所有控制器。使用诸如annotations 、 basePackageClasses和basePackages (或其别名value )之类的选择器来定义目标控制器的更窄子集。如果声明了多个选择器,则应用布尔OR逻辑,这意味着所选控制器应至少匹配一个选择器。请注意,选择器检查是在运行时执行的,因此添加许多选择器可能会对性能产生负面影响并增加复杂性。

   @ExceptionHandler:

   用于处理特定处理程序类和/或处理程序方法中的异常的注释。
使用此注释注释的处理程序方法可以具有非常灵活的签名。它们可能具有以下类型的参数,按任意顺序:异常参数:声明为一般异常或更具体的异常。如果注释本身没有通过其value()缩小异常类型,这也可用作映射提示。请求和/或响应对象(通常来自 Servlet API)。您可以选择任何特定的请求/响应类型,例如javax.servlet.ServletRequest / javax.servlet.http.HttpServletRequest 。
会话对象:通常是javax.servlet.http.HttpSession 。这种类型的参数将强制存在相应的会话。因此,这样的论点永远不会是null 。请注意,会话访问可能不是线程安全的,尤其是在 Servlet 环境中:如果允许多个请求同时访问会话,请考虑将"synchronizeOnSession"标志切换为“true”。
   org.springframework.web.context.request.WebRequest或org.springframework.web.context.request.NativeWebRequest 。允许通用请求参数访问以及请求/会话属性访问,而不与本机 Servlet API 绑定。
java.util.Locale用于当前请求的语言环境(由可用的最具体的语言环境解析器确定,即在 Servlet 环境中配置的org.springframework.web.servlet.LocaleResolver )。
java.io.InputStream / java.io.Reader用于访问请求的内容。这将是 Servlet API 公开的原始 InputStream/Reader。
java.io.OutputStream / java.io.Writer用于生成响应的内容。这将是 Servlet API 公开的原始 OutputStream/Writer。
org.springframework.ui.Model作为从处理程序方法返回模型映射的替代方法。请注意,提供的模型未预先填充常规模型属性,因此始终为空,以便为特定于异常的视图准备模型。
处理程序方法支持以下返回类型:
一个ModelAndView对象(来自 Servlet MVC)。
一个org.springframework.ui.Model对象,视图名称通过org.springframework.web.servlet.RequestToViewNameTranslator隐式确定。
用于公开模型的java.util.Map对象,视图名称通过org.springframework.web.servlet.RequestToViewNameTranslator隐式确定。
一个org.springframework.web.servlet.View对象。
一个String值,它被解释为视图名称。
@ResponseBody注释方法(仅限 Servlet)来设置响应内容。返回值将使用消息转换器转换为响应流。
用于设置响应标头和内容的HttpEntity<?>或ResponseEntity<?>对象(仅限 Servlet)。 ResponseEntity 正文将使用消息转换器进行转换并写入响应流。
void如果方法本身处理响应(通过直接编写响应内容,为此目的声明javax.servlet.ServletResponse / javax.servlet.http.HttpServletResponse类型的参数)或者视图名称应该通过隐式确定org.springframework.web.servlet.RequestToViewNameTranslator (不在处理程序方法签名中声明响应参数)。
您可以将ExceptionHandler注释与@ResponseStatus结合使用以获取特定的 HTTP 错误状态。

  (3)测试自定义异常类

  创建控制器,以抛BusinessException的自定义异常

package com.itheima.controller;

import com.itheima.exception.BusinessException;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @RequestMapping("BusinessException")
    public String testBusinessExceptionStatus(@RequestParam("i") int i){
        if (i == 0){
            throw new BusinessException(600,"这是自定义异常");
        }
        return "success";
    }
}

  启动项目,访问 "http://localhost:8080/BusinessException?i=0" 测试异常处理情况,则抛出下方错误信息:

  {"code”:600,"message”:"这是自定义异常"}

  

posted @ 2022-07-17 23:50  不惹兔子  阅读(80)  评论(0编辑  收藏  举报