SpringMVC源码深入解析

通过前面 SpringAOP源码深度解析 , SpringIOC源码深度解析 加上本文的SpringMVC的源码阅读,我从中收获很多,学习了各种设计模式,各种抽象思想,以及各种底层原理,比如动态代理,反射等等,虽然前前前后后大概花了一个多月,但是我不后悔,并不觉得是浪费时间

本文比较长,我花了三天的时间完成本文,但是还是水平有限,不可能面面俱到,当中也可能会有错的,还请读者指出,一起交流一起进步。

本文采用的源码版本是5.2.x,同样,为了能收获更多,还请读者打开Spring的源码工程进行跟进。

基础知识

Servlet的基础知识

为什么要先了解Servlet的知识呢,因为后面你会看到我们所熟悉的SpringMVC其实也是一个Servlet,只是它封装了很多的东西并和Spring进行了整合,后面我们进行的源码分析就是围绕着Servlet的生命周期进行的,所以有必要了解一下Servlet相关的知识。

Servlet概念

全称 Java Servlet ,是用Java编写的服务器端程序。其主要功能在于 交互式地浏览和修改数据,生成动态Web内容。Servlet运行于支持 Java应用的服务器中。从原理上讲,Servlet可以响应任何类型的请求, 但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

Servlet的工作原理

下面通过一张时序图来理解Servlet的工作流程

SpringMVC源码深入解析

从上面的时序图总结如下:

  1. 首先客户端请求接入
  2. Servlet容器,比如Tomcat处理请求
  3. Tomcat 创建HttpServletRequest对象,将请求的信息封装到这个对象中。
  4. Tomcat 创建HttpServletResponse对象, 此时是一个空的对象等待请求返回并填充
  5. Tomcat 调用service方法,最终传递到子类HttpServlet中
  6. HttpServlet从request中获取请求信息,处理完毕后将返回值信息封装到HttpServletResponse对象
  7. Tomcat容器返回处理后的信息

Servlet生命周期

打开Servlet源码发现Servlet接口有几个方法:

  1. init()方法Tomcat创建Servlet时会调用
  2. 每次有请求来时Tomcat都会调用service()方法
  3. 请求结束时Tomcat会调用destroy()方法
public interface Servlet {
   //Servlet创建时会调用init方法进行初始化
    void init(ServletConfig var1) throws ServletException;

    ServletConfig getServletConfig();
	
   // 每次有新的请求来时都会调用
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

    String getServletInfo();
	
    // 请求结束时调用
    void destroy();
}
复制代码

一个Servlet例子

写一个AddUserServlet类继承自HttpServlet(为什么要继承这个类后面有说明)

public class AddUserServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		response.setContentType("text/plain;charset=utf8");
		response.getWriter().write("添加成功");
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		doGet(request, response);
	}

}
复制代码

webapp/WEB-INF下新建web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
        <servlet>
    <description></description>
    <display-name>AddUserServlet</display-name>
    <servlet-name>AddUserServlet</servlet-name>
    <servlet-class>com.sjc.springmvc.servlet.AddUserServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AddUserServlet</servlet-name>
    <url-pattern>/AddUserServlet</url-pattern>
  </servlet-mapping>
</web-app>
复制代码

将程序部署到Tomcat就可以访问了(具体不会的请读者查相关资料)

有同学可能会产生疑惑:我都没有看到main方法,怎么就可以访问了呢?

回答如下:

Servlet实际上是tomcat容器生成的,调用init方法可以初始化。他有别于 普通java的执行过程,普通java需要main方法;但是web项目由于是服务器控 制的创建和销毁,所以servlet的访问也需要tomcat来控制。通过tomcat访问 servlet的机制是通过使用http协议的URL来访问,所以servlet的配置完想要访 问,必须通过URL来访问,所以没有main方法。

应用系统三层架构

表现层

