1. 拦截器
1.概述
SpringMVC 提供了拦截器机制;允许运行目标方法之前进行一些拦截工作,或者目标方法运行之后进行一些其他处理;自定义的拦截器必须实现HandlerInterceptor
接口
-
preHandle()
方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。 -
postHandle()
方法目标方法调用之后,但是 DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。 -
afterCompletion()
方法在 DispatcherServlet 完全处理完请求、资源响应之后被调用,可以在该方法中进行一些资源清理的操作。
2. 实现
<!-- 测试拦截器 -->
<mvc:interceptors>
<!--配置某个拦截器;默认是拦截所有请求的; -->
<bean class="com.atguigu.controller.MyFirstInterceptor"></bean>
<!-- 配置某个拦截器更详细的信息 -->
<mvc:interceptor>
<!-- 只来拦截test01请求 -->
<mvc:mapping path="/test01"/>
<bean class="com.atguigu.controller.MySecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
单个拦截器
运行流程:拦截器的preHandle------目标方法------拦截器postHandle------页面------拦截器的afterCompletion
异常流程:
- 只要
preHandle
不放行,就没有以后的流程; - 只要
preHandle
放行,无论目标方法是否报错,afterCompletion
都会执行;
多个拦截器
(正常)运行流程(与Filter一致)
MyFirstInterceptor...preHandle...
MySecondInterceptor...preHandle...
test01....
MySecondInterceptor...postHandle...
MyFirstInterceptor...postHandle...
success.jsp....
MySecondInterceptor...afterCompletion...
MyFirstInterceptor...afterCompletion
异常流程:
- 任一拦截器的
preHandle
不放行,从此以后都没有; - MyFirstInterceptor放行,MySecondInterceptor不放行,那么其前面已经放行了的拦截器(MyFirstInterceptor)的
afterCompletion
总会执行;
3.源码解析
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//拿到方法的执行链,包含拦截器
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//拦截器preHandle执行位置;有一个拦截器返回false目标方法以后都不会执行;直接跳到afterCompletion
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.适配器执行目标方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
//目标方法只要正常,就会走到postHandle;任何期间有异常
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
//页面渲染;如果完蛋也是直接跳到afterCompletion;
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
preHandle:
preHandle:
第一次:ConversionServiceExposingInterceptor interceptorIndex=0;
第二次:MyFirstInterceptor interceptorIndex=1
第三次;MySecondInterceptor 执行afterCompletion()
已经放行了的拦截器的afterCompletion总会按照interceptorIndex逆序执行
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (getInterceptors() != null) {
for (int i = 0; i < getInterceptors().length; i++) {
HandlerInterceptor interceptor = getInterceptors()[i];
//preHandle-true-false
if (!interceptor.preHandle(request, response, this.handler)) {
//执行完afterCompletion();
triggerAfterCompletion(request, response, null);
//返回一个false
return false;
}
//记录一下索引
this.interceptorIndex = i;
}
}
return true;
}
postHandle:
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
if (getInterceptors() == null) {
return;
}
//逆向执行每个拦截器的postHandle
for (int i = getInterceptors().length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
页面渲染:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
页面渲染
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
//页面正常执行afterCompletion;
//即使没走到这,afterCompletion总会执行;
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
throws Exception {
if (getInterceptors() == null) {
return;
}
//有记录最后一个放行拦截器的索引,
//从他开始把之前所有放行的拦截器的afterCompletion都执行
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = getInterceptors()[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
什么时候用Filter,什么时候用拦截器?若某些功能需要其他组件配合完成,就使用拦截器(可Autowired
);其他情况可以写filter(脱离了Spring MVC也能运行)
2. 异常处理
1. 概述
Spring MVC 通过 HandlerExceptionResolver
处理程序的异常(9大组件之一),包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
默认装配这几个HandlerExceptionResolver
:
①ExceptionHandlerExceptionResolver
:处理标注了@ExceptionHandler
的异常
/**
* 集中处理所有异常
* 1、集中处理所有异常的类加入到ioc容器中
* 2、@ControllerAdvice专门处理异常的类
*/
@ControllerAdvice
public class MyConcentrateException {
/**
* 告诉SpringMVC这个方法专门处理这个类发生的异常
* 1、给方法上随便写一个Exception,用来接受发生的异常
* 2、要携带异常信息不能给参数位置写Model;
* 3、返回ModelAndView就行了;
* 4、精确优先:如果有多个@ExceptionHandler都能处理这个异常
* 5、全局异常处理与本类异常处理同时存在,则本类优先而非精确优先;
*/
@ExceptionHandler(value={ArithmeticException.class})
public ModelAndView handleException01(Exception exception){
System.out.println("全局的:handleException01..."+exception);
//
ModelAndView view = new ModelAndView("myerror");
view.addObject("ex", exception);
//视图解析器拼串
return view;
}
@ExceptionHandler(value={Exception.class})
public ModelAndView handleException02(Exception exception){
System.out.println("全局的:handleException02..."+exception);
ModelAndView view = new ModelAndView("myerror");
view.addObject("ex", exception);
视图解析器拼串
return view;
}
}
②ResponseStatusExceptionResolver
:处理标注了@ResponseStatus
的异常,用于标注自定义异常
@RequestMapping("/handle02")
public String handle02(@RequestParam("username") String username) {
if (!"admin".equals(username)) {
System.out.println("登陆失败....");
throw new UserNameNotFoundException();
}
System.out.println("登陆成功....");
return "success";
}
@ResponseStatus(reason="用户被拒绝登陆",value=HttpStatus.NOT_ACCEPTABLE)
public class UserNameNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
}
③DefaultHandlerExceptionResolver
:判断是否Spring MVC自带的异常,如果没人处理(轮流解析,若ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
都不能解析),就由这个异常解析器处理。
有以下默认异常:
try {
if (ex instanceof NoSuchRequestHandlingMethodException) {
return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,
handler);
}
else if (ex instanceof HttpRequestMethodNotSupportedException) {
return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,
response, handler);
}
else if (ex instanceof HttpMediaTypeNotSupportedException) {
return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,
handler);
}
else if (ex instanceof HttpMediaTypeNotAcceptableException) {
return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,
handler);
}
else if (ex instanceof MissingServletRequestParameterException) {
return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,
response, handler);
}
else if (ex instanceof ServletRequestBindingException) {
return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,
handler);
}
else if (ex instanceof ConversionNotSupportedException) {
return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);
}
else if (ex instanceof TypeMismatchException) {
return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotReadableException) {
return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);
}
else if (ex instanceof HttpMessageNotWritableException) {
return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);
}
else if (ex instanceof MethodArgumentNotValidException) {
return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);
}
else if (ex instanceof MissingServletRequestPartException) {
return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);
}
else if (ex instanceof BindException) {
return handleBindException((BindException) ex, request, response, handler);
}
else if (ex instanceof NoHandlerFoundException) {
return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);
}
}
catch (Exception handlerException) {
logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);
}
return null;
}
④如果异常解析器都不能处理就直接抛出去;
此外还有SimpleMappingExceptionResolver
,通过配置的方式进行异常处理
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- exceptionMappings:配置哪些异常去哪些页面 -->
<property name="exceptionMappings">
<props>
<!-- key:异常全类名;value:要去的页面视图名; -->
<prop key="java.lang.NullPointerException">myerror</prop>
</props>
</property>
<!--指定错误信息取出时使用的key -->
<property name="exceptionAttribute" value="ex"></property>
</bean>
SimpleMappingExceptionResolver
优先级最低
2. 源码
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//如果有异常
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
//处理异常
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
//来到(渲染)页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
异常解析器尝试解析,解析完成进行后续,解析失败则换下一个解析器
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
//解释失败
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
//如果异常解析器都不能处理就直接抛出去(给Tomcat)
throw ex;
}
3. Spring MVC运行流程
4.SpringMVC 与 Spring 整合
目的
为了分工明确
- SpringMVC 的配置文件就来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,支持ajax等)
- Spring 的配置文件来配置和业务有关的(事务控制,数据源等)
整合方法
SpringMVC和Spring分容器
① Spring管理业务逻辑组件
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
② SpringMVC管理控制器组件
<context:component-scan base-package="com.atguigu" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
Spring 是一个父容器,SpringMVC 是一个子容器
- 子容器还可以引用父容器的组件;
- 父容器不能引用子容器的组件;