SpringMVC源码剖析

工作流程
在这里插入图片描述

加载 DispatcherServlet
追朔源码,最终晓得这个也和Servlet有了瓜葛,要想完成用户的一次请求,第一步就是加载 DispatcherServlet,下面我们介绍一下过程:
class DispatcherServlet extends FrameworkServlet … ——>
class FrameworkServlet extends HttpServletBean … ——>
class HttpServletBean extends HttpServlet … ——>
class HttpServlet extends GenericServlet … ——>
abstract class GenericServlet implements Servlet …

分析:
我们从下向上看,可以知道Servlet是一个接口,那Servlet到底是什么东西呢,说白了就是开发动态网站的技术,Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:

  1. 编写一个Java类,实现servlet接口。
  2. 把开发好的Java类部署到web服务器中。

是不是明白点了,其实在设计什么东西的时候往往都会留下一些疑问,因为什么东西都不可能一次性设计好,比如一个好的程序更是常常在更新,所以Sun公司也就提供了两个类实现Servlet接口,是HttpServlet和GenericServlet,其中HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。而GenericServlet这个类的存在使得编写Servlet更加方便。它提供了一个简单的方案,这个方案用来执行有关Servlet生命周期的方法以及在初始化时对ServletConfig对象和ServletContext对象进行说明。

DispatcherServlet 的init()方法(初始化方法)在其父类HttpServletBean 中实现,主要作用是加载web.xml(总配置文件)中DispatcherServlet 的配置,并调用子类的初始化。

  1. init()
@Override
	public final void init() throws ServletException {

		// Set bean properties from init parameters.
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				bw.setPropertyValues(pvs, true);
			}
			catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}

		// Let subclasses do whatever initialization they like.
		initServletBean();
	}

在 HttpServletBean 的init() 方法中调用了 initServletBean() 方法,这个方法在 FrameworkServlet 类中实现,主要作用是建立 WebApplicationContext 容器。

  1. initServletBean()源码
@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");
		}
	}
  1. 初始化操作 WebApplicationContext
    initWebApplicationContext()方法主要用于创建和刷新WebApplicationContext实例,并对Servlet功能所使用的变量进行初始化。
    initWebApplicationContext()源码
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
						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) {
			// No context instance is defined for this servlet -> create a local one
			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);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}
  1. 建立好 WebApplicationContext 后,通过onRefresh(ApplicationContext context) 方法回调,进入 DispatcherServlet 类中,因为在DispatcherServlet 类中存在 onRefresh() 方法。

onRefresh()

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

initStrategies() 负责SpringMVC九个组件的初始化。

initStrategies()

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

走到这里就算完成了第一步了。
如果没有配置HandlerMapping,HandlerAdapter,ViewResolver等组件就会使用 DispatcherServlet.properties 文件中默认的组件。

DispatcherServlet.properties文件内容如下

# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.

org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver

org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver

org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
	org.springframework.web.servlet.function.support.RouterFunctionMapping

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
	org.springframework.web.servlet.function.support.HandlerFunctionAdapter


org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator

org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver

org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager

紧接着就是 DispatcherServlet 调用 HandlerMapping 来处理 request(请求) 和 handler(处理) 之间的映射关系,调用 HandlerMapping 类的唯一方法 getHandler() ,而 handler 被包装化为 HandlerExecutionChain类 ,也就是 HandlerMapping 直接与 HandlerExecutionChain 打交道,
HandlerMapping 的 getHandler() 源码如下:

@Nullable
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

可以得知,的确是与 HandlerExecutionChain 直接打交道,调用了其 getHandler方法,而且方法参数也是 request ,这也就对应上了 HandlerMapping 的确是处理 request 和 handler 之间的映射关系,
HandlerExecutionChain 的 getHandler() 源码如下:

public Object getHandler() {
		return this.handler;
	}

这下就得到了真正的handler(所谓的处理器对象)。

HandlerExecutionChain 这个类也可以说是 handler执行链,包含了 handler 和 interceptor ,handler 由 getHandler()方法可以得到,interceptor 由 getInterceptors() 方法可以得到,所以配置文件也可能会加入这里。

得到的 handler 会根据url,method,context-type等找到对应的控制器(controller),然后调用 getHandlerAdapter() 方法得到 HandlerAdapter,
getHandlerAdapter()源码

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 中首先调用 supports() 方法判断是否支持该 handler ,如果支持则经过适配执行具体的 Controller ,Controller 将请求处理的具体结果放入 ModelAndView 中,handler 将结果传给 ViewResolver 渲染解析得到最终的结果反还给用户。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝朋友丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值