一、SpringMvc 初始化
1、SpringMvc 九大组件
- HandlerMapping: 处理器映射器
HandlerMapping是一个接口,内部只有一个方法,用来查找Handler的。在SpringMVC中会有很多请求,每个请求都需要一个Handler处理,它的作用是根据request找到对应的Handler。—用来查找Handler的
DispatcherServlet.properties
HandlerMapping=
BeanNameUrlHandlerMapping; 根据配置文件中的beanID 进行识别
RequestMappingHandlerMapping; 根据controller中的 @Controller 注解进行识别
- HandlerAdapter 适配器。
因为SpringMVC中的Handler可以是任意的形式,只要能处理请求就可以,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这就是HandlerAdapter要做的事情。任意形式的Handler通过使用适配器,可以“转换”成固定形式,然后交给Servlet来处理。每种Handler都要有对应的HandlerAdapter才能处理请求。
- HandlerExceptionResolver
SpringMVC中专门负责处理异常的类,根据异常设置ModelAndView。之后交给render方法进行渲染,HandlerExceptionResolver只能处理页面渲染之前的异常,页面渲染过程中的异常,它是不能处理的。
- ViewResolver
这个接口只有一个方法:View resolveViewName(String viewName, Locale locale)throws Exception; 用来将String类型的视图名(viewName)和Locale解析为View类型的视图。
RequestToViewNameTranslator
从request中获取ViewName
LocaleResolver
有两个参数:一是视图名(viewName),另一个是Locale。视图名是处理器返回的,但是Locale没有,而LocaleResolver就是用于从request解析出Locale。
- ThemeResolver
解析主题
- MultipartResolver
用于处理上传请求,将普通的request包装成MultipartHttpServletRequest,就可以调用getFile方法获取File
- FlashMapManager
管理FlashMap的,FlashMap是用在redirect中传递参数。 就像我们写接口传递的参数,可以封装到Map中
2、SpringMvc 启动流程
思考:当我们使用SpringMVC 的时候,相当于在spring框架的基础之上做了一个扩展工作而已,我们自己来实现扩展,可以从哪里下手?
- SpringMvc 是Spring的扩展,那么在启动springmvc之前必须要先把spring 容器启动起来。
2.1 SpringMVC 启动基本流程
- 首先启动tomact 容器,读取 web.xml 中对应对配置文件,核心配置文件有:
- spring-config.xml 启动Spring容器使用的
- applicationContextx.xml 启动SpringMvc 容器使用的
- 然后启动Spring 容器,生产Spring中内置的一些bean 对象
- 最后启动SpringMvc 容器,创建SpringMvc 特有对bean 对象
2.2 源码分析启动流程
2.2.1 启动tomact 容器
第一步:启动tomact 容器,详细内容等后期看tomcat 源码专题系列分析
2.2.1 启动Spring 容器
第二步:启动Spring 容器
读取web.xml配置文件
在原始Spring 项目中,会有web.xml 配置文件, 在配置文件中会进行一个listener 配置
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</linstener-class>
</listener>
调用 initWebApplicationContext() 方法
- ContextLoaderListener 调用 initWebApplicationContext() 方法作为启动容器的入口
- �ContextLoaderListener 类继承了ContextLoader类,实现ServletContextListener接口
- 实现 contextInitialized() 方法,在该方法中调用了父类的 initWebApplicationContext() 方法 ```json /**
- 引导 监听器启动 和关 闭Spring的根 WebApplicationContext。
- 简单地委托给ContextLoader和ContextCleanupListener
- Bootstrap listener to start up and shut down Spring’s root {@link WebApplicationContext}.
- Simply delegates to {@link ContextLoader} as well as to {@link ContextCleanupListener}.
*/ public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
/**
- Create a new {@code ContextLoaderListener} that will create a web application
- context based on the “contextClass” and “contextConfigLocation” servlet
- context-params. See {@link ContextLoader} superclass documentation for details on
- default values for each.
*创建一个新的{@code ContextLoaderListener}来创建一个web应用程序上下文基,于“contextClass”和“contextConfigLocation”servlet上下文参数。
This constructor is typically used when declaring {@code ContextLoaderListener}
- as a {@code
} within {@code web.xml}, where a no-arg constructor is - required.
The created application context will be registered into the ServletContext under
- the attribute name {@link WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE}
- and the Spring application context will be closed when the {@link #contextDestroyed}
- lifecycle method is invoked on this listener.
- @see ContextLoader
- @see #ContextLoaderListener(WebApplicationContext)
- @see #contextInitialized(ServletContextEvent)
@see #contextDestroyed(ServletContextEvent) */ public ContextLoaderListener() { }
/**
- Create a new {@code ContextLoaderListener} with the given application context. This
- constructor is useful in Servlet 3.0+ environments where instance-based
- registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}
- API.
The context may or may not yet be {@linkplain
- org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it
- (a) is an implementation of {@link ConfigurableWebApplicationContext} and
- (b) has not already been refreshed (the recommended approach),
- then the following will occur:
- If the given context has not already been assigned an {@linkplain
- org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it
- {@code ServletContext} and {@code ServletConfig} objects will be delegated to
- the application context
- {@link #customizeContext} will be called
- Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer}s
- specified through the “contextInitializerClasses” init-param will be applied.
- {@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called
- If the context has already been refreshed or does not implement
- {@code ConfigurableWebApplicationContext}, none of the above will occur under the
- assumption that the user has performed these actions (or not) per his or her
- specific needs.
See {@link org.springframework.web.WebApplicationInitializer} for usage examples.
In any case, the given application context will be registered into the
- ServletContext under the attribute name {@link
- WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring
- application context will be closed when the {@link #contextDestroyed} lifecycle
- method is invoked on this listener.
- @param context the application context to manage
- @see #contextInitialized(ServletContextEvent)
- @see #contextDestroyed(ServletContextEvent) */ public ContextLoaderListener(WebApplicationContext context) { super(context); }
/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
/**
* Close the root web application context.
*/
@Override
public void contextDestroyed(ServletContextEvent event) {
closeWebApplicationContext(event.getServletContext());
ContextCleanupListener.cleanupAttributes(event.getServletContext());
}
}
3. 调用configureAndRefreshWebApplicationContext() 方法
1. 根据ServletContext 创建得到 ConfigurableWebApplicationConext对象
1. 根据 上面提供的两个对象,调用 configureAndRefreshWebApplicationContext() 方法
```json
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
// 创建对象 context为 WebApplicationContext
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
// 调用真正的方法
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
中间代码省去了,为了篇幅和效果,可以直接查看源码
return this.context;
}
wac.refresh()方法,ConfigurableApplicationContext接口refresh()方法,完成Spring 容器的启动
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
// 调用 Spring 的核心方法入口开始 bean的创建流程
wac.refresh();
}
-
2.2.3 启动SpringMvc 容器
第三步: 启动SpringMvc 容器
SpringMvc 的本质就是一个Servlet,在web.xml 配置文件中定义了DispatcherServlet 这个类,所以SpringMvc 的启动就是找到init()方法,但是DispatcherServlet 类中没有init()方法,思路就是一步一步找到父类中的init()方法。tomcat 读取到web.xml 文件后,会读取到servlet 的配置,然后进行执行Servlet的构造方法,最后调用init()方法,具体源码在 tomact 源码中 启动SpringMvc 容器:在启动SpringMvc 容器的过程,就是执行DispatcherServlet 生命周期
调用Servlet 的init() 方法
DisPatcherServlet 继承了FrameworkServlet,FrameworkServlet 继承了HttpServletBean,调用的Servlet的init()方法实际就是 HttpServletBean的 init 方法,此时就是通过创建生成的DispatcherServelt对象调用的
![image.png](https://cdn.nlark.com/yuque/0/2022/png/12747730/1652528196119-b94ba63b-92bc-4182-ab93-91414349d63d.png#clientId=u681eaa93-d1f2-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=333&id=ufa884f9e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=998&originWidth=1668&originalType=binary&ratio=1&rotation=0&showTitle=false&size=85375&status=done&style=none&taskId=u16cf6b52-bb66-4181-83e5-feeb340c894&title=&width=557)
@SuppressWarnings("serial")
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
}
调用 FrameworkServlet 类的 initServletBean()方法
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
@Override
protected final void initServletBean() throws ServletException {
// 记录开始时间
long startTime = System.currentTimeMillis();
try {
// 创建或刷新WebApplicationContext实例并对Servlet功能所有的变量进行初始化
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
}
}
调用 initWebApplicationContext() 方法
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
protected WebApplicationContext initWebApplicationContext() {
// 获取得到 WebApplicationContext 对象
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果构造方法中已经传入webApplicationContext 属性,则直接使用
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
// 方式二
if (wac == null) {
wac = findWebApplicationContext();
}
// 方式三,一般使用方式三,因为在方式一、二中是找不对象
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
// 由具体的实现类进行实现,SpringMvc的9大组件就是在这里调用该方法,在子类中实现的,后面有详细分析
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
}
调用configureAndRefreshWebApplicationContext() 方法,进入到Spring的启动流程中
入口为上面initWebApplication 中的createWebApplicationContext()方法
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(getEnvironment());
wac.setParent(parent);
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
configureAndRefreshWebApplicationContext(wac);
return wac;
}
wac.refresh();
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
wac.refresh();
}
3. SpringMvc 的核心类-DispatherServlet
SpringMvc 实质就是一个servlet,即DispatcherServlet。DispatcherServlet 继承和实现多个父类和父接口。最顶层的接口就是Servlet。从一开始接触Servlet知道,Servlet最核心的就是它的生命周期,初始化 -> 处理请求-> 销毁。
来看一下Servlet接口和对应的方法: ```java
package javax.servlet;
import java.io.IOException;
/**
- Defines methods that all servlets must implement. /
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
看到servlet 最核心的方法: init() 初始化, service() 处理请求,destroy() 销毁。 在SpringMvc中,核心类DispatcherServlet 继承了 HttpServletBean, 实现了其父类的init() 方法,完成整个SpringMvc的启动和9大内置对象的加载。<br />继续跟进HttpServletBean类的init()方法,可以看到调用了本类中的initServletBean()方法,由具体子类实现
```java
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
public final void init() throws ServletException {
// Let subclasses do whatever initialization they like.
initServletBean();
}
protected void initServletBean() throws ServletException {
}
initServletBean() 方法的唯一实现者,即DisPatchServlet的夫类FrameworkServlet。详细看一下这个类的实现,也是SpringMvc 的核心入口方法调用:
protected WebApplicationContext initWebApplicationContext() {
// 获取得到 WebApplicationContext 对象
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//如果构造方法中已经传入webApplicationContext 属性,则直接使用
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
// 第一步: 创建webApplicationContenxt 对象,一步一步调用最后执行到Spring 的refresh()方法
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
// 第二步: 调用SpingMvc的onRefresh()方法,进行9大内置对象的加载
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
//.........
return wac;
}
跟进两个核心方法一:createWebApplicationContext()方法,发现最后执行到configableAndRefreshWebApplicationContext()方法中,
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
//.........
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// 第一步: 初始化环境资源
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//
postProcessWebApplicationContext(wac);
//
applyInitializers(wac);
//第四步: 调用Spring的底层方法
wac.refresh();
}
根据核心方法二:onRefresh()方法,在FrameworkServlet 类中是一个抽象方法没有具体的实现,再一步跟进发现他最后的实现类是DispatcherServlet,也就是SpringMvc的核心类之一,根据代码如下:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
*/
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
二、SpringMvc 请求处理流程
一、SpringMvc 基本流程
![image.png](https://cdn.nlark.com/yuque/0/2022/png/12747730/1652152113936-1352bd69-50f2-4a77-a41e-a2b80e3b47ff.png#clientId=u7e5a1957-ab4f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=322&id=ue8ba96c1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=644&originWidth=1184&originalType=binary&ratio=1&rotation=0&showTitle=false&size=196149&status=done&style=none&taskId=u623f2086-8427-49ca-847a-9db18d2152b&title=&width=592)<br />①:DispatcherServlet是SpringMVC中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件。<br />②:HanlerMapping是SpringMVC中完成url到Controller映射的组件。DispatcherServlet接收request,然后从HandlerMapping查找处理request的controller.<br />③:Cntroller处理request,并返回ModelAndView对象,Controller是SpringMVC中负责处理request的组件(类似于struts2中的Action),ModelAndView是封装结果视图的组件。<br />④ ⑤ ⑥:视图解析器解析ModelAndView对象并返回对应的视图给客户端。
二、请求处理流程
2.1 请求处理流程
- 浏览器发起请求
- 进入到FrameworkServlet 类中(DispatcherServlet 的父类),执行service 方法
- 获得请求方式(get、post、或者其他)
- super.service
- 根据请求方式进行执行 doXXX()方法,每个doXXX()方法中调用同一个方法,processRequest()
- processRequest()方法执行处理
- doService(request,response)
- 执行DispathcherServlet.doService()
- 逻辑处理和一些属性设置
- 调用doDispatcher 方法
执行doDispatcher()方法,最核心和最关键的方法
- checkMultipart()检查请求是否为文件上传请求
- getHandler() 获取请求对应的HandlerExecutionChain 对象(根据请求获取到对应处理请求的Controller 类和具体处理请求的方法)
- getHandlerAdapter(mapperHandler.getHadler()) 获取处理器适配器
- getLastModified() 获取请求中服务器最后被修改时间
- mv = ha.handle(); 真正的调用handle 方法,也就是执行对应的方法,并返回视图。
processDispatchResult(); // 处理返回结果,包括处理异常,渲染页面,触发Interceptor的afterCompletion
2.2 请求处理流程源码分析
1. 浏览器发起请求
2.代码调用执行到FrameWorkServlet 中的 service() 方法中
获取方法,得到请求方式
调用 super.service()方法,因为详细的调用进入到了tomcat中 ```json @Override protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
} else {
super.service(request, response);
} }
> **在FrameworkServlet 中有多个doXXX()方法,其实每个方法都是对应的一种请求处理方式,创建的请求方式有:get、post、delete等,也就相应的存在 doGet()方法、doPost()方法等。**
> 1. **所有的doXXX() 方法中都调用了同一个方法 processRequest(request,response)**
> 1. **processRequest 方法都统一调用了一个doService()方法**
3. 调用FrameWorkServlet的doGet()方法
```json
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
调用 doServcie()方法 ```json 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 | 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);
} }
<a name="tpDwQ"></a>
#### 3. 调用DispatcherServlet中的 doService()方法主要通过子类实现完成
```json
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//........
try {
doDispatch(request, response);
}
// ........
}
4.执行完成SpringMvc 核心方法doDispatcher()
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 {
// 检查是否是文件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3.获取得到 适配器
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;
}
// 4. 执行处理并返回ModleAndView
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) {
// 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);
}
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);
}
}
}
}
2.3 doDispatcher 处理流程
三、SpringMvc 工作机制
在容器初始化时会建立所有url和controller的对应关系,保存到Map<url,controller>中。tomcat启动时会通知spring初始化容器(加载bean的定义信息和初始化所有单例bean),然后springmvc会遍历容器中的bean,获取每一个controller中的所有方法访问的url,然后将url和Controller保存到一个Map中;
这样就可以根据request快速定位到Controller,因为最终处理request的是Controller中的方法,Map中只保留了url和Controller中的对应关系,所以要根据request的url进一步确认Controller中的method,这一步工作的原理就是拼接Controller的url(Controller上@RequestMapping的值)和方法的url(method上@RequestMapping的值),与request的url进行匹配,找到匹配的那个方法;
确定处理请求的method后,接下来的任务就是参数绑定,把request中参数绑定到方法的形式参数上,这一步是整个请求处理过程中最复杂的一个步骤。SpringMVC提供了两种request参数与方法形参的绑定方法:
① 通过注解进行绑定 @RequestParam
② 通过参数名称进行绑定.
使用注解进行绑定,我们只要在方法参数前面声明@RequestParam(“a”),就可以将request中参数a的值绑定到方法的该参数上。使用参数名称进行绑定的前提是必须要获取方法中参数的名称,Java反射只提供了获取方法的参数的类型,并没有提供获取参数名称的方法。SpringMVC解决这个问题的方法是用asm框架读取字节码文件,来获取方法的参数名称。asm框架是一个字节码操作框架,关于asm更多介绍可以参考它的官网。个人建议,使用注解来完成参数绑定,这样就可以省去asm框架的读取字节码的操作。
四、源码分析
根据工作机制三部分来分析SpringMVC的源码:
其一 ApplicationContext初始化时建立所有url和controller类的对应关系(用Map保存);
其二 根据请求url找到对应的controller,并从controller中找到处理请求的方法;
其三 request参数绑定到方法的形参,执行方法处理请求,并返回结果视图;
第一步:SpringMVC 的初始化
1. 初始化Springmvc 9大组件
- 初始化入口:initWebApplicationContext ```json
FrameworkServlet.initWebApplicationContext(){ onRefresh(wac) }
```初始化9大组件流程 ```json
DispatcherSerclet extends FrameworkServlet
实现 FrameworkServlet 类的 onRefresh() 方法@Override protected void onRefresh(ApplicationContext context) {
initStrategies(context);
} /**
- Initialize the strategy objects that this servlet uses.
May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { // 初始化 initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); // 初始化 HandlerMappings:映射器,用来将对应的request 请求跟controller进行对应 initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); } ```
2. 初始化处理器映射器,建立Map
的关系
initHandlerMappings =》 BeanNameUrlHandlerMapping ```json
- 第一步:getDefaultStrategies
private void initHandlerMappings(ApplicationContext context) {
}this.handlerMappings = null;
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
}
}
- 第二步:createDefaultStrategy
/**
- Create a List of default strategy objects for the given strategy interface.
The default implementation uses the “DispatcherServlet.properties” file (in the same
- package as the DispatcherServlet class) to determine the class names. It instantiates
- the strategy objects through the context’s BeanFactory.
- @param context the current WebApplicationContext
- @param strategyInterface the strategy interface
- @return the List of corresponding strategy objects
*/
@SuppressWarnings(“unchecked”)
protected
List getDefaultStrategies(ApplicationContext context, Class strategyInterface) { String key = strategyInterface.getName(); String value = defaultStrategies.getProperty(key); if (value != null) {
// 核心方法String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
} else {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(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
} }return new LinkedList<>();
- 第三步:createBean()
protected Object createDefaultStrategy(ApplicationContext context, Class<?> clazz) { return context.getAutowireCapableBeanFactory().createBean(clazz); }
4.第四步:AbstractAutowireCapableBeanFactory.createBean() 进入到Spring源码中,创建bean的详细流程
5.第五步:doCreateBean() protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
//1.创建bean对象 Instantiate the bean.
instanceWrapper = createBeanInstance(beanName, mbd, args);
//2. Initialize the bean instance.
Object exposedObject = bean;
try {
//2. 填充属性
populateBean(beanName, mbd, instanceWrapper);
//3. 初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
6.第六步: initializeBean protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// Aware接口处理器,调用BeanNameAware,BeanClassLoaderAware、beanFactoryAware
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 将BeanPostProcessors 应用到给定定现有Bean实列,调用他们的postProcessorBeforeInitialization初始化方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
return wrappedBean;
}
applyBeanPostProcessorsBeforeInitialization @Override public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
ApplicationContextAwareProcessorpost.ProcessBeforeInitialization()
@Override @Nullable public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
- invokeAwareInterfaces
private void invokeAwareInterfaces(Object bean) {
}if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
ApplicationObjectSupport.setApplicationContext()
@Override public final void setApplicationContext(@Nullable ApplicationContext context) throws BeansException { initApplicationContext(context); }
回到SpringMVC 源码中:#AbstractDetectingUrlHandlerMapping.initApplicationContext @Override public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
11.1 #AbstractHandlerMapping.initApplicationContext @Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); detectMappedInterceptors(this.adaptedInterceptors); initInterceptors(); }
11.2#AbstractDetectingUrlHandlerMapping.detectHandlers()
// 获取 applicationContext应用上下文
ApplicationContext applicationContext = obtainApplicationContext();
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + applicationContext);
}
// 获取ApplicationContext容器中所有bean的Name
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
// 遍历beanNames,并找到这些bean对应的url
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
registerHandler()
//到此完成 BeanNameUrlHandlerMapping 的初始化
private final Map
2. initHandlerMappings =》 RequestMappingHandlerMapping
```json
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
}
1. #afterPropertiesSet
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
2. #initHandlerMethods
/**
* Scan beans in the ApplicationContext, detect and register handler methods.
* @see #isHandler
* @see #detectHandlerMethods
* @see #handlerMethodsInitialized
*/
protected void initHandlerMethods() {
for (String beanName : beanNames) {
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
3. #detectHandlerMethods
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
Class<?> userType = ClassUtils.getUserClass(handlerType);
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
}
catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isDebugEnabled()) {
logger.debug(methods.size() + " request handler methods found on " + userType + ": " + methods);
}
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
4. #registerHandlerMethod
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
this.mappingRegistry.register(mapping, handler, method);
}
5. 完成基本属性值的设置
class MappingRegistry {
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
上面的代码就是两种初始化HandlerMapping 的方式,但是在SpringMvc的实际应用中使用的都是第二种方式。我们首先看第一个步骤,也就是建立Map<url,controller>关系的部分。第一部分的入口类为ApplicationObjectSupport的setApplicationContext方法。setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子类AbstractDetectingUrlHandlerMapping实现了该方法,所以我们直接看子类中的初始化容器方法。
public void initApplicationContext() throws ApplicationContextException {
super.initApplicationContext();
detectHandlers();
}
/**
* 建立当前ApplicationContext中的所有controller和url的对应关系
*/
protected void detectHandlers() throws BeansException {
ApplicationContext applicationContext = obtainApplicationContext();
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(applicationContext, Object.class) :
applicationContext.getBeanNamesForType(Object.class));
// Take any bean name that we can determine URLs for.
for (String beanName : beanNames) {
String[] urls = determineUrlsForHandler(beanName);
if (!ObjectUtils.isEmpty(urls)) {
// URL paths found: Let's consider it a handler.
registerHandler(urls, beanName);
}
}
if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
}
}
/** 获取controller中所有方法的url,由子类实现,典型的模板模式 **/
protected abstract String[] determineUrlsForHandler(String beanName);
determineUrlsForHandler(String beanName)方法的作用是获取每个controller中的url
/**
* 获取controller中所有的url
*/
protected String[] determineUrlsForHandler(String beanName) {
// 获取ApplicationContext容器
ApplicationContext context = getApplicationContext();
//从容器中获取controller
Class<?> handlerType = context.getType(beanName);
// 获取controller上的@RequestMapping注解
RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
if (mapping != null) { // controller上有注解
this.cachedMappings.put(handlerType, mapping);
// 返回结果集
Set<String> urls = new LinkedHashSet<String>();
// controller的映射url
String[] typeLevelPatterns = mapping.value();
if (typeLevelPatterns.length > 0) { // url>0
// 获取controller中所有方法及方法的映射url
String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
for (String typeLevelPattern : typeLevelPatterns) {
if (!typeLevelPattern.startsWith("/")) {
typeLevelPattern = "/" + typeLevelPattern;
}
boolean hasEmptyMethodLevelMappings = false;
for (String methodLevelPattern : methodLevelPatterns) {
if (methodLevelPattern == null) {
hasEmptyMethodLevelMappings = true;
}
else {
// controller的映射url+方法映射的url
String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
// 保存到set集合中
addUrlsForPath(urls, combinedPattern);
}
}
if (hasEmptyMethodLevelMappings ||
org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
addUrlsForPath(urls, typeLevelPattern);
}
}
// 以数组形式返回controller上的所有url
return StringUtils.toStringArray(urls);
}
else {
// controller上的@RequestMapping映射url为空串,直接找方法的映射url
return determineUrlsForHandlerMethods(handlerType, false);
}
} // controller上没@RequestMapping注解
else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
// 获取controller中方法上的映射url
return determineUrlsForHandlerMethods(handlerType, false);
}
else {
return null;
}
}
到这里HandlerMapping组件就已经建立所有url和controller的对应关系。
第二步:根据访问url找到对应controller中处理请求的方法
下面我们开始分析第二个步骤,第二个步骤是由请求触发的,所以入口为DispatcherServlet.DispatcherServlet的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,我们查看doDispatch()的源代码。
/** 中央控制器,控制请求的转发 **/
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
int interceptorIndex = -1;
try {
ModelAndView mv;
boolean errorView = false;
try {
// 1.检查是否是文件上传的请求
processedRequest = checkMultipart(request);
// 2.取得处理当前请求的controller,这里也称为hanlder,处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和interceptors.
mappedHandler = getHandler(processedRequest, false);
// 如果handler为空,则返回404
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
//3. 获取处理request的处理器适配器handler adapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 处理 last-modified 请求头
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;
}
}
// 4.拦截器的预处理方法
HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();
if (interceptors != null) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
return;
}
interceptorIndex = i;
}
}
// 5.实际的处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 结果视图对象的处理
if (mv != null && !mv.hasView()) {
mv.setViewName(getDefaultViewName(request));
}
// 6.拦截器的后处理方法
if (interceptors != null) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);
}
}
}
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
if (mv != null && !mv.wasCleared()) {
render(mv, processedRequest, 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");
}
}
// 请求成功响应之后的方法
triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);
}
第2步:getHandler(processedRequest)方法实际上就是从HandlerMapping中找到url和controller的对应关系。这也就是第一个步骤:建立Map
第三步、动态代理调用处理请求的方法,返回结果视图
上面的方法中,第2步其实就是从第一个步骤中的Map
中取得Controller,然后经过拦截器的预处理方法,到最核心的部分—第5步调用controller的方法处理请求。在第2步中我们可以知道处理request的Controller,第5步就是要根据url确定Controller中处理请求的方法,然后通过反射获取该方法上的注解和参数,解析方法和参数上的注解,最后反射调用方法获取ModelAndView结果视图。因为上面采用注解url形式说明的,所以我们这里继续以注解处理器适配器来说明。第5步调用的就是AnnotationMethodHandlerAdapter的handle().handle()中的核心逻辑由invokeHandlerMethod(request, response, handler)实现。
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();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}