文章目录
一、核心概念
1.1 配置文件
1. Web.xml :负责配置Spring和Spring MVC,重点配置了两个地方,一个是contextConfigLocation负责将Web与Spring结合,一个是DispatcherServlet,负责整合Spring MVC
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>Web Application</display-name>
<!--配置 spring-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--配置前置控制器 -->
<servlet>
<servlet-name>SpringDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup> 1 </load-on-startup>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 引入spring mvc配置文件 -->
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SpringDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--2.上下文载入器,载入除DispatcherServlet加载的配置文件之外的其他配置文件-->
<listener>
<listener-class>org.Springframework.web.context.ContextLoadListener</listener-class>
</listener>
</web-app>
2. spring的配置文件(applicationContext.xml)
由于web.xml配置的文件名为applicationContext.xml,所以Web项目只能扫描到名为applicationContext.xml的配置文件给spring使用。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描除controller包外所有使用注解的类型 -->
<context:component-scan base-package="com.ssm">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置数据库相关参数properties的属性:${url} -->
<!-- <context:property-placeholder location="classpath:jdbc.properties" /> -->
<!-- 配置基于注解的声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
3. spring mvc的 配置文件(spring-servlet.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- 配置SpringMVC -->
<!-- 1.开启SpringMVC注解模式 -->
<!-- 简化配置:
(1)自动注册DefaultAnootationHandlerMapping,AnotationMethodHandlerAdapter
(2)提供一些列:数据绑定,数字和日期的format @NumberFormat, @DateTimeFormat, xml,json默认读写支持
-->
<mvc:annotation-driven />
<!-- 2.静态资源默认servlet配置
(1)加入对静态资源的处理:js,gif,png
(2)允许使用"/"做整体映射
-->
<mvc:default-servlet-handler/>
<!-- 3.配置jsp 显示ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 4.扫描web相关的bean -->
<context:component-scan base-package="com.ssm.controller" />
</beans>
1.2 ContextLoadListener
该类实现了ServletContextListener接口,ServletContextListener接口提供了一个在Web容器启动时,向其中添加信息的机会。
所以 ContextLoadListener 该类作用是启动Web容器时,自动装配WebApplicationContext (完成spring容器的初始化),创建ioc容器并初始化,并将WebApplicationContext注入到ServletContext中。(WebApplicationContext是ApplicationContext的一个子类,ServletContext是每个Web应用的上下文,每个Web应用与一个ServletContext相关联。)
容器启动时,会发布事件(beforeContextInitialized、afterContextInitialized),然后调用ContextLoadListener的contextInitialized()方法,该方法调用initWebApplicationContext()方法,该方法首先验证WebApplicationContext是否存在,不存在才去创建,然后注入到ServletContext中,key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE所表示的值。
在创建WebApplicationContext过程中会读取ContextLoader.properties文件,从中取出XmlWebApplicationContext的全类名,然后通过反射创建一个WebApplicationContext的实现类。
创建完成后,会调用configureAndRefreshWebApplicationContext(cwac, servletContext)方法进行ioc容器的初始化。(最终会调用abstractApplicationContext的refresh()方法和FrameworkServlet的onRefresh()方法)
1.3 DispatcherServlet
DispatcherServlet配置的load-on-startup为1,即web容器启动时,就创建该实例,并执行init()方法。
DispatcherServlet的init()方法将web.xml中配置的servlet类型实例转换为BeanWrapper类型。然后调用initServletBean()方法初始化spring mvc容器。(最终会调用abstractApplicationContext的refresh()方法和FrameworkServlet的onRefresh()方法去初始化spring mvc容器,该容器为前面ContextLoadListener初始化的 ioc 容器的子容器)
二、总体流程(**)
主要介绍通过配置文件来启动web项目的流程,如果通过注解的方式的话,主要通过spi机制,导入WebApplicationInitializer的实现类。
2.1 编写配置文件
编写web.xml、spring相关的xml、springmvc相关的xml、整合mybatis的xml等等
2.2 启动tomcat启动
Tomcat对应于一个Catalina对象,一个Catalina对应一个Server,一个Server包含多个service,一个Service包含一个Container和多个Connector,Connector负责连接相关的工作,例如Http的Connector就负责将Socket接收到的Http请求封装为HttpRequest或者将HttpResponse转换为SokcetResponse发送给对应的Socket。Container则相当于一个Web容器。
一个Container包含多个Host,Host包含多个Context,每个Context包含多个Wrapper。
Wrapper相当于我们编写的每个项目中的Servlet
Context相当于我们编写的一个项目或者对应于一个web.xml文件
tomcat下的webapps目录下的每个目录都相当于一个context
2.2.1 bootstrap启动
tomcat启动就是执行bootstrap的main方法。该方法创建一个Bootstrap对象,调用init()方法。init方法初始化一个ClassLoader,并通过ClassLoader创建一个Catalina对象,赋给catalinaDemon变量。
2.2.2 Catalina启动
当执行Bootstrap传入的命令参数为start时,启动catalina。根据conf/server.xml文件创建一个server对象,并调用server的init()方法。(该配置文件在tomcat的conf目录下)
2.2.3 Server启动
server的 init() 方法和 strat() 方法循环调用其含有的所有 Service 的 init() 方法和 start() 方法。
server.xml中默认只有一个name为"catalina"的service,即每个tomcat服务器默认只有一个service。
该service有两个connector分别负责处理http请求和ajp请求,监听的端口号为8080和8009
该service有一个host,对应localhost这个虚拟站点
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
2.2.4 Service启动
Service的init()和start()会分别调用container、executors、mapperListener、connectors的init()和start()。
mapperListener是Mapper的监听器,负责监听container容器的变化,executors为connectors中管理线程的线程池。
container管理servlet以及处理request,Connectors用于处理连接相关的事情。
tomcat启动时会初始化Context并启动,在启动Context的时候,加载web.xml文件。
2.2.4 Container启动
会获取所有的子容器并启动。
2.2.4 Context启动
这部分是我们关注的重点,即我们真正的web应用。
2.3 Context启动的详细流程
2.3.1 initInternal()
首先调用initInternal()方法进行一些初始化操作(在tomcat的init()阶段)。
2.3.2 startInternal()
当bootstrap调用start()方法后,一层一层的向下调用,一直到调用standardContext的startInternal()方法。
protected synchronized void startInternal() throws LifecycleException {
//...省略一部分
try {
if (ok) {
//...
//加载web.xml文件,此处会发布一个configure_start事件
//而该事件的监听者为ContextConfig,所以会调用ContextConfig的lifecycleEvent()
//而contextConfig的lifecycleEvent()根据事件的分类,为configure_start事件
//调用webconfig()方法,解析web.xml文件
//将context-param元素存放到context中的parameters中,
//将listener元素放到context中的applicationListener中
//将filter放到context中的filterConfigs中
//将servlet元素封装为wrapper添加到context中的child属性
this.fireLifecycleEvent("configure_start", (Object)null);
//...
}
//....
//将parameters存放到servletContext中,前面的代码会调用getServletContext,
//如果servletContext不存在,则创建一个ApplicationContext
this.mergeParameters();
//启动所有的applicationListener中的监听器
if (ok && !this.listenerStart()) {
log.error(sm.getString("standardContext.listenerFail"));
ok = false;
}
//...
//启动所有的filter
if (ok && !this.filterStart()) {
log.error(sm.getString("standardContext.filterFail"));
ok = false;
}
//将该context的child属性中包含的子容器(即servlet)
//封装为wrapper然后加载(会判断load-on-startup是否大于0,大于0才加载)
if (ok && !this.loadOnStartup(this.findChildren())) {
log.error(sm.getString("standardContext.servletFail"));
ok = false;
}
super.threadStart();
} finally {
this.unbindThread(oldCCL);
}
//。。。。
}
2.3.3 listenerStart()
我们在web.xml中配置了ContextLoadListener,所以在此方法中,至少会执行ContextLoadListener的contextInitialized(ServletEvent event)方法。
ContextLoadListener的contextInitialized()方法完成了ApplicationContext的创建和初始化
- createWebApplicationContext(servletContext)----完成创建ioc容器的创建
- configureAndRefreshWebApplicationContext(cwac, servletContext)-----完成ioc容器的初始化
/** ContextLoadListener的contextInitialized(ServletEvent event)
*. 初始化web应用上下文
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
==========================================
//主要关注两个重点:
public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {
throw new IllegalStateException(
"Cannot initialize context because there is already a root application context present - " +
"check whether you have multiple ContextLoader* definitions in your web.xml!");
}
//....一些其他代码
long startTime = System.currentTimeMillis();
try {
//创建应用上下文
if (this.context == null) {
//创建应用上下文的时候,会调用父类ContextLoader的方法,
//该方法会读取ContextLoader.properties,从中取出ApplicationContext实现类的全类名
//然后通过反射创建实例
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);
}
//初始化ApplicationContext
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
//。。。。。省略一些代码
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}
1.ioc容器创建
/**
* 返回WebApplicationContext的实现类,否则返回默认XmlWebApplicationContext或者通过开发者自定义.
* @param servletContext 当前servlet上下文
* @return WebApplicationContext的实现类
*/
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);//"contextClass"
if (contextClassName != null) {
try {
return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load custom context class [" + contextClassName + "]", ex);
}
}
else { // 否则,读取ContextLoader.properties中配置的key=WebApplicationContetxt.class.getName()的value,这里返回XmlWebApplicationContext.class.getName()
contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
try {
return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
throw new ApplicationContextException(
"Failed to load default context class [" + contextClassName + "]", ex);
}
}
}
///ContextLoader.properties中的内容
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
- ioc容器初始化
//configureAndRefreshWebApplicationContext
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
if (idParam != null) {
wac.setId(idParam);
}
else {
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
//从servletContext中得到web.xml中的<context-param>标签,得到name为contextConfigLocation
//value为applicationContext.xml的文件,存到WebApplicationContext中
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
//调用父类AbstractApplicationContext的refresh()方法,进行spring ioc容器的初始化。
wac.refresh();
}
这样就完成了Tomcat和spring的连接。
2.3.4 filterStart()
启动过滤器,过滤一些用户不合法的请求等等。
2.3.5 loadOnStartup()
我们在web.xml中配置了DispatcherServlet,所以此处至少会初始化并启动DispatcherServlet。
loadOnStartup()会从StandardContext的child容器中取出解析web.xml所获得的所有servlet,将其封装为Wrapper对象,并判断load-on-startup属性是否大于0,大于0则加载,加载会调用load()方法。
@Override
public synchronized void load() throws ServletException {
//加载servlet类,并返回一个实例
instance = loadServlet();
if (!instanceInitialized) {
//进行初始化
initServlet(instance);
}
if (isJspServlet) {
StringBuilder oname = new StringBuilder(getDomain());
oname.append(":type=JspMonitor");
oname.append(getWebModuleKeyProperties());
oname.append(",name=");
oname.append(getName());
oname.append(getJ2EEKeyProperties());
try {
jspMonitorON = new ObjectName(oname.toString());
Registry.getRegistry(null, null).registerComponent(instance, jspMonitorON, null);
} catch (Exception ex) {
log.warn("Error registering JSP monitoring with jmx " + instance);
}
}
}
由于DispatcherServlet继承了FrameworkServlet,FrameworkServlet又继承了HttpServletBean。
所以DispatcherServlet执行init()方法时,调用的是HttpServletBean的init()方法。
HttpServletBean的init()创建一个BeanWrapper(),并进行了初始化,然后调用initServletBean()方法,该方法是个空方法,由FrameworkServlet实现。
FrameworkServlet的initServletBean()的核心方法为:
- initWebApplicationContext(),创建Spring mvc容器,即spring ioc容器的子容器,然后重写onRefresh()方法,本类中onRefresh()方法是空的,由DispatcherServlet实现。
- initFrameworkServlet()
其中initFrameworkServlet()为空方法,所以核心逻辑为initWebApplicationContext()。
protected WebApplicationContext initWebApplicationContext() {
// 1)获取由ContextLoaderListener创建的根IoC容器
// 2)获取根IoC容器有两种方法,还可通过key直接获取
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
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) {
// 如果当前Servelt存在一个WebApplicationContext即子IoC容器,
// 并且上文获取的根IoC容器存在,则将根IoC容器作为子IoC容器的父容器
// 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) {
// 如果当前Servlet不存在一个子IoC容器则去查找一下
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// 如果仍旧没有查找到子IoC容器则创建一个子IoC容器
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// 调用子类DispatcherServlet覆盖的onRefresh方法完成“可变”的初始化过程
// 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);
}
return wac;
}
/**
* Template method which can be overridden to add servlet-specific refresh work.
* Called after successful context refresh.
* <p>This implementation is empty.
* @param context the current WebApplicationContext
* @see #refresh()
*/
protected void onRefresh(ApplicationContext context) {
// For subclasses: do nothing by default.
}
@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
...
/**
* This implementation calls {@link #initStrategies}.
*/
@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);
}
...
}
/**
onRefresh()方法直接调用了initStrategies()方法.
该方法用于初始化创建multipartResovle来支持图片等文件的上传、
本地化解析器、主题解析器、HandlerMapping处理器映射器、H
andlerAdapter处理器适配器、异常解析器、视图解析器、flashMap管理器等,
这些组件都是SpringMVC开发中的重要组件,相关组件的初始化创建过程均在此完成。
/
2.4 总结
2.4.1 启动流程
-
编写配置文件,web.xml,spring相关配置,spring mvc相关配置等。
-
启动tomcat,从bootstrap的main方法开始,一级一级的往下调用startIniternal()方法。
-
当调用到context这一层时,调用context的startIniternal()方法时,会加载web.xml文件,正式开始我们的项目的加载。
3.1 :通过发布configure_start事件,由ContextConfig类监听,然后读取ContextLoader.properties文件,取出ApplicationContext实现类的全类名,通过反射创建ApplicationContext实例。
此过程会解析web.xml,然后将context-param、servlet、fliter、listener等标签的内容放入到ServletContext对应的成员变量中,配置最少含有ContextLoadListener和DispatcherServlet,分别用于ioc容器创建和springmvc子容器的创建。
3.2: 启动所有监听器,此处至少含有一个ContextLoadListener,所以会调用其contextInitialized(ServletEvent event)方法。
通过执行createWebApplicationContext(servletContext)方法完成ioc容器的创建。
通过configureAndRefreshWebApplicationContext(cwac, servletContext)完成ioc容器初始化,在该方法内会执行AbstractApplicationContext的refresh()方法。
3.3 启动所有的fliter,过滤一些不合法的请求等等
3.4 启动所有在web.xml中配置的servlet,调用其load()方法,此处至少含有DispatcherServlet。
调用initWebApplicationContext()方法,创建spring mvc容器,并且DispatcherServlet重写了onRefresh()方法,所以创建完spring mvc容器,进行初始化,调用AbstractApplicationContext的refresh()方法的时候会调用DispatcherServlet重写的onRefresh()方法,进行mvc的一些组件的初始化。(例如 handlerMapping、Handler等等)
-
项目启动完成,提供服务。
ioc容器和spring mvc容器的关系:
tomcat的启动流程图:
2.4.2 服务流程
当我们发送请求:http://localhost:8080/articles/67/comment
- 请求发送到服务器,服务器将请求分发到8080端口,客户端与监听8080端口的socket建立连接,Tomcat负责处理http请求的Connector 从socket 中取出数据封装为 ServletRequest ,交给Engine(即Container)处理。
- Engine获取到request,根据Host匹配自己所含有的所有虚拟站点,匹配到名为localhost的站点,将请求交给host
- host匹配context路径,匹配不到交给主应用去处理(即webapp下的root目录),匹配到/articles这个context后,交给对应context去处理。
- context处理实际上是交给DispatcherServlet处理 (即DispatcherServlet的service()方法,该方法来自其父类FrameworkServlet,其又会先调用super.service())。所以,DispatcherServlet处理请求时,先调用HttpServletBean的service()方法,将ServletRequest转换为HttpServletRequest,然后Framework的service()又调用了processRequest(request, response),根据request的请求方法的不同,去调用对应的doGet、doPost、doPut等方法。
- Framework的doGet、doPost等方法再调用doSerivce()方法,该方法由DispatcherServlet重写
- 然后检查是否是上传请求。
- 然后查找所有的HandlerMapping,查找与/{articlesId}/comment对应的handler,找出对应的Handler处理链(HandlerExecutionChain对象),返回给DispatcherServlet
- DispatcherServlet得到handler链,然后将request和handler链交给HandlerAdapter去处理。
- HandlerAdapter执行链中每一个Handler对应的方法,得到结果并返回(ModelAndView对象)
10.DisPatcherServlet接收到结果后,交给ViewResolver去解析ModelAndView,返回给DispatcherServlet解析出的View对象,然后进行渲染,返回给客户端。
tomcat处理请求:
spring mvc处理请求:
参考:
StandardContext启动详解
《spring源码深度解析》
《看透spring mvc》
https://www.cnblogs.com/yy3b2007com/p/12219805.html#autoid-2-0-0