我想创建一个异常处理程序,它将拦截我项目中的所有控制器。那有可能吗?看起来我必须在每个控制器中放置一个处理程序方法。谢谢你的帮助。我有一个发送 Json 响应的弹簧控制器。因此,如果发生异常,我想发送一个可以从一个地方控制的错误响应。
3 回答
(我找到了一种在 Spring 3.1 中实现它的方法,这在这个答案的第二部分中有描述)
见章节16.11 处理Spring 参考的异常
还有比使用更多的方法@ExceptionHandler
(见gouki 的回答)
- 您可以实现一个HandlerExceptionResolver(使用 servlet 而不是 portlet 包) - 这是某种全局 @ExceptionHandler
如果您没有特定的异常逻辑,而只有特定的视图,那么您可以使用SimpleMappingExceptionResolver,它至少是
HandlerExceptionResolver
您可以指定异常名称模式和视图(jsp)的实现,当抛出异常。例如:<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">dataAccessFailure</prop> <prop key=".TypeMismatchException">resourceNotFound</prop> <prop key=".AccessDeniedException">accessDenied</prop> </props> </property> </bean>
在Spring 3.2+中,可以用 注释一个类@ControllerAdvice
,该类中的所有@ExceptionHandler
方法都以全局方式工作。
在Spring 3.1中没有@ControllerAdvice
. 但只要稍加修改,就可以拥有类似的功能。
关键是对@ExceptionHandler
工作方式的理解。在 Spring 3.1 中有一个类ExceptionHandlerExceptionResolver
。此类实现(在其超类的帮助下)接口HandlerExceptionResolver
并负责调用@ExceptionHandler
方法。
该HandlerExceptionResolver
接口只有一个方法:
ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex);`.
当请求由 Spring 3.x 控制器方法处理时,此方法(由 表示org.springframework.web.method.HandlerMethod
)是handler
参数。
使用ExceptionHandlerExceptionResolver
( handler
)HandlerMethod
获取 Controller 类并扫描它以查找带有 注释的方法@ExceptionHandler
。如果其中一种方法与异常 ( ex
) 匹配,则调用此方法以处理异常。(否则null
返回以表明此异常解析器认为没有责任)。
第一个想法是实现一个自己HandlerExceptionResolver
的行为ExceptionHandlerExceptionResolver
,但不是@ExceptionHandler
在控制器类中搜索,而是在一个特殊的 bean 中搜索它们。缺点是,必须(复制(或子类ExceptionHandlerExceptionResolver
)并且必须)手动配置所有好的消息转换器、参数解析器和返回值处理程序(真正的配置ExceptionHandlerExceptionResolver
是由 spring 自动完成的)。于是我想出了另一个主意:
实现一个简单HandlerExceptionResolver
的将异常“转发”到 THE (already configured) ExceptionHandlerExceptionResolver
,但修改后handler
指向包含全局异常处理程序的 bean(我称它们为全局,因为它们为所有控制器工作)。
这是实现:GlobalMethodHandlerExeptionResolver
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
public class GlobalMethodHandlerExeptionResolver
implements HandlerExceptionResolver, Ordered {
@Override
public int getOrder() {
return -1; //
}
private ExceptionHandlerExceptionResolver realExceptionResolver;
private List<GlobalMethodExceptionResolverContainer> containers;
@Autowired
public GlobalMethodHandlerExeptionResolver(
ExceptionHandlerExceptionResolver realExceptionResolver,
List<GlobalMethodExceptionResolverContainer> containers) {
this.realExceptionResolver = realExceptionResolver;
this.containers = containers;
}
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
for (GlobalMethodExceptionResolverContainer container : this.containers) {
ModelAndView result = this.realExceptionResolver.resolveException(
request,
response,
handlerMethodPointingGlobalExceptionContainerBean(container),
ex);
if (result != null)
return result;
}
// we feel not responsible
return null;
}
protected HandlerMethod handlerMethodPointingGlobalExceptionContainerBean(
GlobalMethodExceptionResolverContainer container) {
try {
return new HandlerMethod(container,
GlobalMethodExceptionResolverContainer.class.
getMethod("fakeHanderMethod"));
} catch (NoSuchMethodException | SecurityException e) {
throw new RuntimeException(e);
}
}
}
全局处理程序必须实现这个接口(为了找到并实现fakeHanderMethod
用于handler
public interface GlobalMethodExceptionResolverContainer {
void fakeHanderMethod();
}
以及全局处理程序的示例:
@Component
public class JsonGlobalExceptionResolver
implements GlobalMethodExceptionResolverContainer {
@Override
public void fakeHanderMethod() {
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorDto handleMethodArgumentNotValidException(
MethodArgumentNotValidException validationException,
Locale locale) {
...
/* map validationException.getBindingResult().getFieldErrors()
* to ValidationErrorDto (custom class) */
return validationErrorDto;
}
}
顺便说一句:您不需要注册,因为 spring 会自动注册所有为异常解析器GlobalMethodHandlerExeptionResolver
实现的 bean 。HandlerExceptionResolver
所以一个简单<bean class="GlobalMethodHandlerExeptionResolver"/>
的就够了。
从 Spring 3.2 开始,您可以使用@ControllerAdvice注释。您可以在@ControllerAdvice 类中声明@ExceptionHandler方法,在这种情况下,它会处理来自所有控制器的@RequestMapping 方法的异常。
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(value=IOException.class)
public @ResponseBody String iOExceptionHandler(Exception ex){
//
//
}
// other exception handler methods
// ...
}
您可以在其中定义异常处理程序的抽象类。然后让你的控制器继承它。