本系列文章皆在分析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
的监听器,具体配置如下。
<listener>
<listener-class>org.springframework.web.
context.ContextLoaderListener</listener-class>
</listener>
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
相关的组件信息,如控制器、拦截器等。