Spring原码解析之SpringMVC(DispatcherServlet)初始化流程

在开始之前我希望你能边操作边参考我的博客!!!

Servlet3.0

在说SpringMVC前先简单说一下Servlet3.0相关知识。Servlet3.0后项目里web.xml可以没有,原因是Servlet3.0后允许我们在

META-INF里键一个services包,在这个包中加入名为 javax.servlet.ServletContainerInitializer 的文件,内容为自定义类的全类名。

如SpringMVC中这样定义:

     

这个自定义类必须实现 ServletContainerInitializer 这个类,这个类只有一个方法:

void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;

可以理解为在服务器启动时会执行这个方法,这样我们就可以在这个方法里干活了。

如SpringMVC这样做:


SpringMVC容器启动过程分析

了解了Servlet3.0后我们在

SpringServletContainerInitializer 类的 --->  onStartup 方法 打上断点调试。

发现它只是执行了一遍这个方法什么也没干就返回了,因为我们并没有写一个配置类并实现

WebApplicationInitializer这个接口,因此我们先跳过这个类,从基本的web.xml配置的方式开始!!!

   ----------

在web.xml中配置DispatcherServlet和相关配置

DispatcherServlet的关系结构图

由于配置了<load-on-startup/>,所以当服务器启动时会加载DispatcherServlet.

Servlet生命周期要求,每个Servlet在第一次加载时都会调用其 init() 方法,但是DispatcherServlet本身没有这个方法,所以系统会去它父类HttpServletBean寻找,然后执行,以下是 init() 方法原码:

具体过程分析:

最后调用FrameworkServlet的 initServletBean()方法,FrameworkServlet就是用来初始化Spring容器的其原码如下:

接下来我们来看看 initWebApplicationContext() :


	protected WebApplicationContext initWebApplicationContext() {
	
		//通过WebApplicationContextUtils 获取Spring父容器
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		//初始化一个SpringMVC的空容器
		WebApplicationContext wac = null;
		//判断是否已经有编程式的容器了 如果有最后调用configureAndRefreshWebApplicationContext(cwac)进行整合
		//我们这里是没有
		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()) {
					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);
				}
			}
		}
		//刚开始是Null所以进入
		if (wac == null) {
			//在servletContext中寻找 SpringMVC容器  刚开始是没有的
			wac = findWebApplicationContext();
		}
		//进入
		if (wac == null) {
			// No context instance is defined for this servlet -> create a local one

			//既没有编程式的容器也没有在servletContext中找到 所以 这里才是真正的创建springmvc容器实例
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {

			//这里才是我们要的  真正初始化DispatcherServlet的时候
            //这里我们会最后讲,因为这是容器初始化完后才执行的
            //这个方法被DispatcherServlet覆写了
			onRefresh(wac);
		}
		//最后将创建好的SpringMVC容器放入servletContext中
		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
			if (this.logger.isDebugEnabled()) {
				this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
						"' as ServletContext attribute with name [" + attrName + "]");
			}
		}
		//将这个SpringMVC容器返回
		return wac;
	}


我们再来看一下:createWebApplicationContext()

最后我们看一下前面没有讲的 onRefresh(wac); 而这个方法被DispatcherServlet覆写了 容器初始化完后执行 

这个方法是完成DispatcherServlet组装的核心方法:

它又调用initStrategies方法,我们看看它的原码:

不知道有没有种很亲切的感觉,哈哈... 它就是用来装配DispatcherServlet的部件的,DispatcherServlet的功能基本在这,我们打上断点,后续在深入。

最后我们总结一下:

Servlet3.0后项目里web.xml可以没有,原因是Servlet3.0后允许我们在META-INF里建一个services包,在这个包中加入名为 javax.servlet.ServletContainerInitializer 的文件,内容为自定义类的全类名。这也是基于JavaConfig配置Spring的基础。

接下来我们对SpringMVC容器和DispatcherServlet的装配流程进行了分析

1)在web.xml中配置

2)调用HttpServletBean 的init方法,拿到初始化参数为DispatcherServlet赋值,并调用FrameworkServlet的 initServletBean()方法

3)接下来调用 initWebApplicationContext() 完成容器的创建

4)创建完容器后刷新容器onRefresh(wac),将DispatcherServlet与容器关联

5)调用initStrategies方法组装DispatcherServlet

总的流程就是四个init打头的方法init   initServletBean   initWebApplicationContext  initStrategies

最后:因为这是一次新的开始,所以参考了牛人的博客加入了自己的理解和分析:https://blog.csdn.net/cwl_0514/article/details/80095880

猜你喜欢

转载自blog.csdn.net/qq_41750725/article/details/87542989