Servlet
Servlet是运行在称为servlet/JSP容器或Web容器的特殊Web服务器中的Java类。在编写servlet程序时,使用以下两个包中的类和接口:
javax.servlet其中包含用于servlet编程的基本类型。
javax.servlet.http其中包含扩展自javax.servlet包中的相应类型的接口和类。
这两个包中最重要的类型是javax.servlet.Servlet接口,所有的servlet都要实现此接口(或是从实现了此接口的类继承)。
使用Servlet接口
javax.servlet.Servlet接口定义了3个生命周期方法:init、service和destroy。servlet容器会在servlet的生存期间调用这些方法。(注:直接实现Servlet接口的另一个方法是扩展javax.servlet.GenericServlet类或是javax.servlet.http.HtpServlet类)。
1、init方法
第一次请求servlet时,会调用init方法。它仅被调用一次,以通知servlet其已经被装入服务中,可以重写此方法,为其提供只需执行一次的初始化代码,比如初始化某些值、装入数据库驱动程序等。
此方法原型是:
public void init(ServletConfig config) throws ServletException
2、service方法
每次调用servlet时,servlet容器就会调用相应servlet的service方法。对于每个HTTP请求,servlet容器会创建一个请求对象(实现javax.servlet.ServletRequest或 javax.servlet.http.HttpServletRequest)和一个响应对象(实现javax.servlet.ServletResponse或javax.servlet.http.HttpServletResponse),并将它们传给servlet的service方法。请求对象封装了从HTTP请求里解析出来的、对servlet有用的信息。比如请求的URL、请求头、cookie及请求的参数等。当请求处理完毕以后,servlet使用响应对象将响应发送回Web客户端。
此方法原型是:
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException
3、destroy方法
在删除一个servlet实例前,servlet容器会调用其destroy方法,这种情况一般发生在servlet容器需要被关闭或释放内存时。可以使用此方法来清除占用的资源(如内存、文件句柄及线程等)或确保某些持续状态与内存中的servlet的当前状态一致等。
此方法原型是:
public void destory()
部署描述符
部署描述符是一个XML文件,所以可以使用文本编辑器来编辑它。
符合servlet 2.3规范的应用部署描述符是以如下形式开始的:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
1、Servlet映射
在Web浏览器中访问servlet之前,需要先在部署描述符里将其映射到一个路径
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>myproject.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/myservlet</url-pattern>
</servlet-mapping>
</web-app>
使用URL来访问该servlet:
http://localhost:8080/MyProject/myservlet
2、定义和获取上下文参数
在部署描述符节(web.xml 文件)里,可以为其所描述的应用定义设置。可以使用此方法来定义初始的上下文参数(这些参数对于应用中所有的servlet/JSP页面都可用)、注册servlet、注册监听器、把资源映射到URL等。
使用上下文参数,不必再将某些信息硬编码在servlet的代码里。这样,如果需要改变这些信息,就无需重新编译servlet。
可以使用content-param元素指定可用于应用中所有servlet/JSP的上下文参数名/值对。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<content-param>
<param-name>userName</param-name>
<param-name>budi</param-name>
</content-param>
<content-param>
<param-name>password</param-name>
<param-name>secret</param-name>
</content-param>
</web-app>
为了获取部署描述符里的上下文参数,首先需要通过ServletConfig对象的getServletContext获得ServletContext对象,然后再调用ServletContext接口的两个方法:
getInitParameterNames方法,此方法无需传入参数,并且返回一个包含所有上下文参数名的枚举类型(java.util.Enumeration)。
getInitParameter方法,此方法需要传入一个String类型的参数,包含所要获取的上下文参数的名称,并且返回包含参数值的String。
监听应用事件
javax.servlet包中提供了两个监听器接口,利用它们的支持,可在ServletContext对象的状态发生改变时得到通知,它们是ServletContextListener接口和ServletContextAttributeListener接口。
使用ServletContextListener接口可监听其言观其行ServletContext对象的生命周期事件,它有以下两个方法:
contextInitialized方法。当Web应用准备好提供服务时。此方法会被调用,Servlet容器在自身的初始化工作完成以后会自动调用它,您可以在这里编写需要在应用初始化时执行的代码,比如加载JDBC(Java数据库连接)驱动程序、创建数据库Connection对象或是给全局变理赋初始化值等。
contextDestroyed方法。当servlet上下文将要关掉的时候会调用此方法,可以在这里编写需要在应用关闭时执行的代码,比如关闭数据库连接或是写入到日志等。
这两个方法的原型是:
public void contextInitialized(ServletContextEvent sce)
public void contextDestroyed(ServletContextEvent sce)
为了使监听器实现类(如:ApplicationListener实现了ServletContextListener接口)工作,需要在部署描述符里注册。
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<listener>
<listener-class>ApplicationListener</listener-class>
</listener>
</web-app>
每次应用启动时(当一个servlet或JSP页面第一次被调用时)调用contextInitialized方法,每次应用停止服务时调用contextDestroyed方法。
JSP
Sun公司在1996年引入servlet,它很快就流行起来,因为它比公共网关接口(Common Geatway Interface, CGI,第一种用来编写动态Web应用的技术)要快。但Sun意识到编写servlet太麻烦,特别是当需要发送一个长的HTM页面时。
Sun对此问题的解决方案是JSP。按照Sun的网站所说的:"JSP技术是servlet技术的扩展,用来支持编写HTML和XML页面"。用JSP可以容易地混合固定的、静态的模板数据与动态的内容。用它开发比只用servlet开发要快,因为它允许直接将HTML标记放在Java代码里,而且无需编译。当JSP页面第一次被调用时,servlet/JSP容器自动编译它。
JSP并设让servlet变得过时。事实上,在很多Web应用里都同时有JSP页面和servlet。而且,不要忘记JSP是servlet的扩展。
在JSP容器里有一个特殊的servlet,叫作页面编译器,servlet容器被配置成将带有匹配*.jsp文件扩展名的URL和HTTP请求转发给此页面编译器。就是这个页面编译器将servlet容器变成了JSP容器。当一个JSP页面第一次被调用的时候,页面编译器解析此JSP页面并试图将其编译成一个servlet类,如查编译成功,则此servlet类被载入内存。
在后续的调用里,对应该JSP页面的servlet已经在内存里,但是JSP页面可能已被更新过,所以,页面编译器servlet会比较JSP servlet和JSP页面的时间戳,如果JSP页面比较新,则需重新编译。通过此过程,一旦部署完成,JSP页面只需经历一次费时的编译过程。
JSP和JavaBeans
为了加快JSP应用的开发过程,需要将界面设计人同的工作与Java程序设计人员的工作分开,使用JavaBeans可以做到这一点。在这种方式下,Java程序设计人员负责把应用所需的所有功能都写进JavaBeans里。与此同时,界面设计人员可进行页面设计,当JavaBeans完成以后,页面设计人员使用与HTML相似的标记来从JSP页面中调用JavaBeans的方法或获取其属性。
事实上,在JSP应用的开发过程中使用bean是一种很常用的方法,这种方法流行的原因在于它的可读性。
其实,JavaBeans就是一个Java类,您无需扩展特定的基类或实现接口来生成一个JavaBeans。但为了成为一个bean,Java类必须遵守JavaBeans规范指定的特定规则。比如,bean类必须有一个无参数的构造函数。
1、设置和获取JavaBean的属性
bean可以有一个公有方法来设置属性的值,此方法称为setter或mutator,它不返回任何值,名称以set打头,所跟属性名。此方法的原型为:
public void setPropertyName(PropertyType value);
比如,属性operand的setter方法名必须是setOperand。
同样作为一种选择,bean可以有一个公有方法来获取属性值。此方法称为getter或accessor,它的返回值类型是属性的类型,名称以get开头,后跟属性名。此方法的原型为:
public propertyType getPropertyName();
比如,属性operand的getter方法名必须是getOperand。
setter和getter方法统称为访问方法(acces method)。在JSP中,jsp:getProperty和jsp:setProperty动作元素就分别用来调用getter和setter方法。
2、从JSP页面中调用bean
在可以从JSP页面中使用一个bean之前,必须先使用jsp:useBean动作元素使其可用。该元素有用于设置bean属性值的特性。
jsp:useBean元素的语法:
下面是可用的jsp:useBean动作元素的5个特性(attribute):
- id 特性定义了bean的惟一标识符,此标识符在整个页面范围内都可用,可以将其看作是bean的对象引用。id 特性的值必须满足当前所用脚本语言的变量命名规则。
- class特性指明了JavaBean类的完全限定的名称,但如果bean所在的包已经使用page指令导入过,则无需完全限定的名称。
- type特性,如果此特性出现在jsp:useBean元素中,则它指明JavaBean类的类型。bean的类型可以是类的类型,也可以是其超类的类型,或是bean类实现的接口。一般来说,不常用这个特性,而是用class特性。
- scope特性定义了bean的可访问性和生命期限。此特性的值可以是page、request、session及application中的一个。其默认值是page。scope特性的值控制bean能够存在多长时间,具体如下所述:
- page bean 仅在当前页面中使用jsp:useBean动作元素定义它的位置后可用。每次请求这个页面都生成bean的一个新实例,并在超出JSP页面范围以后自动销毁此实例,比如在控制转移到其他页面时,如果您使用jsp:include或jsp:forward标记,那么在包含的页面或是转发的页面里不能访问这个实例。
- request bean的可访问性扩展到jsp:include或jsp:forward动作元素所引用的包含或转发的页面,包含及转发的页面无需 jsp:useBean动作元素即可使用bean。比如,可以在一个包含或是转发的页面里使用jsp:getProperty及jsp:setProperty动作元素访问原页面中实例化的bean。
- session bean的作用域为用户的会话(session)对象,只要用户会话对象还存在,bean的实例就一直存在。换句话说,bean的可访问性扩展到了其他页面。但如果实例化bean的页面没有加入到JSP容器的会话管理,那么就不能使用这个作用域。因为bean的实例被放入到会话对象中。
- application 在该作用域下,bean的存在时限与JSP容器一样长,在应用的任何资源中都可以访问bean的实例。
- beanName特性是指实例化bean时java.beans.Beans类的实例化方法所需的bean名称。
Taglib 定制标记
使用JavaBeans可以将JSP页面的表示部分从Java代码里分离出来,但只有3个动作元素java:useBean、jsp:getProperty和jsp:setProperty可用来访问bean。所以在某此情况下,我们还是要求助于在JSP页面中使用Java代码。认识到JavaBeans并不是分离表示与业务规则实现的良好方案,JSP1.1定义了一种新功能:可用来执行自定义动作的定制标记。
1、定制标记和JavaBeans的比较
与JavaBeans相比,定制标记有一些好处:
- 定制标记可访问JSP页面里所有对象。
- 定制标记可通过特性来定制。
但是,定制标记也有缺点,它的建立和使用要比JavaBeans稍难一点。有时,由于其可得用性,JavaBeans更适合一些。
2、开发和使用定制标库
开发定制标记库包含3个主要步骤:
- 编写标记处理程序。标记处理程序是一个包含了定制标记的逻辑Java类。每当在JSP页面里遇到一个定制标记时,JSP容器就找到与此定制标记对应的标记处理程序并执行它。
- 编写标记库描述符(tag library descriptor, TLD)。TLD是一个定义了标记库及其标记的XML文件。
- 编辑Web应用的部署描述符(web.xml文件)。要使用定制标记,需要在部署描述符里指定一个taglib元素。
- 编写包含taglib指令的JSP页面。要在JSP页面使用定制标记,需要使用taglib指令来标识TLD和标记。
<1>、编写标记处理程序
通常使用javax.servlet.jsp.tagext包中的接口和类来编写标记处理程序。
标记处理程序必须实现Tag、IterationTag或BodyTag接口,或者是从实现其中某个接口的类继承。

