延伸阅读:Root Context and Dispatcher Servlet Context into Spring MVC
千万不要把ServletContext与Spring ApplciationContext搞混
ServletContext
要介绍 Servlet 必须要先把 Servlet 容器说清楚,Servlet 与 Servlet 容器的关系有点像枪和子弹的关系,枪是为子弹而生,而子弹又让枪有了杀伤力。虽然它们是彼此依存的,但是又相互独立发展,这一切都是为了适应工业化生产的结果。从技术角度来说是为了解耦,通过标准化接口来相互协作。既然接口是连接 Servlet 与 Servlet 容器的关键,那我们就从它们的接口说起。
前面说了 Servlet 容器作为一个独立发展的标准化产品,目前它的种类很多,但是它们都有自己的市场定位,很难说谁优谁劣,各有特点。例如现在比较流行的 Jetty,在定制化和移动领域有不错的发展,我们这里还是以大家最为熟悉 Tomcat 为例来介绍 Servlet 容器如何管理 Servlet。Tomcat 本身也很复杂,我们只从 Servlet 与 Servlet 容器的接口部分开始介绍,关于 Tomcat 的详细介绍可以参考我的另外一篇文章《 Tomcat 系统架构与模式设计分析》。 Tomcat 的容器等级中,Context 容器是直接管理 Servlet 在容器中的包装类 Wrapper,所以 Context 容器如何运行将直接影响 Servlet 的工作方式。
Tomcat 的容器分为四个等级,真正管理 Servlet 的容器是 Context 容器,一个 Context 对应一个 Web 工程,在 Tomcat 的配置文件server.xml
中可以很容易发现这一点,如下:
<Context path="/projectOne " docBase="D:\projects\projectOne"
reloadable="true" />
ServletContext
是Java EE里的Api,官方解释是
Defines a set of methods that a servlet uses to communicate with its servlet container, for example, to get the MIME type of a file, dispatch requests, or write to a log file.
ServletContext
定义了一组servlet与servlet容器(如tomcat)交流的方法
There is one context per "web application" per Java Virtual Machine. (A "web application" is a collection of servlets and content installed under a specific subset of the server's URL namespace such as /catalog and possibly installed via a .war file.)
每个JVM中每个"web 应用"只有一个context(一组servlet的集合;所包含的内容被安装在服务器URL命名空间的一个特殊子集下,比如/catalog(http://localhost:8080/catalog);通过一个.war文件进行安装)
In the case of a web application marked "distributed" in its deployment descriptor, there will be one context instance for each virtual machine. In this situation, the context cannot be used as a location to share global information (because the information won't be truly global). Use an external resource like a database instead.
在分布式部署环境下,因为web application是一个JVM一个的(one context per "web application" per Java Virtual Machine),因此应用之间不能通过ServletContext 共享信息,因为不同虚拟机上,即使是同个web application,它们的ServletContext也是不同的
The ServletContext object is contained within the ServletConfig object, which the Web server provides the servlet when the servlet is initialized.
一个ServletConfig
对象包含了ServletContext
对象,ServletConfig
是web server在servlet 初始化时提供给servlet的,因此任何servlet都可以通过获取ServletConfig
再获取ServletContext
在传统web应用中,web.xml作用就是用来配置ServletContext
<context-param/>
对应于ServletContext.getInitParameter()
<servlet/><filter/><listener/>
就是往ServletContext
中加入servlet fliter listener
而<servlet/>
中的<init-param>
对应于ServletConfig.getInitParameter()
,<servlet/>
就是用来通过
ServletConfig
初始化servlet的
看一段spring boot 中的代码
public class MyWebAppInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext container) {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext = ...
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.register(DispatcherConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("main", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("main/");
}
}
Root Context
The root-context in a Spring application is the ApplicationContext
that is loaded by the ContextLoaderListener
(传统项目中,写到web.xml的<listener>
中,在ServletContext启动过程中启动,相当于当监听到web servlet容器启动时,启动spring应用上下文,用来装配spring里的一些bean). This context should have globally available resources like services, repositories, infrastructure beans (DataSource, EntityManagerFactorys etc.) etc.
The ContextLoaderListener
registers this context in the ServletContext
under the name org.springframework.web.context.WebApplicationContext.ROOT
.
(将Spring Root ApplicationContext注册成ServletContext
一个属性,这样就可以被整个应用共用了,应用中的所有组件、servlet等都可以通过获取ServletContext
这一属性获取该ApplicationContext,然后就能从中getBean
)
If you load an ApplicationContext
yourself and register it with the name above in the ServletContex
t that will then qualify as the root-context.
Child Context
The child-context in a Spring application is the ApplicationContext
that is loaded by a DispatcherServlet
(or for instance a MessageDispatcherServlet
in a Spring-WS application
). This context should only contain beans relevant to that context, for Spring MVC that would be ViewResolvers
, HandlerMappings
etc.
The servlet registers this context in the ServletContext under the name org.springframework.web.servlet.FrameworkServlet.CONTEXT.<servlet-name>
.
等于说在启动一个子Spring 上下文,这个上下文里的内容只与DispatcherServlet
有关,DispatcherServlet
其实就是Spring MVC的入口,因为所有请求都必须经过它(如果采用标准spring mvc配置的话),那么一些和spring mvc相关的bean就放在这个上下文里。
如上代码中的这一段,就是通过dispatcherContext这个上下文,实例化一个DispatcherServlet
,然后将这个servlet添加到ServletContext
里
container.addServlet("main", new DispatcherServlet(dispatcherContext));