Spring MVC
2018-07-02 14:18:28 0 举报
AI智能生成
Spring MVC 请求流程和异常解析
作者其他创作
大纲/内容
异常处理
描述
所有用于处理在请求映射和请求处理过程中抛出的异常的类,都要实现HandlerExceptionResolver接口
AbstractHandlerExceptionResolver实现该接口和Orderd接口,是HandlerExceptionResolver类的实现的基类
ResponseStatusExceptionResolver等具体的异常处理类均在AbstractHandlerExceptionResolver之上,实现了具体的异常处理方式
一个基于Spring MVC的Web应用程序中,可以存在多个实现了HandlerExceptionResolver的异常处理类
他们的执行顺序,由其order属性决定
order值越小,越是优先执行
在执行到第一个返回不是null的ModelAndView的Resolver时,不再执行后续的尚未执行的Resolver的异常处理方法
Spring MVC提供的异常处理类
DefaultHandlerExceptionResolver
HandlerExceptionResolver接口的默认实现
基本上是Spring MVC内部使用,用来处理Spring定义的各种标准异常,将其转化为相对应的HTTP Status Code
处理的异常类型
handleNoSuchRequestHandlingMethod
handleHttpRequestMethodNotSupported
handleHttpMediaTypeNotSupported
handleMissingServletRequestParameter
handleServletRequestBindingException
handleTypeMismatch
handleHttpMessageNotReadable
handleHttpMessageNotWritable
handleMethodArgumentNotValidException
handleMissingServletRequestParameter
handleMissingServletRequestPartException
handleBindException
ResponseStatusExceptionResolver
用来支持ResponseStatus的使用
处理使用了ResponseStatus注解的异常,根据注解的内容,返回相应的HTTP Status Code和内容给客户端
使用
Web应用程序中配置ResponseStatusExceptionResolver
使用ResponseStatus注解来注解我们自己编写的异常类
在Controller中抛出该异常类
示例代码
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such Order") // 404
public class OrderNotFoundException extends RuntimeException {
// ...
}
public class OrderNotFoundException extends RuntimeException {
// ...
}
AnnotationMethodHandlerExceptionResolver
用来支持ExceptionHandler注解
示例代码
@Controller
public class ExceptionHandlingController {
// @RequestHandler methods
...
// 以下是异常处理方法
// 将DataIntegrityViolationException转化为Http Status Code为409的响应
@ResponseStatus(value=HttpStatus.CONFLICT, reason="Data integrity violation") // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// 针对SQLException和DataAccessException返回视图databaseError
@ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed to
// the view-resolver(s) in usual way.
// Note that the exception is _not_ available to this view (it is not added to
// the model) but see "Extending ExceptionHandlerExceptionResolver" below.
return "databaseError";
}
// 创建ModleAndView,将异常和请求的信息放入到Model中,指定视图名字,并返回该ModleAndView
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) {
logger.error("Request: " + req.getRequestURL() + " raised " + exception);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
public class ExceptionHandlingController {
// @RequestHandler methods
...
// 以下是异常处理方法
// 将DataIntegrityViolationException转化为Http Status Code为409的响应
@ResponseStatus(value=HttpStatus.CONFLICT, reason="Data integrity violation") // 409
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// 针对SQLException和DataAccessException返回视图databaseError
@ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed to
// the view-resolver(s) in usual way.
// Note that the exception is _not_ available to this view (it is not added to
// the model) but see "Extending ExceptionHandlerExceptionResolver" below.
return "databaseError";
}
// 创建ModleAndView,将异常和请求的信息放入到Model中,指定视图名字,并返回该ModleAndView
@ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) {
logger.error("Request: " + req.getRequestURL() + " raised " + exception);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", exception);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
如果需要使用ExceptionHandler来处理全局的Exception,则需要使用ControllerAdvice注解
示例代码
@ControllerAdvice
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// 如果异常使用了ResponseStatus注解,那么重新抛出该异常,Spring框架会处理该异常。
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
// 否则创建ModleAndView,处理该异常。
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
class GlobalDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error";
@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// 如果异常使用了ResponseStatus注解,那么重新抛出该异常,Spring框架会处理该异常。
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
// 否则创建ModleAndView,处理该异常。
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
return mav;
}
}
3.2开始不再推荐使用,使用ExceptionHandlerExceptionResolver代替
ExceptionHandlerExceptionResolver
同AnnotationMethodHandlerExceptionResolver,3.2开始推荐使用
SimpleMappingExceptionResolver
提供了将异常映射为视图的能力,高度可定制化
根据异常的类型,将异常映射到视图
可以为不符合处理条件没有被处理的异常,指定一个默认的错误返回
处理异常时,记录log信息
指定需要添加到Modle中的Exception属性,从而在视图中展示该属性
示例代码
@Configuration
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");
mappings.setProperty("InvalidCreditCardException", "creditCardError");
r.setExceptionMappings(mappings); // 默认为空
r.setDefaultErrorView("error"); // 默认没有
r.setExceptionAttribute("ex");
r.setWarnLogCategory("example.MvcLogger");
return r;
}
...
}
@EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("DatabaseException", "databaseError");
mappings.setProperty("InvalidCreditCardException", "creditCardError");
r.setExceptionMappings(mappings); // 默认为空
r.setDefaultErrorView("error"); // 默认没有
r.setExceptionAttribute("ex");
r.setWarnLogCategory("example.MvcLogger");
return r;
}
...
}
自定义ExceptionResolver
通过继承SimpleMappingExceptionResolver来定制Mapping的方式和能力
也可以直接继承AbstractHandlerExceptionResolver来实现其它类型的异常处理类
异常处理Bean的加载方式
Spring MVC有两种加载异常处理类的方式
一种是根据类型,这种情况下,会加载ApplicationContext下所有实现了ExceptionResolver接口的bean,并根据其order属性排序,依次调用
一种是根据名字,这种情况下会加载ApplicationContext下,名字为handlerExceptionResolver的bean
不管使用那种加载方式,如果在ApplicationContext中没有找到异常处理bean,那么Spring MVC会加载默认的异常处理bean
默认的异常处理bean定义在DispatcherServlet.properties中
原理
Spring MVC把请求映射和处理过程放到try catch中,捕获到异常后,使用异常处理bean进行处理
所有异常处理bean按照order属性排序,在处理过程中,遇到第一个成功处理异常的异常处理bean之后,不再调用后续的异常处理bean
使用建议
如果自定义异常类,考虑加上ResponseStatus注解
对于没有ResponseStatus注解的异常,可以通过使用ExceptionHandler+ControllerAdvice注解,或者通过配置SimpleMappingExceptionResolver,来为整个Web应用提供统一的异常处理
如果应用中有些异常处理方式,只针对特定的Controller使用,那么在这个Controller中使用ExceptionHandler注解
不要使用过多的异常处理方式,不然的话,维护起来会很苦恼,因为异常的处理分散在很多不同的地方
DispatcherServlet(实现类)
初始化各个功能,如异常处理、视图处理、请求映射处理等
继承自:FrameworkServlet(抽象类)
继承自:HttpServletBean(抽象类)
继承自:HttpSerlvet(抽象类)
抽象类
JDK提供
模板方法设计模式
初始化web.xml中的参数,如init-param标签中的参数
将Servlet与Spring容器上下文进行关联
请求处理过程
1.根据请求路径找到HandlerMethod(带有Method反射属性,也就是对应Controller中的方法)
2.匹配路径对应的拦截器
3.构造HandlerExecutionChain对象
通过HandlerMapping接口提供的方法得到
4.通过HandlerAdapter对象进行处理,得到ModelAndView对象
使用各种HandlerMethodArgumentResolver实现类处理HandlerMethod的参数
使用各种HandlerMethodReturnValueHandler实现类处理返回值,处理成ModelAndView
1-4中发生的异常会被HandlerExceptionREsolver接口实现类进行处理
0 条评论
下一页