MyException - 我的异常网
当前位置:我的异常网» 开源软件 » Servlet轨范简介

Servlet轨范简介

www.MyException.Cn  网友分享于:2013-11-16  浏览:0次
Servlet规范简介
引言

Web 框架一般是通过一个 Servlet 提供统一的请求入口,将指定的资源映射到这个 servlet, 在这个 servlet 中进行框架的初始化配置,访问 Web 页面中的数据,进行逻辑处理后,将结果数据与的表现层相融合并展现给用户。 WEB 框架想要在符合 Servlet 规范的容器中运行,同样也要符合 Servlet 规范。
将一个 WEB 框架注入到一个 servlet 中,主要涉及到 Servlet 规范中以下部分:
Xml代码
部署描述符 
映射请求到 Servlet 
Servlet 生存周期 
请求分发 

Servlet 相关技术规范简介

部署描述符

部署描述符就是位于 WEB 应用程序的 /WEB-INF 目录下的 web.xml 的 XML 文件,是 WEB 应用程序不可分割的部分,管理着 WEB 应用程序的配置。部署描述符在应用程序开发人员,应用程序组装人员,应用程序部署人员之间传递 WEB 应用程序的元素和配置信息。
在 WEB 应用程序的部署描描述符中以下类型的配置和部署信息是所有的 servlet 容器必须支持的:
 ServletContext 初始化参数  
 Session 配置  
Servlet 声明  
Servlet 映射  
应用程序生存周期监听器  
 Filter 的定义和映射  
 MIME 类型的映射  
欢迎文件列表  
错误文件列表  

出现在部署描述符中的安全信息可以不被支持,除非这个 Servlet 容器是 J2EE 规范实现的一部分。
所有正确的 WEB 应用程序部署描述符 (Servlet2.3 规范 ) 必须包含下面的 DOCTYPE 声明:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web  
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">  


