Controller层、Filter层、Interceptor层全局统一异常处理

SpringBoot为异常处理提供了很多优秀的方法,但是像我这种新手在处理异常时还是会觉得一头包,终于我痛定思痛,总结了一下统一异常处理的办法,让异常处理变得上流。

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_拦截器

本片文章较长,我先定义以下后面会用到的类

/**
* @author ht113
*
* 错误dto,就是从后端返回到前端的错误信息,下面这个作为例子比较简单
*/
@Data
public class ErrorInfo {
String datetime;
String msg;
String url;
HttpStatus httpStatus;

public ErrorInfo(String datetime, String message, String url, HttpStatus httpStatus) {
this.datetime = datetime;
this.msg = message;
this.url = url;
this.httpStatus = httpStatus;
}
}

自定义抽象异常类

/**
* @author ht113
*
*
* 基本抽象类,所以我自定义的异常类都继承自此方法
*/
public abstract class AbstractBaseException extends Exception{
private ErrorInfo errorInfo;

public AbstractBaseException(String msg){
super(msg);
}

public AbstractBaseException(String msg, Throwable cause){
super(msg, cause);
}

public void setErrorInfo(ErrorInfo errorInfo){
this.errorInfo = errorInfo;
}

//这是唯一的抽象方法,也是子类必须实现的方法,以此达到使自定义异常对应到不同的http状态
@NonNull
public abstract HttpStatus getHttpStatus();

}

以下两个是自定义的异常类,对应的http状态分别为​ ​NOT_FOUND​ ​​和​ ​INTERNAL_SERVER_ERROR​ ​​,即​ ​404​ ​​和​ ​500​

public class MyExceptionA extends AbstractBaseException {

public MyExceptionA(String msg) {
super(msg);
}

public MyExceptionA(String msg, Throwable cause){
super(msg, cause);
}

@Override
public HttpStatus getHttpStatus() {
return HttpStatus.NOT_FOUND;
}
}

public class MyExceptionB extends AbstractBaseException {

public MyExceptionB(String msg) {
super(msg);
}

public MyExceptionB(String msg, Throwable cause){
super(msg, cause);
}

@Override
public HttpStatus getHttpStatus() {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
}

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_exception_02

1.一般Controller层的统一异常处理

@ExceptionHandler

在Spring中,我们可以通过对Controller中的方法添加​ ​@ExceptionHandler​ ​注释来实现对同一个Controller中路由方法抛出的异常进行处理,来达到我们想要的异常处理效果

/**
* @author ht113
*/
@RestController
public class HelloController {

@RequestMapping("/")
public String hello() throws AbstractBaseException{
throw new MyExceptionA("exception A test");
}

//需要捕获的异常的类
@ExceptionHandler(MyExceptionA.class)
public String exceptionHandlerA(){
return "exception A caught successfully";
}

}

处理效果:

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_拦截器_03

是个好方法,但是有个致命缺点,就是处理方法不能在Controller间共享,如果我在另一个Controller中抛出自定义异常就要重新实现一次,太拉垮了。

/**
* @author ht113
*/
@RestController
public class HelloController2 {

@RequestMapping("/hello2")
public String hello2() throws AbstractBaseException {
throw new MyExceptionA("exception A test");
}

}

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_spring_04

@ControllerAdvice

通过对Controller添加注释​ ​@ControllerAdvice​ ​​可以实现Controller层的全局异常处理,处理方法和​ ​@ExceptionHandler​ ​相同,但是可以在Controller中共享,实例如下:

/**
* @author ht113
*/
@ControllerAdvice
public class MyHandler {

@ExceptionHandler(AbstractBaseException.class)
public ResponseEntity<ErrorInfo> handler(HttpServletRequest request, AbstractBaseException e){
return ResponseEntity.status(e.getHttpStatus()).body(
new ErrorInfo(new Date().toString(), e.getMessage(),
request.getRequestURL().toString(), e.getHttpStatus()));
}

}

测试类

/**
* @author ht113
*/
@RestController
public class HelloController {

@RequestMapping("/1/testA")
public void hello() throws AbstractBaseException{
throw new MyExceptionA("exception A test from Controller1");
}

@RequestMapping("/1/testB")
public void hello1() throws AbstractBaseException{
throw new MyExceptionB("exception B test from Controller1");
}

}

/**
* @author ht113
*/
@RestController
public class HelloController2 {

@RequestMapping("/2/testA")
public void hello() throws AbstractBaseException {
throw new MyExceptionA("exception A test from Controller2");
}

@RequestMapping("/2/testB")
public void hello1() throws AbstractBaseException{
throw new MyExceptionB("exception B test from Controller2");
}

}

测试结果

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_spring_05
Spring中Controller层、Filter层、Interceptor层全局统一异常处理_exception_06
Spring中Controller层、Filter层、Interceptor层全局统一异常处理_异常处理_07
Spring中Controller层、Filter层、Interceptor层全局统一异常处理_exception_08
我们所有的自定义异常,只要继承自​​ ​AbstractBaseException​ ​​都可以被​ ​hander()​ ​​方法捕获,进行统一异常处理,当然也可以根据异常的不同进行特定的处理,只需要在​ ​MyHandler​ ​类中添加新的处理方法即可

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_exception_09

2.Filter层、Interceptor层异常统一处理

过滤器Filter和过滤器Interceptor拦截器的使用总是后端开发不可避免的一部分,有时候我们需要在过滤器或拦截器中进行逻辑处理,抛出异常,那么这里抛出的异常可以被​ ​@ControllerAdvice​ ​注释的异常处理类捕获吗?答案是Filter不会,而Interceptor可以,Filter是基于Servlet框架编写的,Interceptor是基于SpringMVC框架的,SpingMVC不会对Filter中的异常统一处理,所以这里被抛出的异常不能被捕获,那我们如何让不能被捕获的异常被被捕获呢?

答案有两个:

  1. 直接在​ ​doFilterInternal()​ ​中处理异常,直接return给客户端
  2. 通过​ ​HandlerExceptionResolver​ ​主动对异常进行捕获(当然了前面的Controller层的全局处理也可以用这种方式)

本例选择方法2,便于实现真正的统一。

Filter、Interception、Controller处理流程:

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_异常处理_10

用上面提到的testA进行测试,结果如下( 如果Controller中抛出异常,那么postHandler不会调用 ):

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_异常处理_11

HandlerExceptionResolver类图:

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_exception_12

如何使用​ ​HandlerExceptionResolver​ ​?

  1. 如果自定义异常类,考虑加上​ ​ResponseStatus​ ​ 注解;
  2. 对于没有​ ​ResponseStatus​ ​​注解的异常,可以通过使用​ ​ExceptionHandler + ControllerAdvice​ ​​注解,或者通过配置​ ​SimpleMappingExceptionResolver​ ​,来为整个Web应用提供统一的异常处理。
  3. 如果应用中有些异常处理方式,只针对特定的Controller使用,那么在这个Controller中使用​ ​ExceptionHandler​ ​ 注解。
  4. 不要使用过多的异常处理方式,不然的话,维护起来会很苦恼,因为异常的处理分散在很多不同的地方。

Filter中捕获异常

/**
* @author ht113
*/
@Component
public class MyFilter extends OncePerRequestFilter {

@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver handlerExceptionResolver;

@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
System.out.println("==============i'm in filter===================");
handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse,
null,new MyExceptionA("exception A test in filter"));
return;//捕获异常后就直接推出了
//filterChain.doFilter(httpServletRequest, httpServletResponse);
//System.out.println("===============i'm out filter===================");
}
}

用的是MyExceptionA,结果如下

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_拦截器_13
Spring中Controller层、Filter层、Interceptor层全局统一异常处理_拦截器_14

Interceptor中捕获异常

/**
* @author ht113
*/
public class MyInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=============i'm in interceptor============");
throw new MyExceptionA("exception A test in interceptor");
//return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=============i'm out interceptor============");
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=============i'm out interceptor============");
}
}

由于​ ​ExceptionHandler​ ​可以直接捕获Interceptor中的异常,所以在Interceptor中自定义异常和Controller中一样使用就可以。

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_异常处理_15

由于​ ​ExceptionHandler​ ​可以直接捕获Interceptor中的异常,所以在Interceptor中自定义异常和Controller中一样使用就可以。

Spring中Controller层、Filter层、Interceptor层全局统一异常处理_过滤器_16

OVER!

java点名器 点名器代码vb

用vb实现点名程序主要是随机变量的产生和数据的读取和存储以及计时器程序的设计,读取的文件命名为data.txt,书写格式为第一行为总人数下面的每行为一个人名,在应用时最好把data文件和程序文件放在一个目录,或者更改程序中的路径代码如下所示:Private Sub Command1_Click() Timer1.Enabled = True ' 点击