也就是我们常说的web层

  1. 它负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应
  2. 表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示
  3. 表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端
  4. 表现层的设计一般都使用MVC模型

业务层

  1. 也就是我们常说的service层。
  2. 它负责业务逻辑处理,和我们开发项目的需求息息相关。web层依赖业务层,但是业务层不依赖web层。
  3. 业务层在业务处理时可能会依赖持久层,如果要对数据持久化需要保证事务一致性

持久层

  1. 也就是我们常说的dao层。
  2. 负责数据持久化,包括数据层即数据库数据库访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中
  3. 通俗的讲,持久层就是和数据库交互,对数据库表进行增删改的。

MVC设计模式

MVC是模型(model)、视图(view)、控制器(controller)的缩写,是一种用于设计编写web应用程序表现层的模式

MVC设计模式的三大角色:

  • Model(模型):

    模型包含业务模型和数据模型,数据模型用于封装数据,业务模型用于处理业务。

  • View(视图): 通常指的是我们的jsp或者html。作用一般就是展示数据的。

    通常视图是依据数据模型创建的。

  • Controller(控制器):

    是应用程序中处理用户交互的部分。作用一般就是处理程序逻辑的。

SpringMVC的流程分析

理解SpringMVC,只要你理解了下面介绍的六大组件基本上就可以了。后面的源码分析我们也是围绕着这六大组件来的。

SpringMVC流程如下图所示:

SpringMVC源码深入解析

总结如下:

  • DispatcherServlet: 前端控制器

    用户请求到达前端控制器,它就相当于MVC中的C,DispatcherServlet是整个流程控制的中心,由它调用其他组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性

  • Handler:处理器

    Handler是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理

    由于Handler涉及到具体的用户业务请求,所以一般情况下需要程序员根据业务需求开发Handler

    常用的有比如Controller,HttpRequestHandler,Servlet、@RequestMapping等等,所以说处理器其实是一个宽泛的概念

  • View:视图

    SpringMVC框架提供了很多的view视图类型的支持,包括:jstlview、freemarkerview、pdfview等。我们最常见的视图就是jsp。 当然,现在很少用jsp了,现在大部分都是前后端分离了,所以后面源码分析我们会忽略视图

  • HandlerMapping: 处理器映射器

    HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式比如:配置文件方式(BeanNameUrlHandlerMapping)、实现接口方式和注解方式(RequestMappingHandlerMapping)等。

  • HandlerAdapter: 处理适配器

    SpringMVC通过适配器模式,将不关联的 DispatcherServlet 和Handler进行关联,通过适配器调用具体的Handler实现。

  • View Resolver:视图解析器

    View Resolver负责将处理结果生成view视图,View Resolver首先根据逻辑视图解析成物理视图名即具体的页面地址,再生成view视图对象,最后对view进行渲染,将处理结果通过页面展示给用户。

Spring源码解析

初始化init()

我们上面说过,分析源码的时候从Servlet入手,我们看它的初始化init()。首先看下类结构图,我们发现DispatcherServlet这个核心组件就是一个Servlet,回到开头我们说的SpringMVC其实也是一个Servlet,只是做的事情比较多而已。

SpringMVC源码深入解析

我们顺着这个类关系图,找到了FrameworkServlet#initServletBean

这里初始化了spring容器WebApplicationContext

@Override
	protected final void initServletBean() throws ServletException {
	   //...省略若干代码
    // 初始化web环境中的spring容器WebApplicationContext
    this.webApplicationContext = initWebApplicationContext();
    initFrameworkServlet();
		//...省略若干代码
	}
复制代码

我们进入到:FrameworkServlet#initWebApplicationContext

我们找到了两个分支

  • configureAndRefreshWebApplicationContext

    这个分支会去初始化Spring容器,又会回到我们SpringIOC容器初始化的那十二步骤,相关的可以阅读我之前分析的 深度解析SpringIOC

  • onRefresh

    会刷新容器的策略,我们主要看这一分支

