本系列文章皆在分析SpringMVC
的核心组件和工作原理,让你从springmvc
浩如烟海的代码中跳出来,以一种全局的视角来重新审视SpringMVC
的工作原理SpringMVC
。
思考,输出,沉淀。用通俗的语言陈述技术,让自己和他人都有所收获。
作者:毅航😜
前言
随着 Spring Boot
和 Spring Cloud
在开发中的普及,极大的简化了web开发的上手难度。可能你已经忘记当年经典的 Servlet + Spring MVC
的组合,忘记了那个繁琐配置 web.xml
时代。
本文将从web.xml
文件中的一行简单配置开始分析,从细节着手,重新来分析 Servlet
是怎么和 Spring MVC
进行集成并完成Spring
容器初始化工作的。
下图展示了本系列文章重点分析的组件信息,其中 ContextLoaderListener
是本文分析的重点。
熟悉又陌生的配置文件
在使用Spring MVC之前,通常我们会在web.xml
文件中配置一个叫做ContextLoaderListener
的监听器,具体配置如下。
org.springframework.web.
context.ContextLoaderListener
SpringMVC
作为web容器, 其核心本质就是对servlet
的封装和处理, 所以必定需要一个servlet
容器,自然而然就想到了其的使用肯定需要借助于Tomcat
的帮助。
而Tomcat
的基本工作流程为,加载web.xml
,解析其中内容,而其中配置有Spring,SpringMVC
的配置文件信息,所以我们选择ContextLoaderListener
作为入口,来探究和分析SpringMVC
的启动流程。
在开始之前,我们不妨先思考一个问题,那这个监听器择ContextLoaderListener
具体有什么作用呢?接下来,我们将带着这个问题来重新审视ContextLoaderListener
这个组件。
监听器
监听器(Listener)是Servlet
体系下的一个组件,其主要可用于监听和响应Web应用程序中事件的组件。它可以监听Servlet
、Session
、请求、上下文等不同级别的事件,并在事件发生时执行相应的逻辑。而组件ServletContextListener
定义了方法contextInitialized
和contextDestroyed
分别用来监听ServletContext
的创建和销毁。
回到我们之前讨论的ContextLoaderListener
,其有如下的继承体系结构。
具体来看,ContextLoaderListener
实现了javax.servlet.ServletContextListener
接口,这保证了其可用于监听ServletContext
的生命周期事件。
其外,由于ContextLoader
是Spring
中的一个辅助类,它用于在Web
应用程序启动时加载Spring
的应用上下文信息。它主要用于创建和初始化Spring
的根应用上下文,以及将其关联到ServletContext
中,使得在整个Web
应用程序中可以共享和重用Spring
的Bean
。
至此,我们可以大致明白SpringMVC
一定程度上可以正常工作,其主要通过事件监听的模式来实现,而监听器正常工作,通常需要几大关键信息:即被监听对象,事件,监听器。 此时,ContextLoaderListener
就相当于一个监听器,被监听的对象则是ServletContext
,而事件则是Tomcat
容器启动。
对于ContextLoaderListener
的类结构信息,我们需要强调的是:
ServletContext
对象是Tomcat
的ServletContextListener
是Tomcat
提供的接口ContextLoaderListener
则是Spring
写的,并实现了ServletContextListener
Web环境中Ioc容器的构建
由于ContextLoaderListner
实现了ServletContextListener
接口,所以当Tomcat
中的ServletContext
创建时,便会触发一个创建ServletContextEvent
事件,此时ContextLoaderListener
便会监听到该事件,从而执行其中的contextInitialized()
,而这个方法内部的initWebApplicationContext()
就是用来初始化Spring
的IOC
容器的。
public class ContextLoaderListener
extends ContextLoader implements ServletContextListener {
/**
* 初始化web容器
*/
@Override
public void contextInitialized(ServletContextEvent event) {
initWebApplicationContext(event.getServletContext());
}
其中方法initWebApplicationContext
的逻辑如下
public WebApplicationContext initWebApplicationContext(ServletContext
servletContext) {
// ... 省略异常捕获,条件判断
if (this.context == null) {
// 创建一个web容器
this.context = createWebApplicationContext(servletContext);
}
// ... 省略异常捕获,条件判断
// 加载配置文件中context-param信息,刷新spring容器
configureAndRefreshWebApplicationContext(cwac, servletContext);
return this.context;
}
在上述代码中,我们剔除掉了大量无关代码,不难发现initWebApplicationContext
主要做了两件事:
- 创建一个web容器
- 加载spring配置文件,完成容器刷新工作
经过这一套流程处理下来,Spring
的IOC
容器也就初始化完成,同时还会构建一个Web环境的容器WebApplicationContext
容器信息。
总结
ContextLoaderListener
的作用是监听Web
应用程序的启动和销毁事件,同时在Web
容器启动时从web.xml
中获取Spring
的相关配置文件,完成bean
的初始化。
其实ContextLoaderListener
更像一个桥梁,保证了Spring
和SpringMVC
之间的整合。事实上,如果仅使用SpringMVC
,而不使用Spring
的其他功能(例如Spring
的IoC
容器和Bean
管理等),则在配置文件中配置ContextLoaderListener
是可选的。
例如,如果仅创建Controller
来处理请求和渲染视图,通常不需要显式配置ContextLoaderListener
。因为DispatcherServlet
自动创建一个子级的容器,并管理与SpringMVC
相关的组件信息,如控制器、拦截器等。