目录
1、请求的核心步骤与核心源码分析
1)DispatcherServlet.doDispatch()源码分析;
2)请求处理核心流程与细节:
getHandler() 根据请求如何找到处理类?
getHandler() 通过处理类如何找到适配器?
handle() 通过适配器如何执行目标方法?
2、handle()方法的执行细节
1)invokeHandlerMethod()源码分析;
2)带有@ModelAttribute的方法优先于目标方法执行源码分析;
3)方法如何确定参数值:
resolveHandlerArguments()源码.
无注解情况参数确定(部分).
有注解的情况参数确定.
无注解的特例情况:确定POJO参数值的三步骤.
3、视图渲染页面分析
1)视图解析源码分析;
2)View对象的核心方法render():
隐含模型中的数据放入请求域中.
4、SpringMVC的九大组件
1)九大组件何时被初始化?
2)举例:HandlerMappings的初始化
getDefaultStrategies()默认初始化方式
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、根据当前请求地址,找到哪个类(Controller)可以来处理
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
//如果没有找到该类,则执行-->抛出异常
noHandlerFound(processedRequest, response);
return;
}//3、拿到能执行这个类的所有方法的适配器:(反射工具)
// 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()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}// Actually invoke the handler.简单来讲就是处理(控制)器也就是Controller中的方法被调用的地方
//注:控制器(Controller) ,处理器(Handler)
//4、这里的实质是:通过上一步拿到的适配器执行目标方法
//5、将目标方法执行完成后的返回值作为视图名,设置保存到ModelAndView中,并返回ModelAndView。
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//判断是否是异步方法,如果是则结束该次请求
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}//如果该方法是void类型,则返回值无视图名,就设置一个默认的视图名,该名字为你当时过来的请求地址名
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
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;转发到页面
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
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);
}
}
}
}
核心流程:
所有请求过来DispatcherServlet收到请求,调用doDispatch()方法进行处理:
1)、 getHandler():根据当前请求地址找到能够处理这个请求的目标处理器类(处理器)。
2)、getHandlerAdapter():根据当前处理类获取能够执行这个处理器方法的适配器。
3)、使用刚才获取到的适配器(AnnotationMethodHandlerAdapter)执行目标方法;
4)、目标方法执行后会返回一个ModelAndView对象
5)、根据ModelAndView的信息转发到具体的页面,并可以在请求域中取出ModelAndView中的数据模型
6)、响应给指定页面
前言:
getHandler()返回了什么?
答:会返回目标处理器类的执行链
getHandler()方法中的参数processendRequest又是什么呢?
答:可以看出这个processendRequest 是来自原生的Servlet的request保存了众多变量,其中requestDispatcherPath保存了我们要请求的地址。
正式 进入getHandler( )方法看一下里面具体做了些什么?
通过for循环遍历得到什么东西?
让我们看一下这个handlerMappings,发现存放了两个对象,一个是基于bean(web.xml配置),一个是基于annotation(注解),简而言之就是两个大的资源部。
看看这个注解的资源部封装了什么? /hello请求对应了哪个控制器(Controller)。因此在此阐述getHandler( )循环后得到了什么:
首先是下标为0的BeanNameUrlHandlerMapping对象,由于没有采用web.xml配置的方式,所以由hm.getHandler(request)方法得到的handlerMap为空 ,由此将继续遍历下标为1的DefaultAnnotationHandlerMapping,相同的方法,找到这个请求的映射信息,最终得到了目标url的处理器类。补充:handlerMap作用 在ioc容器启动创建Controller对象的时候扫描每个处理器都能处理什么请求,保存在HandllerMapping的hadnlerMap属性中;下一次请求过来,就来看哪个HandlerMapping中有这个请求的映射信息。
前言:
源码中的HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
-->mappedHandler.getHandler()就是得到处理类。
直接进入getHandler()方法看一下里面具体做了些什么?
与getHandler() 方法类似,也有个for循环,最终在当前类下找到了一个能解析注解方法的适配器。补充:适配器 就是用它来执行目标方法 。
直接进入handle()中:
404行:判断这个处理类这否有sessionAttributes注解.
中间非核心部分省略....
最核心的428行: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);
//包装原生的(request,response)
ServletWebRequest webRequest = new ServletWebRequest(request, response);
//创建了一个BindingAwareModelMap() 就是隐含模型
ExtendedModelMap implicitModel = new BindingAwareModelMap();
//真正执行目标方法:目标方法利用反射执行期间确定参数值,提前执行modelattribute等所有操作
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;
}
public final Object invokeHandlerMethod(Method handlerMethod, Object handler,NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
//得到待执行的方法包含方法参数
Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
try {
boolean debug = logger.isDebugEnabled();
for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
if (attrValue != null) {
implicitModel.addAttribute(attrName, attrValue);
}
}
//先找到该类所有有@ModelAttribute注解的方法(意思就是要先执行此方法)
for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
//确定@ModelAttribute方法执行时使用的每一个参数值,并且传递了一个implicitModel(隐含模型)。
Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
}
//1、对@ModelAttribute的实际操作例如:添加到隐含模型中。
String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
continue;
}
ReflectionUtils.makeAccessible(attributeMethodToInvoke);
//@ModelAttribute最终通过反射调用执行了
Object attrValue = attributeMethodToInvoke.invoke(handler, args);
if ("".equals(attrName)) {
Class> resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
}
if (!implicitModel.containsAttribute(attrName)) {
implicitModel.addAttribute(attrName, attrValue);
}
}//与上面类似,确定目标方法执行时使用的每一个参数值
Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
if (debug) {
logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
}
ReflectionUtils.makeAccessible(handlerMethodToInvoke);
//2、经过对@ModelAttribute的方法执行后,这里才是真正的执行目标方法....
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;
}
}
resolveHandlerArguments()源码.
代码概述:首先会获得参数的基本信息,再循环遍历每个参数,为其保存参数信息。
注意:在循环遍历的时候,会判断是否包含有注解,有注解的参数与无注解的参数将区分对待
无注解情况参数确定(部分).
具体源码:
1)resolveCommonArgument()方法实质是在用resolveStandardArgument()解析标准参数的方法:判断是否为原生API2)如果不是则判断是否为Model或者Map,如果是的话将implicitModel(隐含模型)传给参数args[ ],
有注解的情况参数确定.
如果在无注解且不是上述的几种情况之外:例如(POJO自定义类型)
注:在判断无注解情况,再不满足上述情况下,attrName=" ";无注解的特例情况:确定POJO参数值的三步骤.
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {// Bind request parameter onto object...
String name = attrName;
if ("".equals(name)) {
//如果attrName是空串:就将参数类型的首字母小写作为值:例如Book-->book
name = Conventions.getVariableNameForParameter(methodParam);
}
Class> paramType = methodParam.getParameterType();
Object bindObject;
//1、如果隐含模型中有这个key(标了@ModelAttribute注解就是注解指定的value,没标就是参数类型的首字母小写),作为该目标对象。
if (implicitModel.containsKey(name)) {
bindObject = implicitModel.get(name);
}
//2、如果没有,则看@SessionAttribute标注的属性,就从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");
}
}
//3、如果都不是,则通过反射创建目标对象
else {
bindObject = BeanUtils.instantiateClass(paramType);
}
WebDataBinder binder = createBinder(webRequest, bindObject, name);
initBinder(handler, name, binder, webRequest);
return binder;
}
前言:
在说视图解析之前,必须得说清之前说过的请求处理核心流程与细节中的目标方法执行完后会返回ModelAndView对象。
因此得到结论:任何方法的返回值,最终都会包装成ModelAndView对象(并且都会有一个视图名属性)。
视图渲染页面=视图解析+视图对象执行render()
何为视图渲染?--->将域中的数据在页面中展示(页面就是来渲染模型数据的)
1)视图解析源码分析;
视图解析:就是得到View(视图对象)的过程
注:这里就不过多说明父类的一些列繁琐的创建View过程,例如:拼串等等...
2)View对象的核心方法render():
隐含模型中的数据放入请求域中.
最终进行请求转发:
/** 文件上传解析器 */
private MultipartResolver multipartResolver;
/** 区域信息解析器:和国际化有关 */
private LocaleResolver localeResolver;
/**主题解析器:强大的主题效果更换 */
private ThemeResolver themeResolver;
/** Handler映射信息:HandlerMapping */
private List handlerMappings;
/** Handler的适配器 */
private List handlerAdapters;
/**SpringMVC强大的异常解析功能:异常解析器 */
private List handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet */
private RequestToViewNameTranslator viewNameTranslator;
/** Flash+MapManager:SpringMVC中运行重定向携带数据的功能 */
private FlashMapManager flashMapManager;
/** 视图解析器 */
private List viewResolvers;
还是在DispatcherServlet中的onRefresh()方法,可以发现有@Override,其实是Spring启动ioc容器的时候,这个onRefresh()方法被调用。
首先getBean通过id为handlerMapping去IOC容器中找,如果为没有找到为null,则采用默认初始化去找。
getDefaultStrategies()默认初始化方式
1、让我们来看看getDefaultStrategies()是如何找的:从defaultStrategies中通过key找到value。
2、 有一个静态代码块,ClassPathResource类路径下的资源,第一个参数是:资源名,第二个参数是:此参数路径下,
找到后在加载这个资源的配置。补充:这也得到了前面处理请求核心细节中getHandler()方法获得处理类时,有这两个对象。