Java web 入门——2.浅谈servletContext、webApplicationContext与DispatcherServlet

Context我们通常解释为上下文环境,我想用“容器”来表述它更容易理解一些。


context.png

1.ServletContext

ServletContext,即Servlet容器。首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其包含从容器环境中获得的初始化信息,提供对应用程序中所有Servlet共有的各种资源功能的访问,为后面的spring IoC容器提供宿主环境。在启动一个web应用时,ServletContext会为它创建一个ServletContext对象,每个web应用有唯一的ServletContext对象。

(1)多个Servlet通过ServletContext对象访问上文属性

ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

servlet可以通过名称将对象属性绑定到上下文,可以被同一个web应用的其他servlet使用。

setAttribute
getAttribute
getAttributeNames
removeAttribute

示例:

// 获取ServletContext对象
ServletContext context = this.getServletContext();    
context.setAttribute("appName", "webApp"); 

在其它的Servlet中利用ServletContext对象获取

ServletContext context = this.getServletContext();   
String name = context.getAttribute("appName");   
(2)获取WEB应用的初始化参数

getInitParameter
getInitParameterNames

在web.xml文件中配置需要初始化的参数信息

<web-app>   
 <context-param>   
   <param-name>url</param-name>   
    <param-value>http://localhost:3306/web</param-value>   
 </context-param>   
</web-app>  

获取初始化参数

 ServletContext context = this.getServletContext();   
//获取指定名称的初始化参数   
String url = context.getInitParameter("url"); 
 //获取web.xml文件中所有的初始化应用参数          
 Enumeration<String> enumer = context.getInitParameterNames();   
(3)实现Servlet的转发
ServletContext context = this.getServletContext();   
 //根据转发的地址获取 RequestDispatcher对象
RequestDispatcher  rd  = context.getRequestDispatcher("/index.jsp");   
//调用转发方法 以下采用任意方法即可    
rd.forward(request, response);   
  //rd.include(request, response);   

注意:forward与include的区别 :
forward方法调用后在响应中的没有提交的内容被自动消除,原先Servlet的执行则终止。将请求转发给其他的Servlet后,由被调用的Servlet负责对请求做出响应。
include方法使原先的Servlet和转发到的Servlet都可以输出响应信息,即原先的Servlet还可以继续输出响应

(4)用ServletContext对象读取资源文件

getResource
getResourceAsStream
getResource和getResourceAsStream

读取资源文件的三种方式,获取实现的代码如下:

ServletContext context = this.getServletContext();     

//第一种方式    
URL url = context.getResource("WEB-INF/classes/db.properties");   
InputStream is =  url.openStream();   

//第二种方式   
 // 读取db.properties文件
String path =context.getRealPath("WEB-INF/classes/db.properties");   
 // 根据文件的路径 构建文件对象
File file = new File(path);   
 // 根据file文件对象 创建输入流
InputStream is = new FileInputStream(file);   

//第三种方式   
InputStream is = context.getResourceAsStream("WEB-INF/classes/db.properties ");    

Web服务器可能支持一个服务器上多个逻辑主机共享一个IP地址。这功能有时被称为“虚拟主机”。这种情况下,每一个逻辑主机必须有它自己的servlet上下文。Servlet上下文不可以被多个虚拟主机共享


2.WebApplicationContext

在web应用中,会使用到WebApplicationContext,它继承自ApplicationContext,但是与之不同的是WebApplicationContext需要ServletContext,也就是说它必须拥有Web容器的前提下才能完成启动的工作。它允许从相对于web根目录的路径中加载配置文件完成初始化工作。
在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件。此时contextLoaderListener 会监听到这个事件,其调用contextInitialized方法,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中.

(1)在web.xml初始化WebApplicationContext:
<context-param>      
<param-name>contextConfigLocation</param-name>      
<param-value>/WEB-INF/applicationContext.xml</param-value>      
</context-param>      
     
<listener>      
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>      
</listener>  
<!--or -->
<servlet>      
<servlet-name>context</servlet-name>      
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>      
<load-on-startup>1</load-on-startup>      
</servlet> 

有两种方法:
一种是用ContextLoaderListener这个Listerner,另一种是ContextLoaderServlet这个Servlet,这两个方法都是在web应用启动的时候来初始化WebApplicationContext,我个人认为Listerner要比 Servlet更好一些,因为比起Listerner监听应用的启动和结束,而Servlet得启动要稍微延迟一些,如果在这时要做一些业务的操作,启动的前后顺序是有影响的。

那么在ContextLoaderListener和ContextLoaderServlet中到底做了什么呢?
以ContextLoaderListener为例,我们可以看到:

public void contextInitialized(ServletContextEvent event) {
this.contextLoader = createContextLoader();
this.contextLoader.initWebApplicationContext(event.getServletContext());
}
protected ContextLoader createContextLoader() {
return new ContextLoader();
}

ContextLoader 是一个工具类,用来初始化WebApplicationContext,其主要方法就是initWebApplicationContext,我们继续追踪initWebApplicationContext这个方法(Spring中的源码),ContextLoader是把WebApplicationContext ( XmlWebApplicationContext是默认实现类)放在了 ServletContext中,ServletContext也是一个“容器”,也是一个类似Map的结构,而 WebApplicationContext在ServletContext中的KEY就是WebApplicationContext.ROOT_WEB_APPLICA TION CONTEXT ATTRIBUTE,我们如果要使用 WebApplicationContext 则需要从ServletContext取出,Spring提供了一个 WebApplicationContextUtils类,可以方便的取出WebApplicationContext,只要把 ServletContext传入就可以了。

(2)从ServletContext中获取WebApplicationContext
WebApplicationContext  ctx=
WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());

3.DispatcherServlet

contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是xmlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关的属性为属性Key(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码),也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文定义的那些bean。

WebapplicationServlet为Spring的根上下文或父上下文,而DispatcherServlet则为子上下文。它的主要用作职责调度工作,本身主要用于控制流程,主要职责如下:

(1)文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
(2)通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
(3)通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
(4)通过ViewResolver解析逻辑视图名到具体视图实现;
(5)本地化解析;
(6)渲染具体的视图等;
(7)如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。

DispatcherServlet初始化的上下文加载的Bean是只对SpringMVC有效的Bean, 如Controller、HandlerMapping、HandlerAdapter等等,该初始化上下文只加载Web相关组件。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文定义的那些bean。让我们来看一个具体的DispatcherServlet的配置事例:

<servlet>
    <servlet-name>qsServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:conf/qs-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
     </servlet>

<servlet-mapping>
    <servlet-name>qsServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
 </servlet-mapping>

param-value指定了web组件的配置文件
load-on-startup表示启动容器时初始化该Servlet
url-pattern表示哪些请求交给Spring Web MVC处理, “/” 是用来定义默认servlet映射的。示例中用“*.do”表示拦截所有以do为扩展名的请求。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349