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