Spring Web MVC (Servlet,HttpServlet,HttpServletBean,FrameworkServlet,DispatcherServlet)源码解析

Spring Web MVC 源码解析

小弟有一个开源项目,希望大家可以多多一键三连,谢谢大家

nirvana-reborn

后续的源码解析也都会进行同步更新上去

总览

  • 我们对 Spring Web MVC 源码解析,那么我们首先要知道什么是 Spring Web MVC,我们会根据一些很直白的问题去进行描述。
  • 我们会根据 Servlet Tomcat SpringWebMvc SpringBoot 等多个技术栈进行分析解答Spring在进行Web开发时所做了哪些工作。

什么是Spring Web MVC?

首先我们从 Spring官网上面进行找到对象 SpringWebMvc相关的解答

Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, “Spring Web MVC,” comes from the name of its source module (spring-webmvc), but it is more commonly known as “Spring MVC”.
释义:
Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring框架中。它的正式名称“Spring Web MVC”来自于它的源模块(Spring-webmvc)的名称,但它更常见的名称是“Spring MVC”。

从Spring官网我们能够得到答案,我们所说的 SpringWebMVC是什么。通俗表达为 SpringWebMVCSpringServlet的一个封装与构建,是 Spring大家庭中的一员。但是我们在这上面我们又看到了一个新鲜的词汇 Servlet 但是对于Java开发的同学我想这个词语一定不会陌生,但是以现在目前的开发方式基本上就接触不到 Servlet底层的东西,所以让我们接下来再次认识一下 Servlet

什么是Servlet?

我们从 Servlet 3.0的规范中找到了答案

A servlet is a Java™ technology-based Web component, managed by a container, that generates dynamic content. Like other Java technology-based components, servlets are platform-independent Java classes that are compiled to platform-neutral byte code that can be loaded dynamically into and run by a Java technology-enabled Web server.Containers, sometimes called servlet engines, are Web server extensions that provide servlet functionality. Servlets interact with Web clients via a request/response paradigm implemented by the servlet container.

释义:
servlet是一个基于Java™技术的Web组件,由容器管理,生成动态内容。与其他基于Java技术的组件一样,servlet是与平台无关的Java类,被编译成与平台无关的字节码,可以动态地加载到支持Java技术的Web服务器中并由其运行。容器(有时称为servlet引擎)是提供servlet功能的Web服务器扩展。servlet通过servlet容器实现的请求/响应范式与Web客户端交互。

Servlet 是基于 Java技术的 Web组件,通过 Web我们能够知道 Servlet是针对于互联网的 Request /Response的操作来进行执行的,而且其中的关键语句是 可以被编译成与平台无关的字节码,可以动态地加载到支持Java技术的Web服务器中并由其运行 。从这句话中我们可以思考一下,ServletJava中的Web技术栈中不可获取的一部分并且也是最关键的部分。

我们知道了并且了解了 Servlet,那么我们怎样去创建一个Servlet,接下来就让我们根据Servlet3.0规范来探索一下。

我们在项目中引入 Servlet依赖

<dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>javax.servlet-api</artifactId>
   <version>4.0.1</version>
</dependency>

HttpServlet

我们在项目中添加到 Servlet的 maven依赖,找到对应的 javax.servlet.Servlet类我们发现这个类是一个 interface接口,于是我们能够找到它的实现类 javax.servlet.http.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>

javax.servlet.http.HttpServlet类方法注释告诉我们,HttpServlet抽象类提供我们用于继承与重写的类,让我们自己去实现和添加我们自己的一些业务方法,去初始化一些相关的链接。其实就是 Servlet给我们提供的一个抽象模板。在抽象类中的方法,我们能够清晰的看出来与Http的几种请求方式是一样的。我们知道了这个特点,我们就可以去看一下 SpringWebMVC是怎么去使用 ServletAPI

Spring

我们现在切换到 Spring框架里面,目前我们开发主要分为 SpringFrameworke 传统的配置文件与 SpringBoot自动装配方式进行开发,我们在用到 SpringBoot 的时候会发现只需要配置一些 properties或者 yaml文件就可以直接运行 SpringBoot的项目,但是大多数的同学都不知道其中为什么能够直接 一键启动 ?其实 SpringBoot底层就是使用传统的SpringFrameworke框架,有关于 SpringBoot底层的设计与源码,我们以后有时间来进行解答一下。这一次我们先说 SpringWebMVCSpringFramework还有 SpringBoot中的对于 WebMVC 的整合方式来进行源码解析

1、HttpServletBean