protected WebApplicationContext initWebApplicationContext() {
	//...省略若干代码
  // 初始化spring容器
	configureAndRefreshWebApplicationContext(cwac);
  // 刷新容器中的策略
	onRefresh(wac);
  //...省略若干代码
}
复制代码

我们根据onRefresh,发现最终会进入到DispatcherServlet#onRefresh

protected void onRefresh(ApplicationContext context) {
		// 初始化策略容器
		initStrategies(context);
	}
复制代码

我们进入到DispatcherServlet#initStrategies

这里会初始各种解析器,比如我们比较关心的处理器映射器,处理器适配器,至于为啥要在这里做初始化呢? SpringMVC为了扩展性,使用策略模式,将这些映射器适配器交给了配置文件,这样如果要再新增一个处理器就不需要改代码了,符合“对修改关闭,对扩展开放”的设计原则,这里的初始化也是为了策略模式做准备 这个也是我们学习源码学习到的知识,以后可以运用到我们实际的项目中。

比如下面的xml配置文件:

initStrategies就是从SpringIOC容器中获取到这些Bean,然后放入Map中来进行初始化的。

<beans>
	<!-- Handler处理器类的配置 -->
	<!-- 通过bean标签,建立beanname和bean的映射关系 -->
	<bean name="/queryUser2"
		class="com.sjc.springmvc.handler.QueryUserHandler"></bean>
	<bean name="/addUser2"
		class="com.sjc.springmvc.handler.AddUserHandler"></bean>
	<!-- HandlerMapping配置 -->
	<bean
		class="com.sjc.springmvc.handlermapping.BeanNameUrlHandlerMapping"
		init-method="init"></bean>
  
	<!-- HandlerAdapter配置 -->
	<bean
		class="com.sjc.springmvc.handleradapter.HttpRequestHandlerAdapter"></bean>
</beans>
复制代码

读者感兴趣的话可以当做为一个分支进行验证。

protected void initStrategies(ApplicationContext context) {
		// 初始化多部件解析器
		initMultipartResolver(context);
		// 初始化国际化解析器
		initLocaleResolver(context);
		// 初始化主题解析器
		initThemeResolver(context);
		// 初始化处理器映射器
		initHandlerMappings(context);
		// 初始化处理器适配器
		initHandlerAdapters(context);
		// 初始化异常解析器
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		// 初始会视图解析器
		initViewResolvers(context);
		initFlashMapManager(context);
	}
复制代码

RequestMappingHandlerMapping初始化

在介绍HandlerMapping找到Handler的过程前,我们先来看看,RequestMappingHandlerMapping的初始化过程发生了什么。我这里先给个结论:

SpringMVC源码深入解析

我们最终的目的就是通过url找到Handler(HandlerMethod)

我们进入到RequestMappingHandlerMapping,看到其中只有这样的方法:

@Override
	public void afterPropertiesSet() {
		this.config = new RequestMappingInfo.BuilderConfiguration();
		this.config.setUrlPathHelper(getUrlPathHelper());
		this.config.setPathMatcher(getPathMatcher());
		this.config.setSuffixPatternMatch(this.useSuffixPatternMatch);
		this.config.setTrailingSlashMatch(this.useTrailingSlashMatch);
		this.config.setRegisteredSuffixPatternMatch(this.useRegisteredSuffixPatternMatch);
		this.config.setContentNegotiationManager(getContentNegotiationManager());
		// 调用父类AbstractHandlerMethodMapping的afterPropertiesSet方法
		super.afterPropertiesSet();
	}
复制代码

我们眼前一亮,它为我们提供了研究RequestMappingHandlerMapping初始化的入口,为什么这么说呢?我们知道SpringIOC提供了两种初始化方式: 第一种、就是在配置文件中中指定init-method方法,这种在我前面分析SpringIOC的文章可以看到**深入解析SpringIOC; **第二种、就是子类实现InitializingBean接口。这个接口有一个方法:

public interface InitializingBean {
	void afterPropertiesSet() throws Exception;
}
复制代码

这样在初始化Bean的时候会调用afterPropertiesSet()方法。

这个流程我们在SpringIOC源码哪里可以看见呢?

我们进入AbstractAutowireCapableBeanFactory#invokeInitMethods

protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		//判断该bean是否实现了实现了InitializingBean接口,如果实现了InitializingBean接口,则只掉调用bean的afterPropertiesSet方法
		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						//直接调用afterPropertiesSet
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				//直接调用afterPropertiesSet
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			//判断是否指定了init-method方法,如果指定了init-method方法,则再调用制定的init-method
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				//进一步查看该方法的源码,可以发现init-method方法中指定的方法是通过反射实现
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}
复制代码

这两种方式哪一种先调用呢?

看源码我们发现实现了InitializingBean接口的类在Bean进行初始化的时候先被调用,然后调用init-method指定的方法;

哪一种方式的效率高呢?

当然是实现了InitializingBean接口的类方式,因为调用init-method指定的方法是通过反射实现的;但是通过映射文件方式消除了对spring的依赖

好了别跑远了,我们接着看RequestMappingHandlerMapping#afterPropertiesSet

里面会调用父类AbstractHandlerMethodMapping#afterPropertiesSet

public void afterPropertiesSet() {
		// 初始化处理器方法对象
		initHandlerMethods();
	}
复制代码

接着进入:AbstractHandlerMethodMapping#initHandlerMethods

protected void initHandlerMethods() {
		// 获取当前spring容器的所有bean的name,并遍历
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				// 处理候选的Bean
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
复制代码

我们关心的是processCandidateBean

进入:AbstractHandlerMethodMapping#processCandidateBean

这里面主要做三件事:

  1. 根据bean的名称,从当前spring容器中获取对应的Bean的Type
  2. 判断是否是Handler,也就是是否有@Controller或者@RequestMapping修饰类,如果有则是Handler对象
  3. 查找并封装HandlerMethod对象
protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			// 根据bean的名称,从当前spring容器中获取对应的Bean的Type
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		// 如果是Handler,则需要查找HandlerMethod(如果带有@Controller或者@RequestMapping则是Handler对象)
		if (beanType != null && isHandler(beanType)) {
			// 重要入口
			// 从Controller或者RequestMapping注解的Bean中,找到所有的HandlerMethod对象,并进行存储
			detectHandlerMethods(beanName);
		}
	}
复制代码

我们进入AbstractHandlerMethodMapping#detectHandlerMethods

这个方法比较复杂,主要用的lambda表达式太多,主要做这几件事:

  1. 将类上的@RequestMapping信息和Method上的Method信息封装成RequestMappingInfo对象
  2. 将Method方法和RequestMappingInfo对象建立映射,存储Map集合中
  3. 遍历methods,注册url和RequestMappingInfo映射关系,注册RequestMappingInfo和HandlerMethod的映射关系
