Spring-framework5.0.2 源码阅读笔记 - MVC调用阶段
作者:互联网
Spring MVC 调用阶段
注意: 本片博客所展示的代码中,删除了对核心流程以外的代码,为了不影响观看,所以只保留了最核心的代码
spring 初始化时序图 (免费克隆)
Spring-framework 5.0.2 中文注释源码下载
闲聊
加班加了一个多月,忙着写项目,现在终于搞完了,开始投身到博客中。
从最开始的MVC
初始化,到IOC
初始化,再到DI
,AOP
,按照启动顺序来讲,应该MVC
是最后一个调用的阶段。在本文中会通过Sping MVC
整个浏览器请求和返回浏览器的执行顺序和逻辑进行解读。
1. MVC 入口
Spring MVC
其实是对Servlet
做了一次封装,不过这个封装就变得特别的庞大,也为我们这些开发人员带来了便利,下面我们就从Servlet
开始寻找入口,因为在第一篇Spring系列的博客中就阐明了Spring MVC
初始化的过程是由Servlet#init()
方法作为入口的。但是Spring MVC
对Servlet
做了封装,那么入口被定义在FrameworkServlet#doService()
方法中,由DispatcherServlet
实现,下面看源码:
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 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<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
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());
if (this.flashMapManager != null) {
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 {
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);
}
}
}
}
从上面这段代码中,很容易就提取到了核心的方法调用,那么就是
doDispatch()
,在此方法内,并没有看到任何关于对请求 get、post… 方法的处理,那么我们进入该方法中:
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);
// Determine handler for the current request.
// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,
// 第一个步骤的意义就在这里体现了.这里并不是直接返回controller,
// 而是返回的HandlerExecutionChain请求处理器链对象,
// 该对象封装了handler和interceptors.
mappedHandler = getHandler(processedRequest);
// 如果handler为空,则返回404
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
//3. 获取处理request的处理器适配器handler adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 4.实际的处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
}
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);
}
}
}
}
别看这段代码很长,逻辑都很清晰,逻辑梳理:
- 如果请求是文件上传的,那么需要对请求方式进行标记,最后如果是文件上传的,需要关闭
IO
资源getHanler()
获取到处理的方法,也就是Controller
和url
映射关系,那么这个处理器返回的结果是一个HandlerExecutionChain
对象,这个对象内部封装了handler
和interceptors
,这一步主要是处理获取一个执行链,如果获取不到的情况下,就返回404
getHandlerAdapter(mappedHandler.getHandler())
,这一步主要是获取到一个处理器适配器,用来后续处理请求。handler()
通过上一步获取到的处理器适配器,来处理结请求,也就是我们常说controller
,处理完毕后会返回一个ModelAndView
对象applyDefaultViewName(processedRequest, mv):
结果视图对象的处理processDispatchResult()
解析视图,把解析后的视图,返回给浏览器。- finally 块主要是处理成功后回调的,例如最后一步处理了被标记为文件上传的请求,对资源的关闭等等
2. Controller 入口
常用
spring boot
做开发的WEB
攻城狮都知道Controller
是接收请求的地方,最终根据controller
类上的mapping
+ 方法上的mapping
定位到唯一的方法执行,在第一小节中,简单阐述了doDispatch()
中各个部分的功能,那么第二小节将一步步找到controller
,执行后返回的逻辑。以及视图处理,返回结果
在第一小节中,讲述了
handler()
方法是去调用controller
的,那么我们进入handler()
内部,来到HandlerAdapter#handle
处理器适配器中,那么这里定义的handler
只是一个抽象,实现类有很多,应该进入哪一个,这里先进入到AbstractHandlerMethodAdapter#handle
中,然后这里调用了他自己的抽象方法handleInternal
,顺藤摸瓜,进入handleInternal()
来到一个熟悉的类RequestMappingHandlerAdapter#handleInternal
, 恍然大悟(@RequestMapping
),逐渐清醒,那么源码如下:
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
// 检查请求的方法和是否需要会话
checkRequest(request);
// 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) {
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
进入该方法,第一句就做请求验证,最终根据不同判断条件进行执行
invokeHandlerMethod
方法,最终拿到ModelAndView
对象,那么invokeHandlerMethod
到底做了什么,下面源码:
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
if (logger.isDebugEnabled()) {
logger.debug("Found concurrent result value [" + result + "]");
}
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 完成 request 上的参数和方法参数的数据进行绑定,执行方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
在以上的代码中,其实最主要的是
invocableMethod.invokeAndHandle(webRequest, mavContainer)
这句代码,主要是完成request
上的参数进行绑定,并且通过反射执行方法,进入invokeAndHandle()
,源码如下:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 执行方法,拿到返回结果
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
}
throw ex;
}
}
3.执行 controller
通过调用方法
invokeForRequest(webRequest, mavContainer, providedArgs)
执行目标方法,获取执行结果,进入方法:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取方法的形参列表
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"' with arguments " + Arrays.toString(args));
}
// 执行method方法
Object returnValue = doInvoke(args);
if (logger.isTraceEnabled()) {
logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
"] returned [" + returnValue + "]");
}
return returnValue;
}
- 获取方法形参:
getMethodArgumentValues(request, mavContainer, providedArgs)
,在spring
框架中,获取参数有两种情况,第一种是通过@RequestParam(name="quest")
,另一种则是通过读取字节码进行获取参数,采用的是 asm框架- 执行被命中的方法,
doInvoke(args)
,执行controller
方法,进入方法查看源码:
protected Object doInvoke(Object... args) throws Exception {
// 通过反射工具,标记该类强行访问,无论是private 的mapping 还是 public,都一律被访问了,
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 通过 method.invoke(),执行该方法,得到一个返回结果
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(getInvocationErrorMessage(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
String text = getInvocationErrorMessage("Failed to invoke handler method", args);
throw new IllegalStateException(text, targetException);
}
}
}
通过该方法,可以知道,即使方法被修饰为
private
或protected
都将会被执行,因为调用了ReflectionUtils.makeAccessible(getBridgedMethod())
,然后通过反射调用目标方法,获取执行结果。
4.总结
感觉这篇文章在划水,后续抽空重新写过。
至此,Spring
源码块的内容笔记就做完了.
标签:handler,Spring,request,mavContainer,response,MVC,null,方法,源码 来源: https://blog.csdn.net/qq_38800175/article/details/109543473