我想创建一个异常处理程序,它将拦截我项目中的所有控制器。那有可能吗?看来我必须在每个控制器中放置一个处理程序方法。谢谢你的帮助。我有一个发送Json响应的弹簧控制器。因此,如果发生异常,我想发送一个可以从一个地方控制的错误响应。
(我在Spring 3.1中找到了实现它的方法,此答案的第二部分对此进行了描述)
请参见第16.11章处理 Spring Reference的异常
HandlerExceptionResolver
<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方法都以全局方式工作。
@ControllerAdvice
@ExceptionHandler
在 Spring 3.1 中没有@ControllerAdvice。但是只要稍加修改,就可以具有类似的功能。
关键是对工作方式的理解@ExceptionHandler。在Spring 3.1中有一个类ExceptionHandlerExceptionResolver。此类(在其超类的帮助下)实现接口HandlerExceptionResolver并负责调用@ExceptionHandler方法。
ExceptionHandlerExceptionResolver
该HandlerExceptionResolver接口只有一种方法:
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);`.
当请求由Spring3.x控制器方法处理时,该方法(由表示org.springframework.web.method.HandlerMethod)就是handler参数。
org.springframework.web.method.HandlerMethod
handler
的ExceptionHandlerExceptionResolver用途handler(HandlerMethod),以获得控制器类和扫描它用于与所注解的方法@ExceptionHandler。如果其中一个方法与例外(ex)相符,则会调用此方法以处理该例外。(否则null返回此信号是为了表示此异常解析器不承担任何责任)。
HandlerMethod
ex
null
第一个想法是实现一个HandlerExceptionResolver行为类似于的自己ExceptionHandlerExceptionResolver,但@ExceptionHandler与其在控制器类中进行搜索,不如在控制器类中进行搜索。缺点是,ExceptionHandlerExceptionResolver必须手动配置(复制(或子类),并且必须)所有不错的消息转换器,参数解析器和返回值处理程序(真正的配置,并且仅ExceptionHandlerExceptionResolver由spring自动完成)。所以我想到了另一个想法:
实现一个简单的HandlerExceptionResolver“将”异常“转发”到THE(已配置)ExceptionHandlerExceptionResolver,但将其修改后handler指向包含全局Exception处理程序的Bean(我称它们为Global,因为它们对所有控制器都起作用)。
这是实现: GlobalMethodHandlerExeptionResolver
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
fakeHanderMethod
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; } }
顺便说一句:您不需要注册,GlobalMethodHandlerExeptionResolver因为spring会自动注册所有HandlerExceptionResolver为异常解析器实现的bean 。如此简单<bean class="GlobalMethodHandlerExeptionResolver"/>就足够了。
<bean class="GlobalMethodHandlerExeptionResolver"/>