⑴ 用户发送请求至前端控制器DispatcherServlet
⑵ DispatcherServlet收到请求调用HandlerMapping处理器映射器。
⑶ 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
⑷ DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
⑸ 执行处理器(Controller,也叫后端控制器)。
⑹ Controller执行完成返回ModelAndView
⑺ HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
⑻ DispatcherServlet将ModelAndView传给ViewReslover视图解析器
⑼ ViewReslover解析后返回具体View
⑽ DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)。
⑾ DispatcherServlet响应用户。
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);//文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
initLocaleResolver(context);//本地化解析
initThemeResolver(context);//主题解析
initHandlerMappings(context);//通过HandlerMapping,将请求映射到处理器
initHandlerAdapters(context);//通过HandlerAdapter支持多种类型的处理器
initHandlerExceptionResolvers(context);//如果执行过程中遇到异常,将交给HandlerExceptionResolver来解析
initRequestToViewNameTranslator(context);//直接解析请求到视图名
initViewResolvers(context);//通过viewResolver解析逻辑视图到具体视图实现
initFlashMapManager(context);//flash映射管理器
}Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
- 性能明显更好。
- Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
- Servlet 是独立于平台的,因为它们是用 Java 编写的。
- 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
- Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
加载—>实例化—>服务—>销毁。
init():
在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。因为Servlet是单例模式,所以要注意线程安全问题。
service():
它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
destroy():
仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
package com.example.myspring;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 98050
* @Time: 2019-02-22 23:18
* @Feature:
*/
public class Test3 extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.service(req, resp);
}
@Override
public void destroy() {
super.destroy();
}
}在servlet中默认情况下,无论你是get还是post 提交过来都会经过service()方法来处理,然后转向到doGet或是doPost方法。
1.web.xml加载
为了读取web.xml中的配置,编写ServletConfig这个类,它代表当前Servlet在web.xml中的配置信息。通过web.xml中加载自己写的MyDispatcherServlet和读取配置文件。
2、初始化阶段
DispatcherServlet的initStrategies方法会初始化9大组件,但是这里将实现一些SpringMVC的最基本的组件而不是全部,按顺序包括:
- 加载配置文件
- 扫描用户配置包下面所有的类
- 拿到扫描到的类,通过反射机制,实例化。并且放到ioc容器中(Map的键值对 beanName-bean) beanName默认是首字母小写
- 初始化HandlerMapping,这里其实就是把url和method对应起来放在一个k-v的Map中,在运行阶段取出
3、运行阶段
每一次请求将会调用doGet或doPost方法,所以统一运行阶段都放在doDispatch方法里处理,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。按顺序包括以下功能:
- 异常的拦截
- 获取请求传入的参数并处理参数
- 通过初始化好的handlerMapping中拿出url对应的方法名,反射调用
只需一个servlet
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>compile</scope>
</dependency>package com.example.myspringmvc.annotation;
import java.lang.annotation.*;
/**
* @Author: 98050
* @Time: 2019-02-23 14:41
* @Feature:
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyController {
String value() default "";
}package com.example.myspringmvc.annotation;
import java.lang.annotation.*;
/**
* @Author: 98050
* @Time: 2019-02-23 14:41
* @Feature:
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface MyRequestMapping {
String value() default "";
}自定义DispatcherServlet,继承HttpServlet,重写init、doGet、doPost三个方法。
框架:
package com.example.myspringmvc.dispatchservlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @Author: 98050
* @Feature:
*/
public class MyDispatcherServlet extends HttpServlet {
@Override
public void init() throws ServletException {
super.init();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.example.myspringmvc.dispatchservlet.MyDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 拦截所有/* 的请求,交给DispatcherServlet处理,性能最好 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>将原来的org.springframework.web.servlet.DispatcherServlet替换为自己的DispatcherServlet:
com.example.myspringmvc.dispatchservlet.MyDispatcherServlet
init()方法
配置完成后,前端所有请求就会被MyDispatcherServlet所拦截,所以在重写init方法的时候我们要明确几个任务:
- 获取扫包范围
- 获取包下所有类
- 对类进行筛选,只要类上有
@MyController注解的类都放入Spring MVC容器当中 - 遍历Spring MVC容器中的类,对每一个类下所属的方法进行筛选,只要方法上有
@MyRequestMapping注解,那就进行url和方法的映射
注意:扫包范围应该通过解析SpringMVC配置文件获取,这里为了方便直接写死;使用工具类获取包下所有类。
定义三个map,用来充当SpringMVC容器、url与类映射、url与方法映射。
/**
* 用来存放spring mvc的bean
*/
private ConcurrentHashMap<String,Object> springMVCBeans = new ConcurrentHashMap<>();
/**
* url与类映射
*/
private ConcurrentHashMap<String,Object> urlBeans = new ConcurrentHashMap<>();
/**
* url与方法映射
*/
private ConcurrentHashMap<String,String> urlMethod = new ConcurrentHashMap<>();**因为通过Java反射机制执行目标方法时,要获取到目标类的对象,目标方法名,**为了方便查询,定义上述三个map。
重写init方法:
@Override
public void init() throws ServletException {
System.out.println("初始化");
try {
//1.获取扫包范围
String packages = "com.example.myspringmvc.controller";
//2.获取包下的所有的类
List<Class<?>> classList = ClassUtil.getClasses(packages);
//3.对类进行筛选,将包含MyController注解的类进行初始化,并且放入spring mvc bean容器中
findAndInitMyControllerClass(classList);
//4.url与方法映射
handlerMapping(springMVCBeans);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}private void findAndInitMyControllerClass(List<Class<?>> classList) throws IllegalAccessException, InstantiationException {
for (Class c : classList){
MyController annotation = (MyController) c.getAnnotation(MyController.class);
if (annotation != null){
Object o = c.newInstance();
String name = toLowerCaseFirstOne(c.getSimpleName());
springMVCBeans.put(name, o);
}
}
} private String toLowerCaseFirstOne(String simpleName) {
String first = (simpleName.charAt(0)+"").toLowerCase();
String rest = simpleName.substring(1);
return new StringBuffer(first).append(rest).toString();
}private void handlerMapping(ConcurrentHashMap<String, Object> springMVCBeans) {
for (String key : springMVCBeans.keySet()){
Object o = springMVCBeans.get(key);
MyController myController = o.getClass().getAnnotation(MyController.class);
Method[] methods = o.getClass().getMethods();
String url = "";
for (Method method : methods){
MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
if (myRequestMapping != null){
//1.拼接url
url = myController.value() + myRequestMapping.value();
//2.url与方法映射
urlMethod.put(url, method.getName());
}
}
//3.url与类映射
urlBeans.put(url, o);
}
}以上都是在init方法中要完成的任务,具体的请求响应放在doPost和doGet中执行。
doGet和doPost
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req,resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}具体执行过程封装在doDispatch中,具体任务如下:
- 获取请求的url
- 根据url获取对应的控制器类
- 根据url获取对应的处理方法
- 使用Java反射机制执行目标方法
- 获取目标方法的返回值,然后进行视图解析
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, ServletException {
//1.获取请求url
String url = req.getRequestURI();
//2.获取对应的控制器
Object o = urlBeans.get(url);
if (o == null){
resp.getWriter().println("404 not found");
}
//3.获取url对应的方法
String methodName = urlMethod.get(url);
//4.使用反射机制执行方法
String result = (String) methodInvoke(o.getClass(),o,methodName);
//5.视图展示
viewResolver(result,req,resp);
}private Object methodInvoke(Class<?> aClass, Object o, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = aClass.getMethod(methodName);
Class<?>[] parameterTypes = method.getParameterTypes();
Object result = method.invoke(o,parameterTypes);
return result;
}private void viewResolver(String pageName, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取后缀信息
String suffix = ".jsp";
//2.页面目录地址
String prefix = "/";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, resp);
}index.jsp
TestController.java
package com.example.myspringmvc.controller;
import com.example.myspringmvc.annotation.MyController;
import com.example.myspringmvc.annotation.MyRequestMapping;
/**
* @Author: 98050
* @Time: 2019-02-23 15:00
* @Feature:
*/
@MyController("/test")
public class TestController {
@MyRequestMapping("/test")
public String test(){
return "index";
}
}结果:
以上只是完成了一个简易版的Spring MVC,目的是学习Spring MVC的执行流程,许多细节性的问题就不过多纠结了(url地址参数读取、xml配置文件解析等)
Spring MVC是建立在IoC容器基础上的。
<!-- 配置Spring的IOC容器-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置Spring MVC的核心控制器-->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- spring mvc的配置文件 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>重点关注两个类:ContextLoaderListener和DispatcherServlet
ContextLoaderListener被定义为一个监听器,这个监听器是与Web服务器的生命周期相关联的,由ContextLoaderListener监听器负责完成IoC容器在Web环境中的启动工作
在建立起一个IoC体系后,把DispatcherServlet作为Spring MVC处理Web请求的转发器建立起来,从而完成响应HTTP请求的准备。
ContextLoaderListener启动的上下文为根上下文。在根上下文的基础上,还有一个与Web MVC相关的上下文用来保存控制器(DispatcherServlet)需要的MVC对象,作为根上下文的子上下文,构成一个层次化的上下文体系。
ContextLoaderListener实现了ServletContextListener接口,这个接口提供了与Servlet生命周期结合的回调,比如contextInitialized方法和contextDestoryed方法。
在Web容器中,建立WebApplicationContext的过程,是在contextInitialized的接口实现中完成的。
具体的载入IoC容器的过程是由ContextLoaderListener交给ContextLoader来完成的。
ContextLoader中完成了两个IoC容器建立的基本过程:
- 在Web容器中建立起双亲IoC容器
- 生成相应的WebApplicationContext并将其初始化
Spring为Web应用提供了上下文的扩展接口WebApplicationContext来满足启动过程的需要。
在启动过程中,Spring会使用一个默认的WebApplicationContext实现作为IoC容器
package org.springframework.web.context;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;
public interface WebApplicationContext extends ApplicationContext {
//这里定义的常量用于在ServletContext中存取根上下文
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
//获取Web容器的ServletContext
@Nullable
ServletContext getServletContext();
}这个默认的实现就是XmlWebApplicationContext。
package org.springframework.web.context.support;
import java.io.IOException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.ResourceEntityResolver;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext {
/** Default config location for the root context. */
public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
/** Default prefix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";
/** Default suffix for building a config location for a namespace. */
public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";
/**
* Loads the bean definitions via an XmlBeanDefinitionReader.
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
* @see #initBeanDefinitionReader
* @see #loadBeanDefinitions
*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
/**
* Initialize the bean definition reader used for loading the bean
* definitions of this context. Default implementation is empty.
* <p>Can be overridden in subclasses, e.g. for turning off XML validation
* or using a different XmlBeanDefinitionParser implementation.
* @param beanDefinitionReader the bean definition reader used by this context
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setValidationMode
* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader#setDocumentReaderClass
*/
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
reader.loadBeanDefinitions(configLocation);
}
}
}
/**
* The default location for the root context is "/WEB-INF/applicationContext.xml",
* and "/WEB-INF/test-servlet.xml" for a context with the namespace "test-servlet"
* (like for a DispatcherServlet instance with the servlet-name "test").
*/
@Override
protected String[] getDefaultConfigLocations() {
if (getNamespace() != null) {
return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};
}
else {
return new String[] {DEFAULT_CONFIG_LOCATION};
}
}
}获取Bean定义的信息是从默认的配置文件applicationContext.xml中获取的
ContextLoaderListener通过使用ContextLoader来完成实际的WebApplicationContext,也就是IoC容器的初始化工作。
ContextLoaderListener实现的是ServletContextListener接口。ServletContextListener监听的是ServletContext,那么ServletContext发生变化就会触发相应的事件。
服务启动时contextInitialized方法被调用,服务关闭时contextDestroyed方法被调用。
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}具体初始化工作在父类 ContextLoader中完成
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!");
}
servletContext.log("Initializing Spring root WebApplicationContext");
Log logger = LogFactory.getLog(ContextLoader.class);
if (logger.isInfoEnabled()) {
logger.info("Root WebApplicationContext: initialization started");
}
long startTime = System.currentTimeMillis();
try {
// Store context in local instance variable, to guarantee that
// it is available on ServletContext shutdown.
if (this.context == null) {
//创建根上下文
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中
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
if (ccl == ContextLoader.class.getClassLoader()) {
currentContext = this.context;
}
else if (ccl != null) {
currentContextPerThread.put(ccl, this.context);
}
if (logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");
}
return this.context;
}
catch (RuntimeException | Error ex) {
logger.error("Context initialization failed", ex);
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
throw ex;
}
}createWebApplicationContext:
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
// 判断使用什么样的类在Web容器中作为IoC容器
Class<?> contextClass = determineContextClass(sc);
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Custom context class [" + contextClass.getName() +
"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
}
return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}判断使用什么样的类作为Web容器中的IoC容器
determineContextClass
protected Class<?> determineContextClass(ServletContext servletContext) {
String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM);
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 {
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);
}
}
}-
读取ServletContext中对CONTEXT_CLASS_PARAM参数的配置
-
如果在ServletContext中配置了需要使用的CONTEXT_CLASS,那么就使用这个class,前提是这个class可用
-
如果没有配置的话就使用默认的ContextClass
defaultStrategies
private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
private static final Properties defaultStrategies;
static {
// Load default strategy implementations from properties file.
// This is currently strictly internal and not meant to be customized
// by application developers.
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage());
}
}加载默认的ContextLoader实现:
所以默认的IoC容器是XmlWebApplicationContext。
直接实例化需要产生的IoC容器
这就是IoC容器在Web容器中的启动过程,在初始化这个上下文后,该上下文会被存储到ServletContext中,这样就建立了一个全局的关于整个应用的上下文。在Spring MVC启动的时候,这个上下文会被设置为DispatcherServlet自带的上下文的双亲上下文。
DispatcherServlet会建立自己的上下文来持有Spring MVC的Bean对象,在建立这个自己持有的IoC容器时,会从ServletContext中得到根上下文作为DispatcherServlet持有上下文的双亲上下文。
DispatcherServlet通过继承FrameworkServlet和HttpServletBean而继承了HttpServlet ,通过使用Servlet API来对HTTP请求进行响应。
接下来重点关注initStrategies和doDispatch。
DispatcherServlet的启动与Servlet的启动过程是相联系的。在Servlet的初始化过程中,Servlet的init方法会被调用,以进行初始化,具体过程在DispatcherServlet的基类HttpServletBean中实现:
@Override
public final void init() throws ServletException {
// 获取Servlet的初始化参数,对Bean属性进行配置
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;
}
}
// 调用子类的initServletBean进行具体的初始化
initServletBean();
}initServletBean的初始化过程在FrameworkServlet中完成
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'");
if (logger.isInfoEnabled()) {
logger.info("Initializing Servlet '" + getServletName() + "'");
}
long startTime = System.currentTimeMillis();
try {
//初始化上下文
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
catch (ServletException | RuntimeException ex) {
logger.error("Context initialization failed", ex);
throw ex;
}
if (logger.isDebugEnabled()) {
String value = this.enableLoggingRequestDetails ?
"shown which may lead to unsafe logging of potentially sensitive data" :
"masked to prevent unsafe logging of potentially sensitive data";
logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails +
"': request parameters and headers will be " + value);
}
if (logger.isInfoEnabled()) {
logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms");
}
}初始化上下文
protected WebApplicationContext initWebApplicationContext() {
//获取根上下文
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
// 使用这个根上下文作为当前MVC上下文的双亲上下文
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// 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) {
// 创建DispatcherServlet的上下文
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.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
// 把当前建立的上下文存储到ServletContext中,注意使用的属性名是和当前Servlet名称相关的
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}1、首先获取根上下文
@Nullable
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}2、创建DispatcherServlet的上下文
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = getContextClass();
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;
}2.1 getContextClass()
/**
* Default context class for FrameworkServlet.
* @see org.springframework.web.context.support.XmlWebApplicationContext
*/
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
/** WebApplicationContext implementation class to create. */
private Class<?> contextClass = DEFAULT_CONTEXT_CLASS;
/**
* Return the custom context class.
*/
public Class<?> getContextClass() {
return this.contextClass;
}所以DispatcherServlet 中使用的IoC容器是XmlWebApplicationContext
2.2 配置双亲上下文
wac.setParent(parent);2.3 设置ServletContext的引用和其他相关的配置信息
configureAndRefreshWebApplicationContext(wac);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();
}这样DispatcherServlet中的IoC容器已经建立起来了,这个IoC容器是根上下文的子容器。这样设置,使得对具体的一个Bean定义查找过程来说,如果要查找一个由DispatcherServlet所在的IoC容器来管理的Bean,系统会首先到根上下文去查找,找不到才会去DispatcherServlet所管理的IoC容器去查找,这个机制是在getBean中实现的。
2.4 把当前建立的上下文存储到ServletContext中,注意使用的属性名是和当前Servlet名称相关的
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}其实就是放在一个HashMap当中。
在FrameworkServlet中对IoC容器初始化完成后,会在onRefresh方法中调用DispatcherServlet的initStrategies
方法,在这个方法中启动整个Spring MVC框架的初始化。
@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);//文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析
initLocaleResolver(context);//本地化解析
initThemeResolver(context);//主题解析
initHandlerMappings(context);//通过HandlerMapping,将请求映射到处理器
initHandlerAdapters(context);//通过HandlerAdapter支持多种类型的处理器
initHandlerExceptionResolvers(context);//如果执行过程中遇到异常,将交给HandlerExceptionResolver来解析
initRequestToViewNameTranslator(context);//直接解析请求到视图名
initViewResolvers(context);//通过viewResolver解析逻辑视图到具体视图实现
initFlashMapManager(context);//flash映射管理器
}具体以HandlerMapping的初始化过程来说明:
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
/**
* 这里导入所有的HandlerMapping Bean,这些Bean可以在当前的DispatcherServlet的IoC容器中,也可能在其双亲容器中
* detectAllHandlerMappings的默认值为true,即从所有IoC容器获取
*/
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
/**
* 可以根据名称从IoC容器中通过getBean方法获取HandlerMapping
*/
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
/**
* 如果没有找到HandlerMapping,那么就需要为Servlet设置默认的HandlerMapping,这些默认值在DispatcherServlet.properties中
*/
// 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.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}经过上述的过程,handlerMappings变量就已经获取了在BeanDefinition中配置好的映射关系。
每一个HandlerMapping可以持有一系列从URL请求到Controller的映射,Spring MVC提供了一系列的HandlerMapping实现:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable;
public interface HandlerMapping {
String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";
String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
//调用getHandler实际返回的是一个HandlerExecutionChain,这个是典型的Command的模式的使用
// 这个HandlerExecutionChain不但持有handler本身,还包括了处理这个HTTP请求相关的拦截器
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}以SimpleUrlHandlerMapping的实现来进行具体分析。
SimpleUrlHandlerMapping中定义了一个map来持有一系列的映射关系,通过这些映射关系,使Spring MVC应用可以根据HTTP请求确定一个对应的Controller。这些映射关系是通过HandlerMapping来进行封装的,通过getHandler方法可以获得与HTTP对应的HandlerExecutionChain,在HandlerExecutionChain中封装了具体的Controller对象。
HandlerExecutionChain
/*
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
/**
* Handler execution chain, consisting of handler object and any handler interceptors.
* Returned by HandlerMapping's {@link HandlerMapping#getHandler} method.
*
* @author Juergen Hoeller
* @since 20.06.2003
* @see HandlerInterceptor
*/
public class HandlerExecutionChain {
private static final Log logger = LogFactory.getLog(HandlerExecutionChain.class);
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
private int interceptorIndex = -1;
/**
* Create a new HandlerExecutionChain.
* @param handler the handler object to execute
*/
public HandlerExecutionChain(Object handler) {
this(handler, (HandlerInterceptor[]) null);
}
/**
* Create a new HandlerExecutionChain.
* @param handler the handler object to execute
* @param interceptors the array of interceptors to apply
* (in the given order) before the handler itself executes
*/
public HandlerExecutionChain(Object handler, @Nullable HandlerInterceptor... interceptors) {
if (handler instanceof HandlerExecutionChain) {
HandlerExecutionChain originalChain = (HandlerExecutionChain) handler;
this.handler = originalChain.getHandler();
this.interceptorList = new ArrayList<>();
CollectionUtils.mergeArrayIntoCollection(originalChain.getInterceptors(), this.interceptorList);
CollectionUtils.mergeArrayIntoCollection(interceptors, this.interceptorList);
}
else {
this.handler = handler;
this.interceptors = interceptors;
}
}
/**
* Return the handler object to execute.
*/
public Object getHandler() {
return this.handler;
}
public void addInterceptor(HandlerInterceptor interceptor) {
initInterceptorList().add(interceptor);
}
public void addInterceptor(int index, HandlerInterceptor interceptor) {
initInterceptorList().add(index, interceptor);
}
public void addInterceptors(HandlerInterceptor... interceptors) {
if (!ObjectUtils.isEmpty(interceptors)) {
CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());
}
}
private List<HandlerInterceptor> initInterceptorList() {
if (this.interceptorList == null) {
this.interceptorList = new ArrayList<>();
if (this.interceptors != null) {
// An interceptor array specified through the constructor
CollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);
}
}
this.interceptors = null;
return this.interceptorList;
}
/**
* Return the array of interceptors to apply (in the given order).
* @return the array of HandlerInterceptors instances (may be {@code null})
*/
@Nullable
public HandlerInterceptor[] getInterceptors() {
if (this.interceptors == null && this.interceptorList != null) {
this.interceptors = this.interceptorList.toArray(new HandlerInterceptor[0]);
}
return this.interceptors;
}
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
/**
* Apply postHandle methods of registered interceptors.
*/
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
interceptor.postHandle(request, response, this.handler, mv);
}
}
}
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
/**
* Apply afterConcurrentHandlerStarted callback on mapped AsyncHandlerInterceptors.
*/
void applyAfterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response) {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = interceptors.length - 1; i >= 0; i--) {
if (interceptors[i] instanceof AsyncHandlerInterceptor) {
try {
AsyncHandlerInterceptor asyncInterceptor = (AsyncHandlerInterceptor) interceptors[i];
asyncInterceptor.afterConcurrentHandlingStarted(request, response, this.handler);
}
catch (Throwable ex) {
logger.error("Interceptor [" + interceptors[i] + "] failed in afterConcurrentHandlingStarted", ex);
}
}
}
}
}
/**
* Delegates to the handler and interceptors' {@code toString()}.
*/
@Override
public String toString() {
Object handler = getHandler();
StringBuilder sb = new StringBuilder();
sb.append("HandlerExecutionChain with [").append(handler).append("] and ");
if (this.interceptorList != null) {
sb.append(this.interceptorList.size());
}
else if (this.interceptors != null) {
sb.append(this.interceptors.length);
}
else {
sb.append(0);
}
return sb.append(" interceptors").toString();
}
}HandlerExecutionChain中定义的Handler和Interceptor需要在定义HandlerMapping时配置好。具体什么时候配置好的,这个需要一个注册过程,这个注册过程在容器对Bean进行依赖注入时发生,它实际上是通过一个Bean的postProcessor来完成的。
SimpleUrlHandlerMapping的注册过程
因为用到了容器的回调,只有SimpleUrlHandlerMapping是ApplicationContextAware的子类才能启动这个注册过程,这个注册过程完成URL和Controller之间的映射关系。
@Override
public void initApplicationContext() throws BeansException {
super.initApplicationContext();
registerHandlers(this.urlMap);
}protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
});
if (logger.isDebugEnabled()) {
List<String> patterns = new ArrayList<>();
if (getRootHandler() != null) {
patterns.add("/");
}
if (getDefaultHandler() != null) {
patterns.add("/**");
}
patterns.addAll(getHandlerMap().keySet());
logger.debug("Patterns " + patterns + " in " + formatMappingName());
}
}
}这个注册过程的完成需要它的基类来配合,这个基类就是AbstractUrlHandlerMapping
protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException {
Assert.notNull(urlPath, "URL path must not be null");
Assert.notNull(handler, "Handler object must not be null");
Object resolvedHandler = handler;
// Eagerly resolve handler if referencing singleton via name.
if (!this.lazyInitHandlers && handler instanceof String) {
String handlerName = (String) handler;
ApplicationContext applicationContext = obtainApplicationContext();
if (applicationContext.isSingleton(handlerName)) {
resolvedHandler = applicationContext.getBean(handlerName);
}
}
Object mappedHandler = this.handlerMap.get(urlPath);
if (mappedHandler != null) {
if (mappedHandler != resolvedHandler) {
throw new IllegalStateException(
"Cannot map " + getHandlerDescription(handler) + " to URL path [" + urlPath +
"]: There is already " + getHandlerDescription(mappedHandler) + " mapped.");
}
}
else {
if (urlPath.equals("/")) {
if (logger.isTraceEnabled()) {
logger.trace("Root mapping to " + getHandlerDescription(handler));
}
setRootHandler(resolvedHandler);
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
if (logger.isTraceEnabled()) {
logger.trace("Mapped [" + urlPath + "] onto " + getHandlerDescription(handler));
}
}
}
}完成这个解析处理过程以后,会把URL和handler作为键值对放到一个handlerMap中去。
private final Map<String, Object> handlerMap = new LinkedHashMap<>();AbstractHandlerMapping中getHandler的实现:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 把handler封装到HandlerExecutionChain中并加上拦截器
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler)) {
CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}获得handler的具体过程在getHandlerInternal方法中实现,它的实现在AbstractHandlerMapping的子类AbstractUrlHandlerMapping中实现
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
//获取url路径
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
request.setAttribute(LOOKUP_PATH, lookupPath);
//url和handler进行匹配
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// We need to care for the default handler directly, since we need to
// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
// Bean name or resolved handler?
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(rawHandler, request);
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
return handler;
}@Nullable
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// Direct match?
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
String bestMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger.trace("Matching patterns " + matchingPatterns);
}
bestMatch = matchingPatterns.get(0);
}
if (bestMatch != null) {
handler = this.handlerMap.get(bestMatch);
if (handler == null) {
if (bestMatch.endsWith("/")) {
handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
}
if (handler == null) {
throw new IllegalStateException(
"Could not find handler for best pattern match [" + bestMatch + "]");
}
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
// for all of them
Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isTraceEnabled() && uriTemplateVariables.size() > 0) {
logger.trace("URI variables " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}DispatcherServlet的doService方法:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(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<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 这个doDispatch是分发请求的入口
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}这个doDispatch中准备ModelAndView,调用getHandler来响应HTTP请求,然后通过执行Handler的处理来得到返回的ModelAndView结果,最后把这个ModelAndView对象交给相应的视图对象去呈现。
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,用来持有handler处理请求的结果
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根据请求得到对应的handler,handler的注册以及getHandler的实现.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 这里是实际调用handler的地方,在handler之前.用HandlerAdapter先检查一下handler的合法性
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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 通过调用HandlerAdapter的handle方法,实际上触发对Contrller的handlerRequest方法的调用.
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);
}
//这里使用视图对ModelAndView数据的展现
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}获得handler的过程getHandler
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}getHandlerAdapter检查handler的正确性
通过判断,可以知道这个handler是不是Controller接口的实现
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}主要通过support方法来实现:
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
/**
* Adapter to use the plain {@link Controller} workflow interface with
* the generic {@link org.springframework.web.servlet.DispatcherServlet}.
* Supports handlers that implement the {@link LastModified} interface.
*
* <p>This is an SPI class, not used directly by application code.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.web.servlet.DispatcherServlet
* @see Controller
* @see LastModified
* @see HttpRequestHandlerAdapter
*/
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return ((Controller) handler).handleRequest(request, response);
}
@Override
public long getLastModified(HttpServletRequest request, Object handler) {
if (handler instanceof LastModified) {
return ((LastModified) handler).getLastModified(request);
}
return -1L;
}
}M:ModelAndView的生成
C:DispatcherServlet、handler实现
V:视图呈现的处理是在render方法调用中完成
DispatcherServlet中的doDispatch方法中的processDispatchResult方法用来完成视图的呈现
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable 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);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
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 {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// 对视图名进行解析.
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// 从ModelAndView中获取到实际的视图对象.
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.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
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 + "]", ex);
}
throw ex;
}
}View对象
JstlView对象