我们从上面知道了,在 Servlet中提供了 HttpServlet抽象模板类来进行继承重写他们的方法,于是我们在 SpringWebMvc框架源码中找到了继承 HttpServlet类的子类 HttpServletBean 我们从 HttpServletBean中能够找到这个类也是一个抽象类,我们能够看到这个类的主要结构
在这里插入图片描述

从这个图中我们能够看到 HttpServletBean抽象类继承了 HttpServlet,我们通过方法能够看到对其 init()方法进行了重写,另外实现的 EnvironmentAwareEnvironmentCapable 两个接口主要用于 Spring配置相关操作,我们不进行详细讨论,我们来看一下 HttpServletBean里面的 init()方法是如何操作的。

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;
			}
		}
		// Let subclasses do whatever initialization they like.
  	// 让子类去实现具体的初始化
		initServletBean();
	}

由此我们能够看到 HttpServletBean也只是做了一些简单处理,具体的初始化是由 HttpServletBean抽象类的子类去操作的,我们看代码可以发现 HttpServletBean的子类有两个一个是 org.springframework.web.servlet.FrameworkServlet,与org.springframework.web.servlet.DispatcherServlet ,其实我们仔细查看一下代码我们就可以知道,DispatcherServlet是继承了 FrameworkServlet所以 SpringServlet的继承关系应该是

javax.servlet.Servlet—> javax.servlet.http.HttpServlet—> org.springframework.web.servlet.HttpServletBean—> org.springframework.web.servlet.FrameworkServlet—> org.springframework.web.servlet.DispatcherServlet

接下来让我们来查看 FrameworkServletDispatcherServlet两个类都做了那些工作吧。

2、FrameworkServlet

我们找到 FrameworkServlet类,先观察一下当前类结构

在这里插入图片描述

根据Java语言的继承特性,我们从类结构上面看到,其实 FrameworkServlet也相当于继承了 HttpServlet,我们从 HttpServletBean中没有发现对 HttpServlet相关方法的重写,那么我们就要看一下在 FrameworkServlet抽象类中有没有对应的方法重写,当然了 FrameworkServlet里面也会有 Spring时间与上下文之间的操作,我们稍后再讲
在这里插入图片描述

根据这个方法重载,我们能够看到 FrameworkServlet确实对 HttpServlet进行了请求方法重写,由此可以看出 SpringWebMvc到这个时候,就已经对 HttpServlet的拓展已经做了大部分的工作了。我们上面提到的 HttpServletBean中的init()方法,最后一段是 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 {
    
    
      // 初始化 WebApplicationContext 上下文
			this.webApplicationContext = initWebApplicationContext();
      // 初始化 Spring框架中的 Servlet
			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");
		}
	}

FrameworkServlet抽象类中我们能够直观的看到,其中最关键的两个方法 initWebApplicationContext()initFrameworkServlet(),从 initWebApplicationContext()方法中我们能够看到是为了初始化在 Spring web 服务中的SpringContext上下文,通过初始化 SpringContext上下文,并且进行 refresh ,可以去加载对应的 HandlerMappingHandlerAdapterViewResolver ,但是当前 FrameworkServlet抽象类中的 initFrameworkServlet()方法是空的,没有进行实现,根据代码实现我们能够看到是 DispatcherServlet 类来进行实现的。

3、DispatcherServlet

我们打开 DispatcherServlet类,我们能够发现该类继承了 FrameworkServlet,也就是意味着,DispatcherServlet也是 Servlet的实现类,而且我们从 DispatcherServlet类的注释上面也能看到,该类对 Spring的作用是什么;

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers
or HTTP-based remote service exporters. Dispatches to registered handlers for processing
a web request, providing convenient mapping and exception handling facilities.

释义:
HTTP请求处理程序控制器的中央调度程序,例如用于Web UI控制器或基于HTTP的远程服务导出器。派遣到已注册的处理程序以处理Web请求,从而提供便利的映射和异常处理功能。

DispatcherServletSpringHttp请求处理的总的中央调度程序,我们可以理解为 DispatcherServlet就是 Spring最终对 Servlet的实现类,我们从最初的 Servlet类来进行一点点的实现,从最底层到最后的实现类都进行了一些分析,我们从 FrameworkServlet类中看到了对 Http请求方法的处理,但是方法里面最后都是去调用 processRequest()方法,然后 FrameworkServlet类把最终的请求处理交给了 doService()方法去执行,但是 doService()FrameworkServlet类中是没有实现的,但是 DispatcherServlet实现了这个方法,并且进行了最后的封装处理其核心处理是 doDispatch() ,所以最终调用环节是,当我们的 Servlet 容器(以 Tomcat为例)接收到 Http请求时对应的 Tomcat接收到请求然后通过 service()方法一层一层的去调用到 HttpServlet抽象类中,但是 Spring框架的 FrameworkServlet继承了 HttpServlet类,所以最终调用到 FrameworkServlet类中的根据请求方法选择对应的 doGet(),doPost()等方法。DispatcherServlet类中的 doDispatch()方法会通过请求地址与请求参数,找到对应的 RequestMapping 也就是我们写的 Controller里面的对应的方法,整体调用如下图所示。
在这里插入图片描述

