SpringMVC 框架搭建
1. pom文件添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<groupId>com.ming</groupId>
<artifactId>spring4-mvc-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.24.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.11.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
</dependencies>
<build>
<finalName>spring4-mvc-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. web.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
metadata-complete="true">
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 全局配置参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<!-- 配置filter, 将post请求转换为put或delete请求 -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置DispatcherServlet,前置控制器 -->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 静态资源的显示 begin -->
<servlet-mapping >
<servlet-name >default </servlet-name >
<url-pattern >*.png</url-pattern>
</servlet-mapping>
<servlet-mapping >
<servlet-name >default </servlet-name >
<url-pattern >*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping >
<servlet-name >default </servlet-name >
<url-pattern >*.gif</url-pattern>
</servlet-mapping>
<!-- 静态资源的显示 end -->
</web-app>
3. applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-4.3.xsd "
default-lazy-init="true">
<!-- 扫描路径 -->
<context:component-scan base-package="com.ming.spring4"/>
</beans>
4. 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-4.3.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"
default-lazy-init="true">
<!-- 扫描路径 -->
<context:component-scan base-package="com.ming.spring4.*.controller"/>
<mvc:annotation-driven />
<!-- 视图解析器类 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 加载主题资源文件 -->
<bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
<!-- 指定文件前缀,即文件所在目录,如果放在classpath下,value为空 -->
<property name="basenamePrefix" value="theme."/>
</bean>
<!-- 使用FixedThemeResolver -->
<bean id="themeResolver" class="org.springframework.web.servlet.theme.FixedThemeResolver">
<!-- 定义theme文件名字 -->
<property name="defaultThemeName" value="myTheme"/>
</bean>
</beans>
5. 项目层级架构
SpringMVC 初始化
以上是 Web 容器的初始化流程。在 web.xml 配置文件中,有两个重要的类:ContextLoaderListener 与 DispatchServlet。其中,ContextLoaderListener 主要是加载 Dao、Service、DataSource 等组件;DispatcherServlet 主要是加载 Controller 等前台相关的组件。DispatcherServlet 类和ContextLoaderListener 类的关系图:
用 ContextLoaderListener 初始化各组件的上下文,接着使用 DispatcherServlet 来初始化 WebMVC 的上下文。
1. ContextLoaderListener
在 Servlet API 中有一个 ServletContextListener 接口,它能够监听 ServletContext 对象的生命周期,实际上就是监听 Web 应用的生命周期。当 Servlet 容器启动或终止 Web 应用时,会触发 ServletContextEvent 事件,该事件会由 ServletContextListener 来处理。由上图也可知 ServletContextListener 接口处理 ServletContextEvent 事件主要有两个方法 contextInitialized() 与 contextDestroyed()。
ContextLoaderListener 监听器的作用是启动 Web 容器时,自动装配 ApplicationContext 的配置信息。因为它实现了 ServletContextListener 接口,在 web.xml 配置了这个监听器时,就会默认执行它实现的方法。由于在 ContextLoaderListener 中继承了 ContextLoader 这个类,所以整个加载配置过程也是由 ContextLoader 来完成的。
以下是 ContextLoader 的源码分析过程:
其中上图提到的 **XmlWebApplicationContext **其实就是读取 /WEB-INF/applicationContext.xml。而接下来我们来看下 ContextLoader.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 {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(sc.getContextPath()));
}
}
wac.setServletContext(sc);
// 获取 “contextConfigLocation” 参数,并设置到WebApplicationContext 参数中,即上面配置文件“applicationContext.xml”
String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
if (configLocationParam != null) {
wac.setConfigLocation(configLocationParam);
}
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
}
customizeContext(sc, wac);
// 调用AbstractApplicationContext.refresh() 方法
wac.refresh();
}
AbstractApplicationContext.refresh 方法的源码很重要
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 刷新前的准备工作
prepareRefresh();
// 获取一个beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 对beanFactory进行初始参数设置
prepareBeanFactory(beanFactory);
try {
// 允许在上下文子类中对 beanFactory 进行后置处理
postProcessBeanFactory(beanFactory);
// 调用在上下文中注册为beanFactory的处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截 bean 创建的Bean处理器
registerBeanPostProcessors(beanFactory);
// 为context初始化消息源
initMessageSource();
// 初始化事件发布器
initApplicationEventMulticaster();
// 初始化其他特定上下文子类中的其他特殊bean
onRefresh();
// 注册事件监听器
registerListeners();
// 实例化剩余的(非惰性初始化)的单例bean
finishBeanFactoryInitialization(beanFactory);
// 最后一步: 发布相应的事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2. DispatcherServlet
在 SpringMVC 架构中,DispatcherServlet 负责请求的分发,起到控制器的作用。下面对此类进行详细的源码分析:
- 由上面的类依赖关系图可知,DispatcherServlet 其实就是一个 Servlet 的子类,所以 DispatcherServlet 的调用就是 Servlet 类的调用过程。
- Servlet 接口有两个核心的方法:init() 和service() 方法,其中 HttpServletBean 重写了 init() 方法。在这部分我们看出其实现的思路:公共的部分统一实现,变化的部分统一来抽象,具体由子类实现。
// HttpServletBean
@Override
public final void init() throws ServletException {
// 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;
}
}
// 具体在子类FrameworkServlet中进行了重写
initServletBean();
}
- FrameworkServlet 提供了整合 web javabean和 Spring application Context 的方案。在源码中可以看到通过执行 initWebApplicationContext() 方法和initFrameworkServlet() 方法实现。而 initWebApplicationContext() 方法调用了一个空方法 onRefresh() 方法,此方法在子类 DispatcherServlet 进行了重写。
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet(); //实现为空
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
// 获取父ApplicationContext, 即ContextLoaderListener加载后的ApplicationContext
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) {
// 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);
}
// 如果webApplicationContext已经存在,则需要进行配置即刷新
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 通过contextAttribute参数获取ApplicationContext
wac = findWebApplicationContext();
}
if (wac == null) {
// 仍然没有context,则根据父上下文创建一个本地Context
wac = createWebApplicationContext(rootContext);
}
// 判断context是否被刷新过,没有则调用DispatcherServlet的onRefresh方法进行刷新
if (!this.refreshEventReceived) {
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;
}
- DispatcherServlet 是 HTTP 请求的中央调度处理器,它将 Web 请求转发 Controller 层处理,提供了敏捷的映射和异常处理机制。其中初始化方法源码如下:
@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) {
// 获取配置文件 multipartResolver 的相关配置,用于文件上传
initMultipartResolver(context);
// 获取配置文件 localeResolver 的相关配置,用于国际化解析器的初始化
initLocaleResolver(context);
// 获取配置文件 themeResolver 的相关配置,用于主题解析器的初始化
initThemeResolver(context);
// 获取配置文件 handlerMapping 的相关配置,用于处理器映射器的初始化
initHandlerMappings(context);
// 获取配置文件 handlerAdapter的相关配置,用于处理器适配器的初始化
initHandlerAdapters(context);
// 获取配置文件 handlerExceptionResolver 的相关配置,用于异常处理解析器的初始化
initHandlerExceptionResolvers(context);
// 获取配置文件 viewNameTranslator 的相关配置, 用于viewNameTranslator的初始化
initRequestToViewNameTranslator(context);
// 获取配置文件 viewResolver 的相关配置, 用于视图解析器的初始化
initViewResolvers(context);
// 获取配置文件 flashMapManager 的相关配置, 用于视图解析器的初始化
initFlashMapManager(context);
}
DispatcherServlet 类中重要属性:
1、HandlerMapping:用于 handlers 映射请求和一系列的对于拦截器的前置处理和后置处理,大部分用 @Controller 注解。
2、HandlerAdapter:帮助DispatcherServlet 处理映射请求处理程序的适配器。
3、ViewResolver:根据实际配置解析实际的 View 类型。
4、ThemeResolver:解决Web应用程序使用的主题,丽日提供个性化布局。
5、MultipartResolver:解析多部分请求,支持从 HTML 表单上传文件。
6、FlashMapManager: 存储并检索用于将一个请求属性传递到另一个请求的 input 和output 的FlashMap,通常用于重定向。
其中 DispatcherServlet 针对以上参数设置默认配置,配置文件在 DispatcherServler.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
SpringMVC 框架执行流程
SpringMVC执行流程:
* 用户发送请求至前端控制器DispatcherServlet。
* DispatcherServlet收到请求调用处理器映射器HandlerMapping。
* 处理映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain。
* DispatcherServlet根据处理器获取请求的处理器适配器HandlerAdapter。并且执行后续一系列的操作,如:参数封装,数据格式转换,数据验证等操作。
* 执行Handler处理器(比如Controller,页面控制器)
* Handler执行完毕返回ModelAndView。
* HandlerAdapter将ModelAndView再返回给DispatcherServlet。
* DispatcherServlet将ModelAndView传给ViewResolver视图解析器。
* ViewResolver解析后返回具体的物理View
* DispatcherServlet对View进行渲染视图
* DispatcherServlet再响应给用户
1. 主流程
DispatcherServler 的源码如下:
@Override
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(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 对request相关属性进行设值
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 {
// 请求的分发处理
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);
}
}
}
}
DispatcherServlet 的请求入口方法为 doService(), 在此方法中对请求参数进行默认值的设置。而具体的请求分发交由 doDispatch() 方法实现。
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 {
// 校验本次请求是否为文件上传, 如果是通过相关解析器进行解析(比如:CommonsMultipartResolver)
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 由当前请求解析获取具体Handler,并进行包装返回HandlerExecutionChain
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据上面获取的Handler,获取处理适配器HandlerAdapter
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;
}
}
// 执行已注册拦截器的preHandle方法(解析Controller前触发)
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 真正调用handle具体逻辑,并返回ModelAndView
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 对于ModelAndView没有视图名,设置为默认的视图名
applyDefaultViewName(processedRequest, mv);
// 执行已注册拦截器的postHandle方法(解析完Controller后,封装渲染View之前触发)
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. 子流程 getHandler() 方法
// DispatcherServlet
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 循环判断HandlerMapping解析出handler
for (HandlerMapping hm : this.handlerMappings) {
if (logger.isTraceEnabled()) {
logger.trace(
"Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
}
// 根据不同的handlerMapping解析请求,并组装成执行链
HandlerExecutionChain handler = hm.getHandler(request);
if (handler != null) {
return handler;
}
}
return null;
}
而 getHandler() 方法又调用了 HandlerMapping 接口的抽象类 AbstractHandlerMapping.getHandler() 方法。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 根据请求对象获取handler处理器
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// 根据bean的name获取对象bean
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
// 获取处理器执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.globalCorsConfigSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 将handler转换为HandlerExecutionChain
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 根据请求路径,判断被哪个拦截器进行了处理,并设置该拦截器
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
3. 子流程 getHandlerAdapter() 方法
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
// 循环访问handlerAdapter的实现类
for (HandlerAdapter ha : this.handlerAdapters) {
if (logger.isTraceEnabled()) {
logger.trace("Testing handler adapter [" + ha + "]");
}
// 判断handler是哪个HandlerAdapter适配解析的,并返回该HandlerAdapter
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. 子流程 processDispatchResult() 方法
对于分发请求处理结果的处理,源码如下:
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);
}
}
// 对ModelAndView进行渲染
if (mv != null && !mv.wasCleared()) {
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) {
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;
// 判断ModelAndView是否为String
if (mv.isReference()) {
// 根据viewName遍历配置的ViewResolver解析出View对象
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 {
// 如果mv是ModelAndView对象,可以直接获取View对象
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;
}
}
总结
SpringMVC 框架的核心为 web.xml, 而 web.xml 配置文件需要配置 ContextLoaderListener,此配置项是为了指定 applicationContext.xml 配置文件,用于扫描 bean 的一些配置。此外 web.xml 配置文件还需要配置 DispatcherServlet,此配置项用于处理前台请求,然后通过HandlerMapping 选取具体的 HandlerAdapter 处理具体的 Controller 层,然后返回 ModelAndView 给前台视图解析器进行渲染,最终在前台展示即可。