下面说明在部署描述符中是如何进行 Servlet 声明和映射的,这个 DTD 的全部内容可以在下面这个地址获得:
http://java.sun.com/dtd/web-app_2_3.dtd
在这个 DTD 中有关 Servlet 声明和映射和映射的部分如下:
<!--  
The servlet element contains the declarative data of a  
servlet. If a jsp-file is specified and the load-on-startup element  
is present, then the JSP should be precompiled and loaded.  
Used in: web-app  
-->  
<!ELEMENT servlet (icon?, servlet-name, display-name?, description?,  
(servlet-class|jsp-file), init-param*, load-on-startup?, runas?,  
security-role-ref*)>  
<!--  
The servlet-class element contains the fully qualified class name  
of the servlet.  
Used in: servlet  
-->  
<!ELEMENT servlet-class (#PCDATA)>  
<!--  
The servlet-mapping element defines a mapping between a servlet  
and a url pattern  
Used in: web-app  
-->  
<!ELEMENT servlet-mapping (servlet-name, url-pattern)>  
<!--  
The servlet-name element contains the canonical name of the  
servlet. Each servlet name is unique within the web application.  
Used in: filter-mapping, servlet, servlet-mapping  
-->  
<!ELEMENT servlet-name (#PCDATA)>  
根据以上 DTD ,一个典型的 Servlet 的声明的格式如下:  
<servlet>  
<servlet-name>catalog</servlet-name>  
<servlet-class>com.mycorp.CatalogServlet</servlet-class>  
<init-param>  
<param-name>catalog</param-name>  
<param-value>Spring</param-value>  
</init-param>  
</servlet>  
一个典型的Servlet映射如下:  
<servlet-mapping>  
<servlet-name>catalog</servlet-name>  
<url-pattern>/catalog/*</url-pattern>  
</servlet-mapping>  
   


通过上面的方法,我们就声明了一个名称为 catalog 的Servlet,它的实现类为com.mycorp.CatalogServlet,并且带有一个catalog参数,参数值为Spring,所有向/catalog/*的请求都被映射到名称为catalog的Servlet。

映射请求到 Servlet

接 收到一个请求后,WEB容器要确定转到哪一个WEB应用程序。被选择的应用程序的最长的上下文路径必须和请求的URL开始部分匹配。URL匹配的部分是映射到Servlet的上下文路径。
WEB 容器下一步必须按照下面的程序定位处理请求的Servlet。
用来映射到Servlet的路径是请求对象的URL减去上下文的路径。下面的URL路径映射规则按顺序执行,容器选择第一个成功的匹配并且不在进行下一个匹配:

        容器试着对请求的路径和Servlet的路径进行精确匹配,如果匹配成功则选择这个Servlet。 
        容器会循环的去试着匹配最长的路径前缀:把’/’当作路径分隔符,按照路径树逐级递减的完成,选择最长匹配的Servlet。 
        如果这个URL路径的最后有扩展名(比如.jsp),Servlet容器会试着匹配处理这个扩展名的Servlet。 
        如果前面的没有与前面三条规则相匹配的Servlet,容器会试着为资源请求提供适当的资源,如果有“默认”的Servlet定义给这个应用程序,那么这个Servlet会被使用。 
  
容器必须使用一个大小写敏感的匹配方式。 
在部署描述符中,用下面的语法定义映射: 
        一个以’/’开始并且以’/*’结束的字符串用来映射路径。 
        一个以’*.’为前缀的字符串用来映射扩展名。 
        一个只包含’/’的字符串指示着这个应用程序“默认”的Servlet,在这种情况下,servlet的路径是请求的URI减去上下文路径,并且这个路径是null。 
        所有其他的字符只用来精确匹配。 

如果容器内置JSP容器,那么*.jsp被映射到这个容器,并允许JSP页面在需要的时候被执行。这种映射叫做隐含映射。如果WEB应用程序中定义了*.jsp的映射,那么这个映射有比隐含映射高的优先级。
WEB 容器允许显式的声明隐含映射以获得优先级,例如,*.shtml的隐含映射可以在服务器上被映射为包含功能。
映射实例:
path pattern  
servlet  
/foo/bar/*  
servlet1  
/baz/*  
servlet2  
/catalog  
servlet3  
*.bop  
servlet4  

下面是实际请求映射的结果
incoming path  
servlet handling request  
/foo/bar/index.html  
servlet1  
/foo/bar/index.bop  
servlet1  
/baz  
servlet2  
/baz/index.html  
servlet2  
/catalog  
servlet3  
/catalog/index.html  
“default” servlet  
/catalog/racecar.bop  
servlet4  
/index.bop  
servlet4  

请注意/catalog/index.html 和/catalog/racecar.bop这两种情况,因为是精确匹配,所以并没有映射到处理/catalog的servlet。

Servlet 生存周期

在介绍 Servlet 的生存周期之前需要先介绍一下 javax.servlet.Servlet 接口。所有的 Servlet 必须实现或者间接实现这个借口,我们通常可以通过继承 javax.servlet.GenericServlet 或者 javax.servlet.http.HttpServlet. 类来实现这个接口。
这个接口中定义了下面 5 种方法:
public void init(ServletConfig config);  
public ServletConfig getServletConfig();  
public void service(ServletRequest req, ServletResponse res);  
public String getServletInfo();  
public void destroy() ;  

init() 方法
init 方法在容器器装入 Servlet 时执行, Servlet 容器在实例化后只调用一次 init 方法, init 方法必须在 servlet 接收到任何请求之前完成。
这个方法通常用来进行一些资源的管理和初始化,如从配置文件读取配置数据,读取初始化参数,初始化缓冲迟等一次性的操作。
getServletConfig() 方法
GetServletConfig 方法返回一个 ServletConfig 对象,该对象用来返回这个 Servlet 的初始化信息和启动参数。返回的是传递到 init 方法 ServletConfig 。
Service() 方法
Service 方法是应用程序逻辑的进入点,是 servlet 方法的核心, WEB 容器调用这个方法来响应进入的请求,只有 servlet 成功被 init() 方法初始化后, Service 方法才会被调用。
getServletInfo() 方法
这个方法返回一个字符串对象,提供有关 servlet 的信息,如作者、版本等。
destroy() 方法
destroy 方法在容器移除 Servlet 时执行,同样只执行一次。这个方法会在所有的线程的 service() 方法执行完成或者超时后执行,调用这个方法后,容器不会再调用这个 servlet 的方法,也就是说容器不再把请求发送给这个 Servlet 。       这个方法给 servlet 释放占用的资源的机会,通常用来执行一些清理任务。

这个接口定义了初始化一个 servlet, 服务请求和从容器中移除 servlet 的方法。他们按照下面的顺序执行:
1.         servlet 被实例化后,用 init 方法进行初始化  
2.         客户端的任何请求都调用 service 方法  
3.         servlet 被移除服务,调用 destroy 方法销毁  

servlet 的生存周期如下图:

请求分发

请求分发可以让一个Servlet把请求分配到另外一个资源,RequestDispatcher接口提供了实现他的机制。可以通过下面两种方式从ServletContext中获得一个实现了RequestDispatcher接口的对象:
• getRequestDispatcher 
• getNamedDispatcher 
getRequestDispatcher方法接受一个指向目标资源的URL路径 
RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”); 


getNamedDispatcher方法接受一个Servlet名称参数,这个名称是在部署描述符中<servlet-name>元素指定的那个名称。
RequestDispatcher rd = getServletContext().getNamedDispatcher (“catalog”);


RequestDispatcher接口有两个方法,允许你在调用的servlet完成初步处理后把请求响应分配到另外一个资源,
forward()方法:
public void forward(ServletRequest request, ServletReponse reponse) throws SwerletException,IOException  

forward方法上让你把请求转发到另外的Servlet或者jsp或者html等资源,由这个资源接下来负责响应。如:
 
RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”);  
rd. forward(request,response);  


include()方法:
public void include (ServletRequest request, ServletReponse reponse) throws SwerletException,IOException  

include方法让你的Servlet响应中包含另外一个资源生成内容 
RequestDispatcher rd = getServletContext().getRequestDispatcher(“/catalog”); 
rd. include(request,response); 


结合WebWork的具体分析

WebWork是由OpenSymphony组织开发实现MVC模式的J2EE Web框架。在介绍完servlet规范的相关内容后,我们看看WebWork是如何注入到一个Servlet中的,假设我们有一个上下文环境为“/WebWorkdDemo”的WEB应用。
部署描述符

在部署描述符中,我们需要进行如下配置:
<servlet>  
<servlet-name>webwork</servlet-name>  
<servlet-class>com.opensymphony.webwork.dispatcher.ServletDispatcher</servlet-class>  
</servlet>  
……  
<servlet-mapping>  
<servlet-name>webwork</servlet-name>  
<url-pattern>*.action</url-pattern>  
</servlet-mapping>  

我们声明了一个名为webwork的Servlet和*.action到这个Servlet的映射,这个Servlet就是webwork中的controller,担任MVC框架中非常重要的控制器角色。
映射请求到Servlet

在XWork的配置文件xwork.xml中有如下片段:
<action name="demo" class=" webworkapp.DemoAction">  
    <result name="success" type="dispatcher">  
       <param name="location">/demo.jsp</param>  
    </result>  
</action>  

这样我们由http://localhost:8080/WebWorkDemo/demo.action这个URL向服务器发出请求时,WEB容器首先确定转到哪一个WEB应用程序,容器将请求URL和上下文环境进行匹配后知道将转到/WebWorkdDemo这个WEB应用。
接下来容器会在/WebWorkdDemo这个应用的部署描述符中进行查找处理这个请求的servlet,根据后缀*.action找到名称为webwork这个Servlet,这样根据部署描述符,这个请求被映射到webwork中的controller组件com.opensymphony.webwork.dispatcher.ServletDispatcher来处理。这个担任控制器组件的Servlet在他的service()方法中在根据请求的路径解析出对应的action来进行处理。
通过上面的的处理,实现了将web请求转到了webwork中的控制器ServletDispatcher。不止是webwork,实现MVC的web框架都需要进行类似的处理来将web请求转入到自己的controller.以便进行进一步的处理。
Servlet生存周期

ServletDispatcher这个Servlet的存周期可以如下:
1)      在服务器启动的时候,容器首先实例化ServletDispatcher  
2)        实例化完成后,将调用init()方法,在init方法中执行了以下操作:  
         初始化Velocity引擎  
         检查是否支持配置文件重新载入功能。如果支持,每个request请求都将重新装载xwork.xml配置文件,在开发时非常方便。  
         设置一些文件上传的信息,比如:上传临时目录,上传的最大字节等。  
3)      每次请求都调用service()方法,在service方法中执行了以下方法  
         通过request请求取得action的命名空间  
         根据servlet请求的Path,解析出要调用该请求的Action的名字(actionName)  
         创建Action上下文(extraContext),遍历HttpServletRequest、HttpSession、ServletContext 中的数据,并将其复制到Webwork的Map实现中,至此之后,所有数据操作均在此Map结构中进行,从而将内部结构与Servlet API相分离。  
         以上述信息作为参数,调用ActionProxyFactory创建对应的ActionProxy实例。ActionProxyFactory 将根据Xwork 配置文件(xwork.xml)中的设定,创建ActionProxy实例,ActionProxy中包含了Action的配置信息(包括Action名称,对应实现类等等)。  
         执行proxy的execute()方法  
4)      容器移除Servlet 时执行destroy(),在ServletDispatcher这个Servlet中并没有重写destroy方法,在移除Servlet时,将什么也不做。  
请求分发  


WebWork提供了多种活灵活视图展现方式,例如还是我们上面在xwork.xml中的配置:
<action name="demo" class=" webworkapp.DemoAction">  
    <result name="success" type="dispatcher">  
       <param name="location">/demo.jsp</param>  
    </result>  
</action>  

根据以上配置当DemoAction的返回值为"success"时的处理类型为"dispatcher",当result的type为"dispatcher"时,通过javax.servlet.RequestDispatcher的forward()或include()方法将处理结果和表现层融合后展现给用户
我们可以看看WebWork提供的dispatcher类型Result Type的实现类com.opensymphony .webwork.dispatcher.ServletDispatcherResult中的代码片断:
 
HttpServletRequest request = ServletActionContext.getRequest();  
HttpServletResponse response = ServletActionContext.getResponse();  
RequestDispatcher dispatcher = request.getRequestDispatcher(finalLocation);  
  
if (dispatcher == null) {  
  response.sendError(404, "result '" + finalLocation + "' not found");    
  return;  
}  
  
if (!response.isCommitted() && (request.getAttribute("javax.servlet.include.servlet_path") == null)) {  
  request.setAttribute("webwork.view_uri", finalLocation);  
  request.setAttribute("webwork.request_uri", request.getRequestURI());  
   
  dispatcher.forward(request, response);  
} else {  
  dispatcher.include(request, response);  
}  

ServletDispatcherResult类的从ServletActionContex中得到HttpServletRequest和HttpServletResponse,然后调用request.getRequestDispatcher(finalLocation)方法得到一个RequestDispatcher实例,如果返回的是null,则输出404页面未找到的错误,否则将调用dispatcher.forward(request, response)或者dispatcher.include(request, response)进行请求分发,将处理结果和表现层融合后展现给用户。

结束语

       通过以上的介绍,我们对web框架是如何注入到servlet中有了简单的了解,如果想更深入的研究,可以阅读Servlet规范以及一些成熟框架的源码。


爱小板凳小编整理:电影天堂

文章评论

程序员都该阅读的书
程序员都该阅读的书
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
程序员必看的十大电影
程序员必看的十大电影
漫画:程序员的工作
漫画:程序员的工作
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
 程序员的样子
程序员的样子
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
每天工作4小时的程序员
每天工作4小时的程序员
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
老程序员的下场
老程序员的下场
10个调试和排错的小建议
10个调试和排错的小建议
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员和编码员之间的区别
程序员和编码员之间的区别
总结2014中国互联网十大段子
总结2014中国互联网十大段子
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
我是如何打败拖延症的
我是如何打败拖延症的
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
我的丈夫是个程序员
我的丈夫是个程序员
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
鲜为人知的编程真相
鲜为人知的编程真相
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
如何成为一名黑客
如何成为一名黑客
一个程序员的时间管理
一个程序员的时间管理
中美印日四国程序员比较
中美印日四国程序员比较
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
旅行,写作,编程
旅行,写作,编程
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员的鄙视链
程序员的鄙视链
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
Java程序员必看电影
Java程序员必看电影
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
代码女神横空出世
代码女神横空出世
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
为什么程序员都是夜猫子
为什么程序员都是夜猫子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有