根据以上源码解析我们知道了当 http请求到 Web容器中的一系列操作,这个流程我们清晰了之后,我们接下来讲解一下对应的 Spring是怎么去扫描到的我们方法上面的 RequestMapping的。

4、DispatcherServlet 查询调用Mapping原理解析

我们在上面说到 FrameworkServlet抽象类中会对 Spring相关组件进行初始化也就是 initWebApplication()方法执行,而 initWebApplication()方法是创建一个与 Web相关的 ApplicationContextApplicationContext都有一个 refresh()方法执行,而且我们也能看到在创建的时候有一个监听器的添加可以看到代码:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
    
    
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
    
    
			// The application context id is still set to its original default value
			// -> assign a more useful id based on available information
			if (this.contextId != null) {
    
    
				wac.setId(this.contextId);
			}
			else {
    
    
				// Generate default id...
				wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
						ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
			}
		}

		wac.setServletContext(getServletContext());
		wac.setServletConfig(getServletConfig());
		wac.setNamespace(getNamespace());
    // 添加 context.refresh()事件监听
		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();
	}
private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
    
    

		@Override
		public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
			FrameworkServlet.this.onApplicationEvent(event);
		}
	}
public void onApplicationEvent(ContextRefreshedEvent event) {
    
    
		this.refreshEventReceived = true;
		onRefresh(event.getApplicationContext());
}

从上面的代码中我们能够看到,其实 这些的方法调用,全部都是在 Servlet init()方法调用时才进行初始化的,也就是说当我们启动 Servlet容器的时候就会去初始化我们 Spring Web相关的 ApplicationContext,并且去扫描 @Controller@RequestMapping注解,而 FrameworkServlet类中的 onRefresh()方法具体的实现还是在 DispatcherServlet类中的实现

@Override
protected void onRefresh(ApplicationContext context) {
	initStrategies(context);
}

protected void initStrategies(ApplicationContext context) {
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
		initFlashMapManager(context);
}

根据 initStrategies()方法中的具体实现我们找到 initHandlerMappings()方法去解析源码,因为篇幅原因,我们先解析初始化的其中一段源码,其他的源码可以以后和大家在一起讨论。

首先我们先看 doDispatch()方法中,因为我们所有的Http请求都是要走这里面,而 Spring要根据 HttpServletRequest请求的 Url中获取请求地址,然后再去匹配相应的 RequestMapping(也就是我们Controller里面的请求方法),所以我们在断点的情况下查看一下一共有多少个 HandlerMapping能被查找到,看下图
在这里插入图片描述
从上面8个 HandlerMapping中我们能看到其中的 RequestMappingHandleMapping对象与我们的 @RequestMapping最接近,但是对不对呢?我们可以验证一下。在 doDispatch()方法中有一个 getHandler()方法的调用,我们可以模拟一个请求到我们的接口,看一下最后的 HandlerMapping到底是哪一个
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qfYDnDox-1617008145907)(Spring MVC 源码解析.assets/image-20210329154116993.png)]

我们在这个方法中进行断点来进行验证,最后过滤掉其他不符合的 HandleMapping发现就是最后的 RequestMappingHandleMapping来进行处理并且返回对应的请求接口
在这里插入图片描述

由此我们知道了在 Spring框架中对我们 @RequestMapping接口的整体调用流程,根据我们的请求地址获取到对应的 HandleMapping并且找到我们对应的接口方法,然后根据请求的参数和我们接口方法参数进行解析与对应,这时候就需要用到我们的 HandlerAdapter @RequestMapping对应的适配器,用于我们对本地接口的调用,其核心调用方法是 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod这个方法是用于接口的数据绑定与数据返回的处理,并且调用本地接口的操作进行了 SpringAOP切面的动态代理操作。通过这个操作,我们有的小伙伴会去想 Spring是什么时候去加载了那么多的接口 HandlerMapping呢?我们来继续查看 RequestMappingHandleMapping发现这个类实现了 InitializingBean接口其中的 afterPropertiesSet()方法

在这里插入图片描述

初始化一些 配置之后,去调用了父类的 afterPropertiesSet(),我们继续往上查看在父类中的一些操作有哪些

public void afterPropertiesSet() {
		initHandlerMethods();
}

