在Servlet的Filter中使用自定义异常通常需要一些特别的处理,因为过滤器是在请求到达Servlet之前运行的,所以过滤器中抛出的异常不会被Spring的@ControllerAdvice全局异常处理器捕获。但是,你可以通过HandlerExceptionResolver来手动触发全局异常处理。
具体来说,你可以在过滤器中捕获异常,然后使用HandlerExceptionResolver的resolveException方法来处理这些异常。这样,你就可以将过滤器中的异常转发给全局异常处理器,从而实现统一的异常处理。
例如,你可以创建一个过滤器,如下所示:
java
@Component
public class MyFilter implements Filter {
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
handlerExceptionResolver.resolveException((HttpServletRequest) request, (HttpServletResponse) response, null, e);
}
}
}
在这个过滤器中,如果doFilter方法抛出异常,它会被捕获并使用HandlerExceptionResolver来处理。这样,你就可以在全局异常处理器中定义如何处理这些异常了。
这种方法允许你在过滤器中使用自定义异常,并且可以利用Spring的异常处理机制来统一处理这些异常。这样,你就可以在过滤器中进行一些预处理工作,比如验证用户权限、检查请求参数等,如果发现问题就抛出自定义异常,然后在全局异常处理器中统一处理这些异常,返回给用户统一的错误响应。
下面是一个例子说明
- 定义自定义异常类 GoodsException
首先,我们定义一个GoodsException类,用于商品业务中的异常处理。
java
public class GoodsException extends RuntimeException {
public GoodsException(String message) {
super(message);
}
public GoodsException(String message, Throwable cause) {
super(message, cause);
}
}
- 创建全局异常处理器 GlobalExceptionHandler
接下来,我们创建一个全局异常处理器,用于处理GoodsException以及其他异常。
java
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(GoodsException.class)
@ResponseBody
public ResponseEntity<Object> handleGoodsException(GoodsException ex, WebRequest request) {
// 可以根据异常类型定义不同的错误响应
Map<String, Object> body = new HashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.BAD_REQUEST.value());
body.put("error", "Bad Request");
body.put("message", ex.getMessage());
body.put("path", request.getPathInfo());
return new ResponseEntity<>(body, HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
// 通用异常处理
Map<String, Object> body = new HashMap<>();
body.put("timestamp", LocalDateTime.now());
body.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
body.put("error", "Internal Server Error");
body.put("message", ex.getMessage());
body.put("path", request.getPathInfo());
return new ResponseEntity<>(body, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
- 创建过滤器 GoodsFilter
然后,我们创建一个过滤器GoodsFilter,在过滤器中抛出GoodsException异常,并使用HandlerExceptionResolver来处理这个异常。
java
@Component
public class GoodsFilter implements Filter {
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
try {
// 业务逻辑检查,如果发现问题则抛出GoodsException
boolean isValid = checkGoods(httpRequest);
if (!isValid) {
throw new GoodsException("商品验证失败");
}
chain.doFilter(request, response);
} catch (GoodsException ex) {
handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex);
} catch (Exception ex) {
handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex);
}
}
private boolean checkGoods(HttpServletRequest request) {
// 这里添加检查商品的逻辑
// 例如,检查请求参数中的goodsId是否有效
String goodsId = request.getParameter("goodsId");
return "123".equals(goodsId); // 假设商品ID为123时认为是有效的
}
}
- 配置过滤器
最后,确保你的过滤器被Spring容器管理。如果你使用的是Spring Boot,可以通过@WebFilter注解自动注册过滤器,或者在配置类中使用FilterRegistrationBean来注册。
java
@Configuration
public class FilterConfig {
@Autowired
private GoodsFilter goodsFilter;
@Bean
public FilterRegistrationBean<GoodsFilter> goodsFilterRegistration() {
FilterRegistrationBean<GoodsFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(goodsFilter);
registration.addUrlPatterns("/goods/*");
return registration;
}
}
这样,当请求匹配到/goods/*路径时,GoodsFilter过滤器会被调用。如果过滤器中的checkGoods方法发现商品验证失败,它会抛出GoodsException异常。这个异常会被HandlerExceptionResolver捕获,并由GlobalExceptionHandler中的handleGoodsException方法处理,最终返回一个统一的错误响应给客户端。
原理解析
在 Servlet 的 Filter 中抛出自定义异常 MyException 时,如果不调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex),异常处理逻辑不会自动触发,因为 Filter 和 Spring MVC 的异常处理机制是分开的。
细节解释
Filter 和 Spring MVC 的分离:
Filter 是在 Servlet 容器层面执行的,而 Spring MVC 的异常处理机制(如 HandlerExceptionResolver)是在 Spring MVC 框架层面执行的。
当异常在 Filter 中抛出时,Servlet 容器并不知道如何处理这个异常,因为它不属于 Spring MVC 的异常处理机制。
HandlerExceptionResolver 的作用:
HandlerExceptionResolver 是 Spring MVC 框架中的一个接口,用于处理控制器(Controller)中抛出的异常。
通过调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex),你可以手动将异常交给 Spring MVC 的异常处理机制来处理。
手动触发异常处理:
在 Filter 中捕获到异常后,调用 handlerExceptionResolver.resolveException(httpRequest, httpResponse, null, ex) 可以将异常传递给 Spring MVC 的异常处理机制。
这样,Spring MVC 就可以使用注册的 HandlerExceptionResolver 来处理异常,并返回自定义的 ExceptionResponse。
总结
关键就是HandlerExceptionResolver 这个接口,它有很多实现类,不同的实现类处理不同的异常处理实现
SimpleMappingExceptionResolver:通过配置异常类和视图名称的映射关系,将异常映射到特定的视图。
DefaultHandlerExceptionResolver:处理 Spring MVC 框架定义的标准异常,如 HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException 等。
ResponseStatusExceptionResolver:根据异常类上标注的 @ResponseStatus 注解来设置响应状态码。
ExceptionHandlerExceptionResolver:处理控制器中使用 @ExceptionHandler 注解定义的异常处理方法。