目录
2.剖析 getHandler(processedRequest)
3.剖析getHandlerAdapter(mappedHandler.getHandler())
4.剖析mappedHandler.applyPreHandle(processedRequest, response)
5.剖析mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
6. 剖析mappedHandler.applyPostHandle(processedRequest, response, mv)
7.剖析processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
1.什么是SpringMVC
是由Spring来实现web模块,简化web开发
MVC:Model + View + Controller,Model封装数据,View界面显示,Controller控制整个项目的跳转。MVC的优势在于,整个项目进行解耦,每层只写自己的东西,不写其他业务逻辑的代码。后期维护更加方便,扩展性更高
但是SpringMVC和传统MVC架构是有区别的,区别在于下图,多了一个前端控制器,由前端控制器来进行智能的派发,而不再是请求直接指定到对应的Controller。
2.由问题引出SpringMVC两大配置
/拦截所有请求,但是不拦截jsp页面
/*,拦截所有请求,包括jsp页面
当我们使用“/*”拦截时,jsp请求也会来到springmvc,但是我们没办法处理,所以只能使用“/”来拦截
当我们请求静态资源时,无论配置哪一个,都会被SpringMVC拦截,那么我们究竟应该怎么配置呢
我们可以看到tomcat中的父web.xml中的default servlet
由上图可见,servlet是用于拦截静态资源的(除jsp和servlet)。当我们配置了前端控制器拦截了“/”,就相当于禁用掉了tomcat的default servlet。这样当静态资源请求来到DispatcherServlet(前端控制器)看哪个方法能的requestMapping是能够处理的。
但是我们没有一个是能处理的,所以就会404。解决步骤第五节
3.SpringMVC流程解析
首先查看org.springframework.web.servlet.DispatcherServlet是什么
可以看出来它是一个Servlet,既然是Servlet。那么肯定是要调用doGet(),doPost()。看看是在哪儿调用的
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
//进入此方法
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//终于来到了SpringMVC的核心代码
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
1.大致流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
//1.判断是否是文件上传请求,如果是就将请求包装
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
//2.决定我们当前请求,找到哪个类能来处理这个请求。得到处理器执行链(目标方法与拦截器)
mappedHandler = getHandler(processedRequest);
//如果没有找到能来处理这个请求的处理器。那么就抛出异常404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//3.拿到能执行这个类的所有方法的适配器,后续会利用适配器来执行目标方法(AnnotationMethodHandlerAdapter)
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
//4.获取请求方式
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//5.运行拦截器的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//6.真正的执行目标方法,利用适配器执行.将目标方法执行完成后的返回值作为视图名,
//设置保存到ModelAndView中,所以无论目标方法返回值是何种类型,最终都会返回这个
//ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//如果没有视图名(方法返回值为void),默认设置一个视图名
applyDefaultViewName(processedRequest, mv);
//7.执行拦截器的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
//当执行目标方法出现异常时,会将异常对象赋值给dispatchException
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
//6.根据方法最终执行完成后封装的ModelAndView,转发到对应页面,而且ModelAndView中的数
//据可以从请求域中获取
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
//上述代码出现任何异常都会走到这里
catch (Exception ex) {
//执行拦截器的afterCompletion方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
1.请求来到doDispatch()方法
2.判断是否是文件上传请求,如果是就将请求包装
3.决定我们当前请求,找到哪个类能来处理这个请求。
4.拿到能执行这个类的所有方法的适配器,后续会利用适配器来执行目标方法
5.真正的执行目标方法,利用适配器执行.将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中,所以无论目标方法返回值是何种类型,最终都会返回这个ModelAndView
6.根据方法最终执行完成后封装的ModelAndView,转发到对应页面,而且ModelAndView中的数据封装到请求域中。
上述六步就是SpringMVC处理请求的大致流程,细节部分见下方。
2.剖析 getHandler(processedRequest)
那么是如何根据请求就能找到对应的处理器类呢?请看下方
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//this.handlerMappings中有一个DefaultAnnotationHandlerMapping
//它里面有一个属性为HandlerMap保存了所有的方法与处理器映射关系,key为方法名,value为controller对象。
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
//找到了能处理该请求的处理器并返回,该处理器封装了拦截器链,controller对象等
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
3.剖析getHandlerAdapter(mappedHandler.getHandler())
如何根据处理器获得适配器呢?请看下方
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
//this.handlerAdapters中有一个AnnotationMethodHandlerAdapter。我们注解模式就是使用
//它。直接返回AnnotationMethodHandlerAdapter
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
if (ha.supports(handler)) {
return ha;
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
4.剖析mappedHandler.applyPreHandle(processedRequest, response)
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
//获取所有拦截器
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//遍历拦截器
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
//调用拦截器的preHandle方法。当拦截器返回false的时候,直接执行
//AfterCompletion方法后直接返回,不再执行后续流程
//当返回true的时候,遍历执行所有拦截器的preHandle方法。
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
//记录一下索引
this.interceptorIndex = i;
}
}
return true;
}
5.剖析mv = ha.handle(processedRequest, response, mappedHandler.getHandler())
在这一步真正通过适配器执行了目标方法。SpringMVC如何确定每一个参数的值。
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//拿到处理器的类型
Class<?> clazz = ClassUtils.getUserClass(handler);
//判断当前类是否标记@SessionAttributes注解
Boolean annotatedWithSessionAttributes = this.sessionAnnotatedClassesCache.get(clazz);
if (annotatedWithSessionAttributes == null) {
annotatedWithSessionAttributes = (AnnotationUtils.findAnnotation(clazz, SessionAttributes.class) != null);
this.sessionAnnotatedClassesCache.put(clazz, annotatedWithSessionAttributes);
}
if (annotatedWithSessionAttributes) {
checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
}
else {
checkAndPrepare(request, response, true);
}
// Execute invokeHandlerMethod in synchronized block if required.
if (this.synchronizeOnSession) {
HttpSession session = request.getSession(false);
if (session != null) {
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
return invokeHandlerMethod(request, response, handler);
}
}
}
//执行此方法
return invokeHandlerMethod(request, response, handler);
}
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//获取方法解析器。用解析器得到当前方法的注解等
ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
//根据解析器得到哪个方法能够执行
Method handlerMethod = methodResolver.resolveHandlerMethod(request);
//获得方法执行器
ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
//包装原生的reuqest,response
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//创建一个BindingAwareModelMap。这就是隐含模型
ExtendedModelMap implicitModel = new BindingAwareModelMap();
//重点:执行目标方法
Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
ModelAndView mav =
methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
return mav;
}
//真正执行目标方法,目标方法利用反射执行期间确定参数值,提前执行modelattribute等所有操作都在这个//方法中。第一个参数为目标方法,第二个参数为目标处理器,第三个参数为隐含模型
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
//@SessionAttributes注解中标注的属性,放入session中。同时给隐含模型中放入
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//获取到所有标注了@ModelAttribut注解的方法
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
//先确定@ModelAttribut注解的方法执行时要使用的每一个参数的值,进去看看
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
//获得@ModelAttribut注解的方法的value值
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
//将该方法设置成可访问的
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
//执行目标方法
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
//如果@ModelAttribut注解的方法没有设置value值
if ("".equals(attrName)) {
//获得@ModelAttribut注解的方法的返回值类型
Class<?> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
//变量名为返回值类型首字母小写
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
//如果隐含模型不包含attrName
if (!implicitModel.containsAttribute(attrName)) {
//将此方法的返回值类型作为key,返回值作为value放入隐含模型
implicitModel.addAttribute(attrName, attrValue);
}
}
//上述就是@ModelAttribut标注的方法的提前运行
//开始执行目标方法,首先解析目标方法的参数是哪些值
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
return handlerMethodToInvoke.invoke(handler, args);
}
catch (IllegalStateException ex) {
// Internal assertion failed (e.g. invalid signature):
// throw exception with full handler method context...
throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
}
catch (InvocationTargetException ex) {
// User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
ReflectionUtils.rethrowException(ex.getTargetException());
return null;
}
}
解析参数的值
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
Class<?>[] paramTypes = handlerMethod.getParameterTypes();
//创建一个和目标方法参数个数相同的数组
Object[] args = new Object[paramTypes.length];
for (int i = 0; i < args.length; i++) {
MethodParameter methodParam = new SynthesizingMethodParameter(handlerMethod, i);
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
String paramName = null;
String headerName = null;
boolean requestBodyFound = false;
String cookieName = null;
String pathVarName = null;
String attrName = null;
boolean required = false;
String defaultValue = null;
boolean validate = false;
Object[] validationHints = null;
int annotationsFound = 0;
//获取到这个参数的所有注解
Annotation[] paramAnns = methodParam.getParameterAnnotations();
//找到目标方法这个参数的所有注解,如果有注解就解析并保存
for (Annotation paramAnn : paramAnns) {
if (RequestParam.class.isInstance(paramAnn)) {
RequestParam requestParam = (RequestParam) paramAnn;
paramName = requestParam.name();
required = requestParam.required();
defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
annotationsFound++;
}
else if (RequestHeader.class.isInstance(paramAnn)) {
RequestHeader requestHeader = (RequestHeader) paramAnn;
headerName = requestHeader.name();
required = requestHeader.required();
defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
annotationsFound++;
}
else if (RequestBody.class.isInstance(paramAnn)) {
requestBodyFound = true;
annotationsFound++;
}
else if (CookieValue.class.isInstance(paramAnn)) {
CookieValue cookieValue = (CookieValue) paramAnn;
cookieName = cookieValue.name();
required = cookieValue.required();
defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
annotationsFound++;
}
else if (PathVariable.class.isInstance(paramAnn)) {
PathVariable pathVar = (PathVariable) paramAnn;
pathVarName = pathVar.value();
annotationsFound++;
}
//如果参数有@ModelAttribute注解,那么@ModelAttribute注解的值就让attrName 保存
else if (ModelAttribute.class.isInstance(paramAnn)) {
ModelAttribute attr = (ModelAttribute) paramAnn;
attrName = attr.value();
annotationsFound++;
}
else if (Value.class.isInstance(paramAnn)) {
defaultValue = ((Value) paramAnn).value();
}
else {
Validated validatedAnn = AnnotationUtils.getAnnotation(paramAnn, Validated.class);
if (validatedAnn != null || paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
validate = true;
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(paramAnn));
validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints});
}
}
}
//上述注解一个参数只能标注其中一个
if (annotationsFound > 1) {
throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
"do not specify more than one such annotation on the same parameter: " + handlerMethod);
}
//没有找到注解的情况,解析普通参数
if (annotationsFound == 0) {
//解析此参数,主要是判断当前参数是否是原生API。进入看看
Object argValue = resolveCommonArgument(methodParam, webRequest);
//如果是原生API,则直接赋值
if (argValue != WebArgumentResolver.UNRESOLVED) {
args[i] = argValue;
}
//此参数是否有默认值,如果有则返回默认值
else if (defaultValue != null) {
args[i] = resolveDefaultValue(defaultValue);
}
else {
//判断当前参数是否是Model或者Map类型的
Class<?> paramType = methodParam.getParameterType();
if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
if (!paramType.isAssignableFrom(implicitModel.getClass())) {
throw new IllegalStateException("Argument [" + paramType.getSimpleName() + "] is of type " +
"Model or Map but is not assignable from the actual model. You may need to switch " +
"newer MVC infrastructure classes to use this argument.");
}
//将刚刚的隐含模型赋值给当前参数,所以知道为什么我们形参能够传map了吧!!
args[i] = implicitModel;
}
//再看看是否是SessionStatus
else if (SessionStatus.class.isAssignableFrom(paramType)) {
args[i] = this.sessionStatus;
}
//再看看是否是HttpEntity
else if (HttpEntity.class.isAssignableFrom(paramType)) {
args[i] = resolveHttpEntityRequest(methodParam, webRequest);
}
//再看看是否是Errors
else if (Errors.class.isAssignableFrom(paramType)) {
throw new IllegalStateException("Errors/BindingResult argument declared " +
"without preceding model attribute. Check your handler method signature!");
}
//再看看是否是简单属性(是否是八大基础类型+String)
else if (BeanUtils.isSimpleProperty(paramType)) {
paramName = "";
}
//如果都不是那么attrName = ""。总结一下
//如果是自定义对象。如果这个参数标注了@ModelAttribute注解,那么attrName就是注解的值,如果
//没有标注@ModelAttribute注解,就是空串
else {
attrName = "";
}
}
}
//确定值的环节
//如果参数名不等于null
if (paramName != null) {
//获取这个参数名的值,通过request.getParameterValues(paramName)获得。后续也是类似
args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
}
else if (headerName != null) {
args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
}
else if (requestBodyFound) {
args[i] = resolveRequestBody(methodParam, webRequest, handler);
}
else if (cookieName != null) {
args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
}
else if (pathVarName != null) {
args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
}
//这个环节就是确定自定义类型参数的值,还要将请求中每一个参数 赋值给这个对象。
else if (attrName != null) {
//获取一个webDataBinder对象
WebDataBinder binder =
resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
if (binder.getTarget() != null) {
//调用此方法就为自定义对象赋值成功
doBind(binder, webRequest, validate, validationHints, !assignBindingResult);
}
args[i] = binder.getTarget();
if (assignBindingResult) {
args[i + 1] = binder.getBindingResult();
i++;
}
implicitModel.putAll(binder.getBindingResult().getModel());
}
}
return args;
}
protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
throws Exception {
if (this.customArgumentResolvers != null) {
for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
if (value != WebArgumentResolver.UNRESOLVED) {
return value;
}
}
}
//获取参数类型
Class<?> paramType = methodParameter.getParameterType();
//解析标准参数(是否是原生API),进入
Object value = resolveStandardArgument(paramType, webRequest);
if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
throw new IllegalStateException("Standard argument type [" + paramType.getName() +
"] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
"]. Consider declaring the argument type in a less specific fashion.");
}
//如果是原生API,则返回
return value;
}
//确定当前参数是否是原生API
protected Object resolveStandardArgument(Class<?> parameterType, NativeWebRequest webRequest) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
//判断此参数是不是ServletRequest
if (ServletRequest.class.isAssignableFrom(parameterType) ||
MultipartRequest.class.isAssignableFrom(parameterType)) {
Object nativeRequest = webRequest.getNativeRequest(parameterType);
if (nativeRequest == null) {
throw new IllegalStateException(
"Current request is not of type [" + parameterType.getName() + "]: " + request);
}
return nativeRequest;
}
//判断此参数是不是ServletResponse
else if (ServletResponse.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
Object nativeResponse = webRequest.getNativeResponse(parameterType);
if (nativeResponse == null) {
throw new IllegalStateException(
"Current response is not of type [" + parameterType.getName() + "]: " + response);
}
return nativeResponse;
}
//判断是不是HttpSession...
else if (HttpSession.class.isAssignableFrom(parameterType)) {
return request.getSession();
}
else if (Principal.class.isAssignableFrom(parameterType)) {
return request.getUserPrincipal();
}
else if (Locale.class == parameterType) {
return RequestContextUtils.getLocale(request);
}
else if (InputStream.class.isAssignableFrom(parameterType)) {
return request.getInputStream();
}
else if (Reader.class.isAssignableFrom(parameterType)) {
return request.getReader();
}
else if (OutputStream.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getOutputStream();
}
else if (Writer.class.isAssignableFrom(parameterType)) {
this.responseArgumentUsed = true;
return response.getWriter();
}
return super.resolveStandardArgument(parameterType, webRequest);
}
再来看看POJO是如何赋值的
首先创建一个未属性赋值的对象,并且创建一个webDataBinder对象。此对象用于绑定请求参数到自定义对象
//此环节为确定POJO的值
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
// Bind request parameter onto object...
String name = attrName;
//如果attrName是空串。
if ("".equals(name)) {
//就将参数类型的首字母小写作为值
name = Conventions.getVariableNameForParameter(methodParam);
}
Class<?> paramType = methodParam.getParameterType();
Object bindObject;
//确定目标对象的值
//如果隐含模型中有这个key(如果标记@ModelAttribute注解的方法
//已经提前运行并且往隐含模型中放入值),那么直接从隐含模型中拿出并赋值给此参数
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
//看看是否是@SessionAttribute标注的参数,如果是就从session中取,如果session没有就直接抛出异常
else if (this.methodResolver.isSessionAttribute(name, paramType)) {
bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
if (bindObject == null) {
raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
}
}
//如果都不是就利用反射创建我们自定义的对象
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}
看看如何绑定请求参数到自定义类型对象中
public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String name = ModelFactory.getNameForParameter(parameter);
Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
createAttribute(name, parameter, binderFactory, webRequest));
if (!mavContainer.isBindingDisabled(name)) {
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null && !ann.binding()) {
mavContainer.setBindingDisabled(name);
}
}
//创建一个binder对象
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
if (binder.getTarget() != null) {
if (!mavContainer.isBindingDisabled(name)) {
//绑定请求参数到自定义对象
bindRequestParameters(binder, webRequest);
}
validateIfApplicable(binder, parameter);
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// Add resolved attribute and BindingResult at the end of the model
Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
mavContainer.removeAttributes(bindingResultModel);
mavContainer.addAllAttributes(bindingResultModel);
return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
ConversionService 负责进行数据类型转换
Validators 进行数据校验
BindingResult 负责保存数据解析以及校验期间产生的错误
6. 剖析mappedHandler.applyPostHandle(processedRequest, response, mv)
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
//拿到所有的拦截器
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
//倒序遍历,逆向执行每个拦截器的postHandle方法
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
7.剖析processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
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);
}
}
//当mv不是null
if (mv != null && !mv.wasCleared()) {
//调用render方法进行页面渲染
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方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
//与国际化相关
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
//定义一个视图对象
View view;
if (mv.isReference()) {
//开始使用视图解析器来通过视图名来获得视图对象,进入此方法
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//真正的渲染页面,进入此方法
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
//遍历容器中的所有视图解析器。
for (ViewResolver viewResolver : this.viewResolvers) {
//通过视图解析器来获得view对象
View view = viewResolver.resolveViewName(viewName, locale);
//如果当前视图解析器能够获得view对象,那么直接返回view对象
if (view != null) {
return view;
}
}
return null;
}
下面来看看视图解析器是如何得到view对象的
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
return createView(viewName, locale);
}
else {
//先从缓存中获取
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
//开始创建view,进入此方法
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
//将view放入缓存
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
}
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
protected View createView(String viewName, Locale locale) throws Exception {
if (!canHandle(viewName, locale)) {
return null;
}
//如果视图名是以"redirect:"前缀的.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
//创建一个RedirectView
RedirectView view = new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible());
view.setHosts(getRedirectHosts());
return applyLifecycleMethods(viewName, view);
}
//如果视图名是以"forward:"为前缀的.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
//返回InternalResourceView
return new InternalResourceView(forwardUrl);
}
//如果都不是就默认创建一个view对象
return super.createView(viewName, locale);
}
看上段代码可以得出总结,viewResolver(视图解析器唯一的作用就是根据视图名获得视图对象)
下面来看看得到视图对象后是如何进行渲染的
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
" and static attributes " + this.staticAttributes);
}
//创建此map用于存放要输出的数据
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
//准备相应
prepareResponse(request, response);
//将模型数据进行渲染
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
//渲染数据,也就是将模型数据放入请求域中
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
//决定转发器转发到哪个页面
String dispatcherPath = prepareForRendering(request, response);
//获取转发器
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'");
}
//转发到页面
rd.forward(request, response);
}
}
先看看是如何渲染数据的
protected void exposeModelAsRequestAttributes(Map<String, Object> model, HttpServletRequest request) throws Exception {
for (Map.Entry<String, Object> entry : model.entrySet()) {
String modelName = entry.getKey();
Object modelValue = entry.getValue();
if (modelValue != null) {
//将模型数据放入请求域
request.setAttribute(modelName, modelValue);
if (logger.isDebugEnabled()) {
logger.debug("Added model object '" + modelName + "' of type [" + modelValue.getClass().getName() +
"] to request in view with name '" + getBeanName() + "'");
}
}
else {
request.removeAttribute(modelName);
if (logger.isDebugEnabled()) {
logger.debug("Removed model object '" + modelName +
"' from request in view with name '" + getBeanName() + "'");
}
}
}
}
4.SpringMVC的9大组件
从第三步已经知道了SpringMVC的运行流程,那么问题来了,handlerMappings(处理器映射器),handlerAdapters(处理器适配器)等是什么时候完成初始化并赋值的呢?
在DispatcherServlet类中有这几个引用类型的属性(刚好是9个)。它们就是我们SpringMVC的9大组件,SpringMVC在工作的时候,关键位置都是由组件完成的。
共同点:九大组件全部都是接口,接口就是规范。我们可以实现这些接口来自定义这些组件
//文件上传解析器
private MultipartResolver multipartResolver;
//区域信息解析器(很少用)
private LocaleResolver localeResolver;
//主题解析器(很少用)
private ThemeResolver themeResolver;
//Handler映射信息
private List<HandlerMapping> handlerMappings;
//Handler适配器
private List<HandlerAdapter> handlerAdapters;
//异常解析器
private List<HandlerExceptionResolver> handlerExceptionResolvers;
//视图名转换器,如果方法返回值是void,那么就将请求地址作为视图名(很少用)
private RequestToViewNameTranslator viewNameTranslator;
//允许重定向携带数据的功能,放在session中
private FlashMapManager flashMapManager;
//视图解析器
private List<ViewResolver> viewResolvers;
接下来我们看到DispatcherServlet类中有一个方法,这个方法我们在Spring源码解析已经讲过了。(给子容器实现)
protected void onRefresh(ApplicationContext context) {
//初始化9大组件
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
//初始化handlerMappings
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
//从ioc容器中找到所有HandlerMapping类型的组件
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
//否则从ioc容器中找一个id为handlerMapping的组件,找到了就直接赋值
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 如果还是为null,就去默认策略中找,我们进入此方法
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
}
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
//主要看这个值,究竟是从哪里找到这个处理器映射器
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<T>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Error loading DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]: problem with class file or dependent class", err);
}
}
return strategies;
}
else {
return new LinkedList<T>();
}
}
static {
//静态代码块,加载默认的策略,给九大组件赋值的步骤就在这儿了
try {
//从DispatcherServlet.class同级目录下找到DispatcherServlet.properties这个配置文件,9大组件从这个配置文件获取并初始化
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
}
}
properties文件如下
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
上述就是9大组件的初始化。总结就是容器中找这个组件,如果没有找到就使用默认的配置。
5.mvc:annotation-driven 标签
之前我们看到SpringMVC,配置"/"后,是不拦截jsp了。jsp交给tomcat去处理了,但是静态资源还是被拦截。我们配置了<mvc:default-servlet-handler/>后,静态不拦截了,动态映射的请求又不行了。
但是当我们加上了<mvc:annotation-driven />后,动态资源,静态资源都可以访问了。这是为什么呢?
首先了解一下<mvc:default-servlet-handler/>的由来,早期的SpringMVC拦截的都是*.do,*.action等相似后缀请求。无法满足Rest风格的URL,如果将拦截请求改为"/"。那么就不管动态请求和静态请求,SpringMVC都拦截到了,但是SpringMVC是无法处理静态请求
的。所以就会404.那么SpringMVC是如何处理呢。首先在配置文件中加入<mvc:default-servlet-handler/>。它给容器中添加了一个DefaultServletHttpRequestHandler组件,用于检查什么请求SpringMVC能处理,什么请求(静态资源)不能处理。不能处理的请求转发到
WEB容器内部的Servlet来进行处理。
现在进行到了如何处理动态请求了。配置<mvc:annotation-driven />,这个配置为我们导入了哪些组件呢?
1.没有配置<mvc:default-servlet-handler/>和<mvc:annotation-driven />的情况下
在此之前我们先看看源码,没有配置<mvc:default-servlet-handler/>和<mvc:annotation-driven />的情况下
处理器映射器有两种,我们使用第二种DefaultAnnotationHandlerMapping,保存了哪些请求使用哪个处理器来处理,但是我们的handlerMap并没有保存静态资源映射的请求,所以访问静态资源会404.
再来看看处理器适配器。我们是用第三个AnnotionMethodHandlerAdapter,它来帮助我们执行目标方法。已经过时(但是思想和流程是一样的,所以说之前的源码还是有必要阅读的)
2.只加<mvc:default-servlet-handler/>
首先来看看处理器映射器,从DefaultAnnotationHandlerMapping变成了SimpleUrlHandlerMapping
所有的请求都交给DefaultServletHttpRequestHandler来处理,也就是交给tomcat处理。那么静态请求倒是ok,动态请求肯定完蛋。
再看看处理器适配器,少了一个AnnotationMethodHandlerAdapter。
3.加<mvc:default-servlet-handler/>和<mvc:annotation-driven />
先看看处理器映射器,多了一个RequestMappingHandlerMapping。保存了请求的映射信息。动态请求就能查询到了。而静态请求RequestMappingHandlerMapping没有映射,那么就使用第三个处理器映射器去让tomcat来处理
再来看看处理器适配器,我们使用第三个RequestMappingHandlerAdapter
然后利用适配器去执行目标方法。和之前的源码不同,不过思想和流程是一样的。
再来看一个接口
此接口是用于解析bean的定义信息的。用于配置文件的解析。注意AnnotationDrivenBeanDefinitionParser。看看它干了什么
代码太长就不看了,主要是解析xml标签。并且给容器中添加了很多组件。
6.拦截器
SpringMVC提供了拦截器机制。直接上源码
public interface HandlerInterceptor {
//在目标方法运行之前调用,返回布尔值类型,为true放行,为false不放行
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
//在目标方法运行之后执行
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
//在请求整个完成之后(来到页面之后)
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
我们只需要实现 HandlerInterceptor 接口,并重写里面的方法。后将其加入<mvc:interceptors> <bean class="org.song.interceptor.MyHandlerInterceptor"></bean> </mvc:interceptors>中即可,也可以具体拦截某些方法。
源码部分请关注3.4 3.6 3.7(最后一部分)
7.异常解析器
作为SpringMVC的九大组件之一,看看默认是加载的哪个异常解析器
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\ org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\ org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver。但是我们都不用
ResponseStatusExceptionResolver 处理标注@ResponseStatus
ExceptionHandlerExceptionResolver 处理标注了@ExceptionHandler的类
DefaultHandlerExceptionResolver 处理SpringMVC自己的异常
因为我们加了<mvc:annotation-driven />,它给我们加了ExceptionHandlerExceptionResolver。作为我们的异常解析器
解析源码,看看我们的方法出现异常SpringMVC是如何进行处理的。我们在处理器方法使用10/0试试。
我们先进入视图解析流程
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
//现在异常不为null
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);
}
}
//当mv不是null
if (mv != null && !mv.wasCleared()) {
//调用render方法进行页面渲染。将模型数据放入请求域,并转发到错误页面
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方法
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
//创建一个ModelAndView
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()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
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;
}
看看如何解析异常
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
if (shouldApplyTo(request, handler)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
}
prepareResponse(ex, response);
//开始解析异常
ModelAndView result = doResolveException(request, response, handler, ex);
if (result != null) {
logException(ex, request);
}
return result;
}
else {
return null;
}
}
protected final ModelAndView doResolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return doResolveHandlerMethodException(request, response, (HandlerMethod) handler, ex);
}
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
//首先获取到能处理这个异常的方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
//设置参数解析器。用于解析处理异常的方法的参数
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//设置返回值解析器 exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//创建一个ModelAndViewContainer,内部其实就是一个隐含模型和视图
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
}
Throwable cause = exception.getCause();
if (cause != null) {
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
//执行目标方法
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
// Any other than the original exception is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && logger.isWarnEnabled()) {
logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
//获取模型数据。可以在页面上获得
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
//创建一个ModelAndView,将模型数据和视图名传进去
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
//返回此ModelAndView
return mav;
}
}
我们上述的三个异常解析器都有自己特定的使用场景
ResponseStatusExceptionResolver 处理标注@ResponseStatus的方法
ExceptionHandlerExceptionResolver 处理标注了@ExceptionHandler的方法
DefaultHandlerExceptionResolver 处理SpringMVC自己的异常
ExceptionHandlerExceptionResolver 的使用场景
//@ControllerAdvice标识这个类专门用于处理异常
@ControllerAdvice
public class MyExceptionHandler {
//告诉SpringMVC这个方法专门处理这个类发生的异常。
//要携带异常信息,不能给参数位置写model
//可以直接返回ModelAndView对象
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public ModelAndView handleException01(Exception exception){
System.out.println("来到错误页面" + exception);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("myerror");
modelAndView.addObject("exception",exception);
return modelAndView;
}
}