【Spring】详解ContextLoaderListener和DispatcherServlet的区别

发布时间:2018-05-22 18:57:26 作者:JIALY 332次浏览

        在开发以Springmvc为框架的web应用中,我们经常会看到web.xml既配置了ContextLoaderListener监听器,又配置了名为DispatcherServlet的Servlet,我们知道DispatcherServlet可以看作springmvc程序的入口,前端的请求提交给DispatcherServlet后,它会寻找对应的HandlerMapping,继而找到处理器。处理器进行相关的业务处理,返回模型与视图,DispatcherServlet通过视图解析器将ModelAndView进行处理之后返回给前端。

        要弄懂他们俩的作用及区别,最好的方式就是去分析他们的源码,知其表里,才能在理解与使用的层次上提高一个水平。

        先来看看ContextLoaderListener的实现:

        1528095475047073347.png

        通过上面代码可以看出ContextLoaderListener继承了ContextLoader类并实现了ServletContextListener接口,ServletContextListener接口能够监听ServletContext的变化。我们知道基于Servlet的web服务器,在服务器启动时ServletContext会被创建,在服务器关闭时ServletContext会被销毁,所以实现了ServletContextListener接口的类就能在服务启动或关闭时处理一些事情。

        1528095543671004018.png

        那么ContextLoaderListener就是通过父类的initWebApplicationContext()方法来初始化上下文。上下文可以理解为一块独立的内存空间,里面可以包含各种bean资源、对象资源,这些资源可以被上下文独享、也可以共享给其他的上下文。打个比方,可以把上下文看做是一个球队,各种资源可以看成球队里的运动员,球队里的队员可以外派给其他球队,当然也有核心球员只能被自己的球队使用。球队与球队之间可以建立联盟关系,这样资源就可以共享。大概意思就是这样,每个人的理解都可能不一样,回归正题。再看下initWebApplicationContext()方法的实现。

        1528095613076035743.png

        首先会去ServletContent里找名为org.springframework.web.context. WebApplicationContext.ROOT的上下文对象,如果存在抛异常提示已经存在。如果不存在会调用createWebApplicationContext(servletContext)方法来创建。

        1528095659132012995.png

        1528095659209057992.png

        继续来看createWebApplicationContext方法,该方法首先会调用determineContextClass方法,该方法会先从上下文初始化参数里找名为CONTEXT_CLASS_PARAM对应的值,该值其实就是上下文类名,可以在web.xml中配置。找到了就创建对应的上下文类对象,找不到就从defaultStrategies对象中获取。来看看defaultStrategies对象,它会在ContextLoader类加载的时候被初始化,读取ContextLoader类路径下ContextLoader.properties属性文件中的值放到defaultStrategies对象中。

        1528095720399072595.png

        1528095720403070821.png

        看下ContextLoader.properties内容,我们就知道在web.xml中配置以下内容也可以完成上下文对象的创建。

        1528095757059051916.png

        创建好的上下文最终会放到属性名为org.springframework.web.context. WebApplicationContext.ROOT的ServletContext中。 接下来看看DispatcherServlet的实现:

        1528095782581037397.png

        如上面类的层次结构图所示,DispatcherServlet继承自HttpServletBean,在web容器启动时会调用HttpServletBean的init()方法,查看代码发现在init()方法里会调用initServletBean()方法,该方法默认实现为空,所以在子类重载该方法就可以在web容器启动时做一些工作。

        1528095813084009600.png

        1528095813125000197.png

        再查看FrameworkServlet的initServletBean()的方法,可以看到在该方法里会调用initWebApplicationContext()方法来完成WebApplicationContext的初始化。查看initWebApplicationContext()方法,我们可以了解到程序会先去ServletContext里找名为org.springframework.web.context. WebApplicationContext.ROOT的上下文,如果在web.xml里定义了ContextLoaderListener监听器,那么此处的rootContext就不为空。接下来可以看到findWebApplicationContext()方法,该方法会从ServletContext里找你在web.xml里配置的contextAttribute参数值对应的attribute。故如下图,同时配置了ContextLoaderListener,那么DispatcherServlet创建的上下文就和ContextLoaderListener创建上下文就完全一样了。

        1528095862329095200.png

        如果没有配置contextAttribute参数或者配置了contextAttribute参数,但是在ServletContext里没有找到对应的上下文,接着会调用createWebApplicationContext()方法来创建应用上下文,其参数为上面的rootContext,这里的rootContext的作用其实就是设置其作为创建好的上下文的父上下文。具体的创建过程在这里不再赘述,至此DispatcherServlet创建的上下文结束。

        从上面ContextLoaderListener和DispatcherServlet的加载过程我们可以看出,ContextLoaderListener创建的上下文会和DispatcherServlet创建的上下文是父子关系。所以ContextLoaderListener一般会加载整个Spring容器相关的bean配置,如:Dao、Service、Log、DataSource、一些属性配置文件等,而DispatcherServlet一般会加载MVC相关的bean配置,如: ViewResolver, Controller, MultipartResolver, ExceptionHandler等。


关键字词: spring ContextLoaderListener DispatcherServlet