Tag 接口
Tag接口包含下列方法:doStartTag、doEndTag、getParent、setParent、setPageContext及release。JSP容器与Tag接口交互的过程如下:
(1)、JSP容器实例化标记处理程序或从池中取出一个标记处理程序的实例,并且调用其setPageContext方法,传入一个表示定制标记所在JSP页面的PageContext对象。
(2)、JSP容器调用setParent方法,传入一个表示最近的、包含当前标记处理程序的标记对象。如果当前标记不在任何标记内,此对象为null。
(3)、JSP容器设置定制标记的所有特性(如果有的话)。标记特性的处理方法与JavaBean属性的处理方法相似,也是通过getter和setter方法处理。
(4)、JSP容器调用doStartTag方法。此方法可返回Tag.SKIP_BODY或Tag.EVAL_BODY_INCLUDE。如果返回Tag.SKIP_BODY,JSP容器就不再处理标记的主体内容;如果返回Tag.EVAL_BODY_INCLUDE,则还会处理标记的主体内容(如果有的话)。
(5)、不管doStartTag方法返回什么,JSP容器都会接着调用doEndTag方法。此方法可返回Tag.SKIP_PAGE或tag.EVAL_PAGE,如果返回Tag.SKIP_PAGE,JSP容器或不再处理此JSP页面的剩余部分;如果返回Tag.EVAL_PAGE,则继续处理页面的剩余部分。
(6)、JSP容器调用release方法,可以在此方法的实现中编写清除代码。
(7)、JSP容器将标记处理程序的实例放回池中以备后面使用。
IterationTag 接口
IterationTag接口扩展Tag接口,它额外还有一个doAfterBody方法和一个静态的final整形变量EVAL_BODY_AGAIN。JSP容器调用标记处理程序的doAfterBody方法实现,并且在调用doStartTag方法以后实现IterationTag。doAfterBody方法可返回Tag.SKIP_BODY或IterationTag.EVAL_BODY_AGAIN,如果返回IterationTag.EVAL_BODY_AGAIN,JSP容器还会再次调用doAfterBody方法;如果返回Tag.SKIP_BODY,则会结束标记和处理,并且JSP容器将传而调用doEndTag方法。
BodyTag 接口
BodyTag接口实现Tag接口或IterationTag接口不能访问定制标记的主体内容。如果需要操作标记的主体内容,必须实现BodyTag接口。BodyTag接口扩展了IterationTag接口,并且添加了doInitBody和setBodyContent两个方法,以及EVAL_BODY_BUFFERED和EVAL_BODY_TAG两个静态的final整形变量。JSP容器在调用doStartTag方法以后会调用setBodyContent方法。而doInitBody方法则在调用setBodyContent方法以后被调用。
实现BodyTag接口的标记处理程序可在doStartTag方法中返回SKIP_BODY、EVAL_BODY_INCLUDE或EVAL_BODY_BUFFERED。如果返回EVAL_BODY_BUFFERED,则会生成一个表示定制标记主体内容的BodyContent对象。
doInitBody方法可用来为对标记主体的处理进行准备。通常,此方法在setBodyContent方法后调用。但如果定制标记没有主体,或是doStartTag方法返回了SKIP_BODY或EVAL_BODY_INCLUDE,则doInitBody方法不会被调用。
BodyContent类
BodyCOntent类是一个扩展javax.servlet.jsp.JspWriter类的抽象类。它表示定制标记的主体内容(如果有的话)。可以从BodyTag接口的setBodyContent方法中获得主体内容。
TagSupport和BodyTagSupport类
javax.servlet.jsp.tagext包还提供了一些支持类,您可以扩展这些类来创建标记处理程序。扩展这些类而不直接实现接口的好处是可以仅为需要重写的方法提供实现,这样就只需要更少的代码。TagSupport类实现了IterationTag接口,BodyTagSupport类则实现了BodyTag接口。
<2>、编写标记库描述符(TLD)
TLD是一个描述标记库的XML文档。对它的验证是基于一个DTD文件,是前DTD的最新版本是1.2。TLD必须通过以下报头作为开始。
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Jsp Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
TLD文件根元素是<taglib>。taglib可包含的子元素如下:
<!ELEMENT taglib (tlib-version, jsp-version, short-name, uri?,display-name?, small-icon?, large-icon?, description?, validator?, listener*, tag+) >
taglib必须包含下列元素:
- tlib-version元素:指明标记库实现的版本。 jsp-version元素:定义了标记库可在其上工作的JSP版本。
- short-name元素:定义了一个惟一的标记库名称。
- tag元素:说明标记库里的一个定制标记。
<3>、编写标记库描述符(TLD)
要在JSP页面中使用定制标记,需要熟悉JSP里的taglib指令。Taglib指令的语法如下:
<%@ taglib uri="tagLibraryURL" prefix="tagPrefix" %>
uri特性指明明了惟一确定与此前缀相关联的TLD文件的绝对URL或相对URI,而prefix特性则定义一个用来区别该定制标记库的前缀字符串。
有了taglib指令以后,可以通过格式使用不带主体内容的定制标记:<prefix:tagName />,或都用以下形式使用带有主体内容的定制标记:<prefix:tagName>Body</prefix:tagName>(注:tagName代表定制标记名称)。
下面的例子是一个叫作myTag的定制标记。它的前缀是m,有两个特性:特性numbe的值是12,特性power的值是13。
<m:myTag number="12" power="13" />

没有评论:
发表评论