protected void initHandlerMethods() {
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			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);
			}
		}
		if (beanType != null && isHandler(beanType)) {
			detectHandlerMethods(beanName);
		}
	}

首先是先去查询所有已经注册成功的 Bean名称,根据判断循环调用 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean方法。这个方法中有一个判断 isHandler()方法,是用于判断当前的 class对象里面有没有 @Controller@RequestMapping注解

@Override
protected boolean isHandler(Class<?> beanType) {
	return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
			AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

通过这个判断可以清楚找到我们相关的 业务请求http接口并且注册到 AbstractHandlerMethodMapping中的 mappingRegistry内置类中

protected void detectHandlerMethods(Object handler) {
		Class<?> handlerType = (handler instanceof String ?
				obtainApplicationContext().getType((String) handler) : handler.getClass());

		if (handlerType != null) {
			Class<?> userType = ClassUtils.getUserClass(handlerType);
			Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
					(MethodIntrospector.MetadataLookup<T>) method -> {
						try {
							return getMappingForMethod(method, userType);
						}
						catch (Throwable ex) {
							throw new IllegalStateException("Invalid mapping on handler class [" +
									userType.getName() + "]: " + method, ex);
						}
					});
			if (logger.isTraceEnabled()) {
				logger.trace(formatMappings(userType, methods));
			}
			methods.forEach((method, mapping) -> {
				Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
				registerHandlerMethod(handler, invocableMethod, mapping);
			});
		}
	}
	
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		this.mappingRegistry.register(mapping, handler, method);
	}

而在我们 org.springframework.web.servlet.DispatcherServlet#getHandler获取对应的 HandlerExecutionChain对象时我们发现其实是调用的 org.springframework.web.servlet.handler.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);
		}

		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 (CorsUtils.isCorsRequest(request)) {
    
    
			CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

开始就是 getHandlerInternal()方法的调用,这个方法就是去查询当前 Request请求对应的 Mapping就是从我们 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry最开始注册进去的对象中查找。我们找到对应的实现类,就是 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerInternal对应的方法

@Override
	protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		this.mappingRegistry.acquireReadLock();
		try {
			HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
			return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
		}
		finally {
			this.mappingRegistry.releaseReadLock();
		}
	}

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
		List<Match> matches = new ArrayList<>();
		List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
		if (directPathMatches != null) {
			addMatchingMappings(directPathMatches, matches, request);
		}
		if (matches.isEmpty()) {
			// No choice but to go through all mappings...
			addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
		}

		if (!matches.isEmpty()) {
			Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
			matches.sort(comparator);
			Match bestMatch = matches.get(0);
			if (matches.size() > 1) {
				if (logger.isTraceEnabled()) {
					logger.trace(matches.size() + " matching mappings: " + matches);
				}
				if (CorsUtils.isPreFlightRequest(request)) {
					return PREFLIGHT_AMBIGUOUS_MATCH;
				}
				Match secondBestMatch = matches.get(1);
				if (comparator.compare(bestMatch, secondBestMatch) == 0) {
					Method m1 = bestMatch.handlerMethod.getMethod();
					Method m2 = secondBestMatch.handlerMethod.getMethod();
					String uri = request.getRequestURI();
					throw new IllegalStateException(
							"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
				}
			}
			request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
			handleMatch(bestMatch.mapping, lookupPath, request);
			return bestMatch.handlerMethod;
		}
		else {
			return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
		}
	}

里面最核心的查找源就是我们在 Spring初始化启动的时候注册进去的那些 @RequestMapping@Controller标注的方法上面

总结

通过上面一些列的源码导读我们能够知道了 Spring是如何对 Servlet进行扩展与整合一起的
首先扩展 HttpServlet通过 Tomcat启动是加载 web.xml并且在 web.xml中去配置 DispatcherServlet用于监听初始化,使其调用 init()方法初始化 web上下文,并且在启动的时候 Spring对含有 @RequestMapping或者 @Controller注解的类进行注册到 org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#mappingRegistry中,启动完成之后,通过 DispatcherServletdoDispatch()方法通过对应的HandlerMapping去查找到与 http url想匹配的 HandlerExecutionChain(本地接口),然后根据 RequestMappingHandlerAdapter进行接口适配去完成对应方法的参数与返回值的解析,最后返回到客户端。

至此我们的 SpringWebMVC相关源码就已经全部解析完毕了,希望大家可以在业余时间,根据以上方法进行一个自我的源码导读,能够更好的理解其相关处理流程,后面小弟也会对更多的方面的源码进行解读,争取给读者更好的一种理解方式,也希望给大家一些帮助。

猜你喜欢

转载自blog.csdn.net/qq_38100149/article/details/115305671