protected void detectHandlerMethods(Object handler) {
		// 获取处理器类型
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			// 如果该类是通过cglib代理的代理类,则获取其父类类型,否则的话,直接返回该类
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			// 存储Method方法和RequestMapping注解信息的映射关系(重点)
			// 该映射关系会解析成我们需要的其他两个映射关系
			// key是Controller类中的Method对象,value是RequestMappingInfo对象
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> { // 此处是设置回调函数
						try {
							// 获取bean上面和method上面的RequestMapping注解信息,封装到RequestMappingInfo对象中
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				// 注册HandlerMethod和RequestMappingInfo对象的关系
				// 注册请求URL和RequestMappingInfo对象的关系
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
复制代码

RequestMappingHandlerMapping初始化分析到此结束,在深入下去就会没完没了。

处理请求service()

同样顺着下面的流程图我们找到实现Servlet#service()方法的类HttpServlet

SpringMVC源码深入解析

我们来到:HttpServlet#service:

可以看到这里面将ServletRequest转成HttpServletRequest, ServletResponse转成HttpServletResponse,这样就可以做更多的事情了。

@Override
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest  request;
        HttpServletResponse response;
        
        if (!(req instanceof HttpServletRequest &&
                res instanceof HttpServletResponse)) {
            throw new ServletException("non-HTTP request or response");
        }

        request = (HttpServletRequest) req;
        response = (HttpServletResponse) res;

        service(request, response);
    }
复制代码

进入到HttpServlet#service(request, response):

所有的Http请求都会经过这里,但是我们找半天没发现service实现代码在哪里,我们看到HttpServlet是一个抽象类,一般类被设计成抽象类有两个因素:

  • 类中有抽象方法,需要子类实现
  • 没有抽象方法,但是类不希望实例

那这个HttpServlet为什么要设计成抽象类呢?别急,我们看下类的注释文档:

翻译起来大概的意思就是我这个类不知道你子类是什么处理请求的,我不会帮你处理的,我这里定义好了各种请求,请你务必实现其中的某一个,不然我就给你返回错误。我们看到这里就是用了 抽象模板方法的设计模式:父类把其他的逻辑处理完,把不确定的业务逻辑抽象成一个抽象方法,交给子类去实现。

/**
 *
 * Provides an abstract class to be subclassed to create
 * an HTTP servlet suitable for a Web site. A subclass of
 * <code>HttpServlet</code> must override at least 
 * one method, usually one of these:
 *
 * <ul>
 * <li> <code>doGet</code>, if the servlet supports HTTP GET requests
 * <li> <code>doPost</code>, for HTTP POST requests
 * <li> <code>doPut</code>, for HTTP PUT requests
 * <li> <code>doDelete</code>, for HTTP DELETE requests
 * <li> <code>init</code> and <code>destroy</code>, 
 * to manage resources that are held for the life of the servlet
 * <li> <code>getServletInfo</code>, which the servlet uses to
 * provide information about itself 
 * </ul>
 *
 /
复制代码
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }
复制代码

我们顺藤摸瓜,找到实现HttpServlet的子类,看看哪个子类实现了service()方法,我们最终看到了DispatcherServlet实现了这个service()方法。

这里我们千呼万唤的doDispatch终于出来了,这个doDispatch做了它擅长的事情,就是 请求的分发 ,我们得慢慢品,细细品这个方法。

@Override
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
			//...省略掉无数代码
      // 处理请求分发(做调度)
			doDispatch(request, response);
	}
复制代码

我们进入到:DispatcherServlet#doDispatch

我们再回顾一下SpringMVC的处理流程:

SpringMVC源码深入解析

这个方法就是干这件事情的,一张图胜似千言万语。 你品,你细品

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 {
				// 处理文件上传的request请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				// 通过处理器映射器HandlerMapping,获取handler处理器执行链,该执行链封装了处理器和对应该处理器的拦截器(可能有多个)
				// 需要注意的是@Controller注解的类,它不是我们这里要查找的处理器,我们要查找的处理器是@RequestMapping对应的方法,这个方法会封装到HandlerMethod类中
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				// 通过找到的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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}

				// 执行拦截器(interceptor)的preHandle方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 通过处理器适配器,真正调用处理器方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				// 设置默认视图名称
				applyDefaultViewName(processedRequest, mv);
				// 执行拦截器(interceptor)的postHandle方法
				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) {
			// 执行拦截器(interceptor)的afterCompletion方法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			// 执行拦截器(interceptor)的afterCompletion方法
			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);
				}
			}
		}
	}
复制代码

SpringMVC策略模式

通过HandlerMapping找到Handler的过程

我们先来看下请求通过HandlerMapping找到Handler的过程:

我们进入到DispatcherServlet#getHandler

不出我们所料,这里就遍历了我们初始化阶段存储的handlerMappings集合,返回HandlerExecutionChain。这里使用到了策略模式:

@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;
	}
复制代码

何为策略模式呢?我们一起来看:

我们假设有这样的需求:我们出行方式有很多种,比如火车,飞机,自行车,我们只需要输入我们有的钱就可以智能地匹配出我们的出行方式

我们来建立这样的模型:

定义一个策略类:

// 策略类
public interface TravelStrategy {
	
	//出行方式
	void travelWay();
	
	boolean isDone(int type);
}
复制代码

定义飞机出行方式类:AirStrategy

public class AirStrategy implements TravelStrategy {

	@Override
	public void travelWay() {
		System.out.println("坐飞机");
	}
	
	@Override
	public boolean isDone(int type) {
		if (type <= 1000 && type >500) {
			return true;
		}
		return false;
	}

}
复制代码

定义自行车出行方式类: BicycleStrategy

public class BicycleStrategy implements TravelStrategy {

	@Override
	public void travelWay() {
		System.out.println("自行车");
	}

	@Override
	public boolean isDone(int type) {
		if (type <= 20) {
			return true;
		}
		return false;
	}

}
复制代码

定义火车出行方式类:TrainStrategy

public class TrainStrategy implements TravelStrategy {

	@Override
	public void travelWay() {
		System.out.println("坐火车");
	}

	@Override
	public boolean isDone(int type) {
		if (type >= 20 && type < 400) {
			return true;
		}
		return false;
	}

}
复制代码

定义一个策略模式环境类(Context)

public class PersonContext {

	// 策略类集合
	private List<TravelStrategy> strategylist;

	public PersonContext() {
		this.strategylist = new ArrayList<>();
		strategylist.add(new AirStrategy());
		strategylist.add(new TrainStrategy());
		strategylist.add(new BicycleStrategy());
	}


	public void travel(int type) {
		// 输入一个数,循环遍历每个策略类,进行最优选择
		for (TravelStrategy travelStrategy : strategylist) {
			if (travelStrategy.isOK(type)) {
				travelStrategy.travelWay();
				break;
			}
		}
	}
}
复制代码

测试类:

public class StrategyTest {
	@Test
	public void test() {
		// 策略环境类
		PersonContext person = new PersonContext();

		// 坐飞机
		person.travel(1500);

		// 坐火车
		person.travel(100);

		// 自行车
		person.travel(1);
	}
}
复制代码

输出:

坐飞机
坐火车
自行车
复制代码

我们再来看SpringMVC中的策略模式,首先环境类DispatcherServlet, 初始化各个策略模式的是DispatcherServlet#initStrategies, 遍历策略选择最合适的策略的是:

@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;
	}
复制代码

策略模式消除了很多的if….else…代码,通过配置文件方式定义各种策略,是一种可扩展的设计模式。

我们进入到AbstractHandlerMapping#getHandler

主要做两件事:

  • 通过请求获取到处理器对象
  • 通过处理器对象创建处理器执行链
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		// 调用具体的子类去获取不同类型的处理器对象(比如获取到的@Controller和@RequestMapping注解的处理器是HandlerMethod对象)
		Object handler = getHandlerInternal(request);
		//...省略若干代码
		// 创建处理器执行链
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		//...省略若干代码

		return executionChain;
	}
复制代码

我们来到实现类AbstractHandlerMethodMapping#getHandlerInternal

我们看到这里的handler是HandlerMethod,这个类封装了controller和Method,

@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		// 获取查找路径(部分URL)
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		request.setAttribute(LOOKUP_PATH, lookupPath);
		this.mappingRegistry.acquireReadLock();
		try {
			// 根据请求查找路径(部分URL),获取最合适的HandlerMethod对象
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			// 对HandlerMethod对象包含的的bean属性进行实例化,再返回HandlerMethod对象
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}
复制代码

我们进入到AbstractHandlerMethodMapping#lookupHandlerMethod

这里主要做:

  1. 根据请求路径(URL)去上面我们分析到的urlLookup集合中获取匹配到的RequestMappingInfo集合

  2. 将RequestMappingInfo对象和HandlerMethod对象进行匹配,将匹配到的信息封装到Match对象,再将Match对象放入matches集合进行返回。

    此时我们的处理器HandlerMethod已经找到。

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		// 根据查找路径(URL)去urlLookup集合中获取匹配到的RequestMappingInfo集合
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			// 将RequestMappingInfo对象和HandlerMethod对象进行匹配,将匹配到的信息封装到Match对象,再将Match对象放入matches集合
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		//...省略寻找最优匹配的过程代码
	}
复制代码

SpringMVC适配器模式

我们再回到 DispatcherServlet#doService 方法中

找到Handler后按照我们上面的流程图,接下来就是要找到HandlerAdapter。

我们看到:

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	
    // 通过找到的handler处理器,去匹配合适的处理器适配器HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

}
复制代码

我们进入到:DispatcherServlet#getHandlerAdapter

这里无非就是根据我们初始化过程中,将配置文件中的HandlerAdapters集合进行遍历,找到合适的HandlerAdapter。

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");
	}
复制代码

这里用到了适配器模式,我们来看看HandlerAdapter类:

public interface HandlerAdapter {

  // 判断是否与当前的适配器支持,如果支持返回true,不支持返回false
	boolean supports(Object handler);

  // 调用handler中的请求处理方法
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	
	long getLastModified(HttpServletRequest request, Object handler);

}
复制代码

SpringMVC这里为啥需要适配器呢?我们来分析一下,由于处理器handler有很多,比如Controller,HttpRequestHandler,Servlet等等,从HandlerMapping中获取到的Handler是一个Object对象,这样在DispatcherServlet#getHandlerAdapter方法中如果不用适配器模式就可能这样写:

private HandlerAdapter getHandlerAdapter(Object handler) {
		if (handler instanceof HttpRequestHandler) {
			return new HttpRequestHandlerAdapter();
		} else if(handler instanceof Controller) {
      return new Controller
    }
		// else if.....
		return null;
	}
复制代码

如果再新增一个handler就得改代码,不符合“ 对修改关闭,对扩展开放 ”的设计原则。

SpringMVC这里就给每个handler对应一个相应的适配器,比如针对Controller实现的handler对应的是SimpleControllerHandlerAdapter适配器,由SimpleControllerHandlerAdapter适配器调用Controller处理器处理相关逻辑,每个handler的处理逻辑不一样,以后再新增一个handler的时候,只用新增相应的适配器就可以了,这完美的解决了这个问题。希望读者能细品其中的设计思想。

我们继续看DispatcherServlet中的其他方法:

找到HandlerAdapter后,主要做以下几件事:

  1. 执行拦截器(interceptor)的preHandle方法
  2. 通过处理器适配器,真正调用处理器方法
  3. 执行拦截器(interceptor)的postHandle方法
  4. 处理调度结果(也就是ModelAndView对象)
  5. 执行拦截器(interceptor)的afterCompletion方法

SpringMVC的 拦截器 功能源码就在此步骤完成,如果读者对此感兴趣可以以此为分支进行深入研究,至此SpringMVC源码分析告一段落。

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	//...省略若干代码
  		// 通过找到的handler处理器,去匹配合适的处理器适配器HandlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
  		// 执行拦截器(interceptor)的preHandle方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				// Actually invoke the handler.
				// 通过处理器适配器,真正调用处理器方法
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				// 设置默认视图名称
				applyDefaultViewName(processedRequest, mv);
				// 执行拦截器(interceptor)的postHandle方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
 	 // 处理调度结果(也就是ModelAndView对象)
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		
  	// 执行拦截器(interceptor)的afterCompletion方法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		

}
复制代码

原文 

https://juejin.im/post/5def65c3518825126131bc68

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » SpringMVC源码深入解析

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址