JAVA WEB学习笔记

这个是我看JavaWeb相关书籍和视频的学习记录,如有侵权,请联系我。

JSP技术

概念

	JSP(JavaServer Pages)是一种动态页面技术,它的主要目的是将表示逻辑从Servlet中分离出来,实现表示逻辑。

JSP语法

JSP页面元素简要说明标签语法
声明声明变量与定义方法<%! Java声明%>
小脚本执行业务逻辑的Java代码<% Java代码%>
表达式用于在JSP页面输出表达式的值<%= 表达式%>
指令指定转换时向容器发出的指令<%@指令%>
动作向容器提供请求时的指令<jsp:动作名/>
EL表达式JSP2.0引进的表达式语言${applicationScope.email}
注释用于文档注释<%–任何文本–%>
模板文本HTML标签和文本同HTML规则

JSP生命周期

阶段名称说明
页面转换对页面解析并创建一个包含对应Servlet的Java源文件
页面编译对Java源文件编译
加载和创建实例将编译后的类加载到容器中,并创建一个Servlet实例
调用jspInit()调用其他方法之前调用该方法初始化
调用_jspService()对每个请求调用一次该方法
调用jspDestroy()当Servlet容器决定停止Servlet服务时调用该方法
理解页面转换过程
  • 所有的JSP声明都转换成页面实现类的成员,它们被原样拷贝。例如,声明的变量转换成实例变量,声明的方法转换成实例方法。
  • 所有的JSP小脚本都转换成页面实现类的_jspService()的一部分,它们也被原样拷贝。小脚本中声明的变量转换成_jspService()的局部变量,小脚本中的语句转换成_jspService()中的语句。
  • 所有的JSP表达式都转换成为_jspService()的一部分,表达式的值使用语句out.print()输出。
  • 有些指令在转换阶段产生Java代码,例如,page指令的import属性转换成页面实现类的import语句。
  • 所有的JSP动作都通过调用针对厂商的类来替换。
  • 所有表达式语言EL通过计算后使用out.write()语句输入
  • 所有模板文本都成为_jspService()的一部分,模板内容使用out.write()语句输出。
  • 所有的JSP注释都被忽略。
理解转换单元

      在JSP页面中可以使用<%@ include… %>指令把另一个文件(如JSP页面、HTML页面等)的内容包含到当前页面中。容器在为当前JSP页面产生Java代码时,它也把被包含的文件的内容插入到产生的页面实现类中。这些被转换成单个页面实现类的页面集合称为转换单元(translation unit)。有些JSP标签影响整个转换单元而不只是它们所在的页面。关于转换单元,要记住下面的要点:

  • page指令影响整个转换单元。有些指令通知容器关于页面的总体性质,例如,page指令的contentType属性指定响应的内容类型,session属性指定页面是否参加HTTP会话。
  • 在一个转换单元中一个变量不能多次声明。例如,如果一个变量已经在主页面中声明,它就不能在被包含的页面中声明。
  • 在一个转换单元中不能使用<jsp:useBean>动作对一个bean声明两次。
JSP脚本元素

      由于声明,小脚本和表达式允许在页面中编写脚本语言代码,所以这些元素统称为脚本元素。

  • JSP表达式并不总是输出到页面中,它们也可以传递给JSP动作的属性。
    <%! String pageURL=“copyright.jsp”;%>
    <jsp:include page="<%=pageURL%>"/>
    以这种方式向动作传递一个属性值使用的表达式称为请求时属性表达式。
  • 注意,请求时属性表达式不能用在指令的属性中,因为指令具有转换时的语义,即容器仅在页面转换期间使用指令。
JSP隐含变量

      在JSP页面的转换阶段,Web容器在页面实现类的_jspService()方法中声明并初始化一些变量,可以在JSP页面小脚本或表达式中直接使用这些变量。

JSP页面中可使用的隐含变量

隐含变量类或接口说明
applicationjavax.servlet.ServletContext接口引用Web应用程序上下文
sessionjavax.servlet.http.HttpSession接口引用用户会话
requestjavax.servlet.http.HttpServletRequest接口引用页面的当前请求对象
responsejavax.servlet.http.HttpServletResponse接口用来向客户发送一个响应
outjavax.servlet.jsp.JspWriter类引用页面输出流
pagejava.lang.Object类引用页面的Servlet实例
pageContextjavax.servlet.jsp.PageContext类引用页面上下文
configjavax.servlet.ServletConfig接口引用Servlet的配置对象
exceptionjava.lang.Throwable类用来处理错误
pageContext变量

PageContext类是一个抽象类,容器厂商提供了一个具体子类(如JspContext),它有下面三个作用。

  1. 存储隐含对象的引用。pageContext对象是作为管理所有在JSP页面中使用的其他对象的一个地方,包括用户定义的和隐含的对象,并且它提供了一个访问方法来检索它们。如果查看JSP页面生成的Servlet代码,会看到session、application、config与out这些隐含变量是调用pageContext对象的相应方法得到的。
  2. 提供了在不同作用域内返回或设置属性的方便的方法。
  3. 提供了forward()和include()实现将请求转发到另一个资源和将一个资源的输出包含到当前页面中的功能。
    RequestDispatcher rd = request.getRequestDispatcher("other.jsp");
    rd.forward(request,response);
    pageContext.forward("other.jsp");
page指令属性

page指令的常用属性

属性名说明默认值
import导入在JSP页面中使用的Java类和接口,其间用逗号分隔java.lang.*;
javax.servlet.*;
javax.servlet.jsp.*;
javax.servlet.http.*;
contentType指定输出的内容类型和字符集text/html;
charset=ISO-8859-1
pageEncoding指定JSP文件的字符编码ISO-8859-1
session用布尔值指定JSP页面是否参加HTTP会话true
errorPage用相对URL指定另一个JSP页面用来处理当前页面的错误null
isErrorPage用一个布尔值指定当前JSP页面是否用来处理错误false
language指定容器支持的脚本语言java
extends任何合法地实现了java.servlet.jsp.JspPage接口的Java类与实现有关
buffer指定输出缓冲区的大小与实现有关
autoFlush指定是否当缓冲区满时自动刷新true
info关于JSP页面的任何文本信息与实现有关
isThreadSafe指定页面是否同时为多个请求服务true
isELIgnored指定是否在此转换单元中对EL表达式求值若web.xml采用Servlet2.4格式,默认值为true
<%=getServletInfo()%>
JSP组件包含
静态包含:include指令

      静态包含是在JSP页面转换阶段将另一个文件的内容包含到当前JSP页面中。使用include指令。

<%@ include file="relativeURL"%>

file是include指令唯一的属性,它是指被包含的文件。文件使用相对路径指定,相对路径或者以斜杠(/)开头,是相对于Web应用程序文档根目录的路径,或者不以斜杠开头,它是相对于当前JSP文件的路径。

动态包含:include动作

      动态包含是通过JSP标准动作jsp:include实现的。动态包含是在请求时将另一个页面的输出包含到主页面的输出中。

<jsp:include page="relativeURL" flush="true|false"/> <%--flush默认值是false--%>
<%request.getRequestDispatcher("other.jsp").include(request,response);%>
<%pageContext.include("other.jsp");%>
  • 传递参数
<jsp:include page="required.jsp">
    <jsp:param name="required" value="required"/>
    <jsp:param name="name2" value="<%=someExpr2%>>"/>
</jsp:include>

键值对保存在request对象中并只能由被包含的组件使用。这些参数的作用域是被包含的页面,在被包含的组件完成处理后,容器将从request对象中清除这些参数,这里的讨论也适用于forward动作。

forward动作

使用forward动作把请求转发到其他组件,然后由转发到的组件把响应发送给客户。

  • forward动作与include动作的不同之处在于,当转发到的页面处理完输出后,并不将控制转回主页面。使用forward动作,主页面也不能包含任何输出。
<%
request.getRequestDispatcher("other.jsp").forward(request,response);
pageContext.forward("other.jsp");
%>
<jsp:forward page="other.jsp">
JavaBeans
  • 使用JavaBeans的优点是:
    • 在JSP页面中使用JavaBeans可使代码更简洁。
    • JavaBeans有助于增强代码的可重用性。
    • 它们是Java语言对象,可以充分利用该语言面向对象的特征。
<jsp:useBean id="beanName"
scope="page|request|session|application"
{class="package.class"|type="package.class"|class="package.class type="package.class"}
{/>|>其他元素</jsp:useBean>}
  • type属性在找不到bean实例时会产生Instantiation异常。
  • 有时希望声明的类型是实际类型的超类或实现某个接口的类型,此时应使用type属性指定超类型。
<jsp:setProperty name="beanName"
{property="propertyName" value="{string|<%=expression%>}"|
property="propertyName"[param="paramName"]|
property="*"
}/>
  • 如果不设置值,则请求参数名必须与bean的属性名匹配。
  • 如果一个属性在请求中没有匹配的参数,属性值将保持不变。
  • 如果请求参数有多个值,则只使用第一个值。
<jsp:getProperty name="beanName" property="propertyName"/>
错误处理
声明式错误处理

在web.xml文件中配置错误页面需要使用元素,它有三个子元素:

  • <error-code>:指定一个HTTP错误代码,如404。
  • <exception-type>:指定一种Java异常类型(使用完全限定名)。
  • <location>:指定要被显示的资源位置。该元素必须以“/”开头。
<error-page>
	<error-code>404</error-code>
	<location>/errors/notFoundError.html</location>
</error-page>
<error-page>
	<exception-type>java.lang.Throwable</exception-type>
	<location>/errors/errorPage.html</location>
</error-page>

注意:在<error-page>元素中,<error-code>和<exception-type>不能同时使用。<location>元素的值必须以斜杠(/)开头,它是相对于Web应用的上下文根目录。另外,如果在JSP页面中使用page指令的errorPage属性指定了错误处理页面,则errorPage属性指定的页面优先。

使用Servlet和JSP页面处理错误
<error-page>
    <error-code>403</error-code>
    <location>/MyErrorHandlerServlet.do</location>
</error-page>
<error-page>
    <exception-type>java.sql.SQLException</exception-type>
    <location>/errorpages/sqlError.jsp</location>
</error-page>

为了在异常处理的Servlet或JSP页面中分析异常原因并产生详细的响应信息,Web容器在将控制转发到错误页面前在请求对象(request)中定义了若干属性。

属性类型描述
javax.servlet.error.status_codejava.lang.Integer该属性包含了错误的状态码值
javax.servlet.error.exception_typejava.lang.Class该属性包含未捕获的异常的Class对象
javax.servlet.error.messagejava.lang.String该属性包含在sendError()方法中指定的消息,或包含在未捕获的异常对象中的消息
javax.servlet.error.exceptionjava.lang.Throwable该属性包含未捕获的异常对象
javax.servlet.error.request_urijava.lang.String该属性包含当前请求的URI
javax.servlet.error.servlet_namejava.lang.String该属性包含引起错误的Servlet名
编程式错误处理

处理Servlet中产生的异常最简单的方法是将代码包含在try-catch块中,在异常发生时通过catch块将错误消息发送给浏览器。

public void sendError(int sc)
public void sendError(int sc,String msg)

会话与文件管理

协议记住客户及其请求的能力称为状态,按照这个观点,协议分为两种类型:有状态的和无状态的。
会话是客户与服务器之间的不间断的请求响应序列。

表达式语言(Expression Language,EL)
  • 表达式语言是JSP2.0新增加的特性,它是一种可以在JSP页面中使用的数据访问语言。${expression}。
  • 在EL中不能定义变量,也不能使用脚本中声明的变量,但它可以访问请求参数、作用域变量、JavaBeans以及EL隐含变量等。
  • 在EL表达式中还可以使用“e”在浮点数中表示幂运算${1e4*1}的值为10000.0。
关系运算符
==eq
!=ne
<lt
>gt
<=le
>=ge
  • ${empty expression}它判断expression的值是否为null、空字符串、空数组、空Map或空集合,若是则返回true,否则返回false。
  • 使用表达式语言,如果没有找到指定的属性不会抛出异常,而是返回空字符串。
EL隐含变量
EL表达式中的11个隐含变量
变量名说明
pageContext包含JSP常规隐含对象的PageContext类型对象
param包含请求参数字符串的Map对象
paramValues包含请求参数字符串数组的Map对象
header包含请求头字符串的Map对象
headerValues包含请求头字符串数组的Map对象
initParam包含Servlet上下文参数的参数名和参数值的Map对象
cookie匹配Cookie域和单个对象的Map对象
pageScope包含page作用域属性的Map对象
requestScope包含request作用域属性的Map对象
sessionScope包含session作用域属性的Map对象
applicationScope包含application作用域属性的Map对象
  • pageContext是PageContext类型的变量。pageContext变量包含request、response、session、out和servletContext属性,使用pageContext变量可以访问这些属性的属性。
  • 因为数组元素是按整数下标访问的,因此必须使用“[]”运算符访问数组元素。
${pageContext.request.method}//获得HTTP请求的方法,如GET或POST
${pageContext.request.queryString}//获得请求的查询串
${pageContext.request.requestURL}//获得请求的URL
${pageContext.request.remoreAddr}//获得请求的IP地址
${pageContext.session.id}//获得会话的ID
${pageContext.session.new}//判断会话对象是否是新建的
${pageContext.servletContext.servletInfo}//获得服务器的信息

注意

  • ${(5+3+a>0)?10:20}a没有定义,但语句合法且输出10。
  • 点号(.)运算符用来访问bean对象的属性值或Map对象一个键的值。方括号([])运算符除了可以访问Map对象键值和bean的属性值外,还可以访问List对象和数组对象的元素。
JSTL和自定义标签

JSTL核心标签的分类

JSTL标签类别JSTL标签标签说明
通用目的<c:out>
<c:set>
<c:remove>
<c:catch>
在页面中显示内容
定义或设置一个作用域变量值
清除一个作用域变量
捕获异常
条件控制<c:if>
<c:choose>
<c:when>
<c:otherwise>
根据一个属性等于一个值改变处理
根据一个属性等于一组值改变处理
用来测试一个条件
当所有when条件都为false时,执行该标签内的内容
循环控制<c:forEach>
<c:forTokens>
对集合中的每个对象作迭代处理
对给定字符串中的每个子串执行处理
URL处理<c:url>
<c:import>
<c:redirect>
<c:param>
重写URL并对它们的参数编码
访问Web应用程序外部的内容
告诉客户浏览器访问另一个URL
用来传递参数
<c:out value="value" [escapeXml="{true|false}"] default="defaultValue"/>
<c:out value="value" [escapeXml="{true|false}"]>default value</c:out>

如果escapeXml的值为true(默认值),表示将value属性值中包含的<、>、’、"或&等特殊字符转换为相应的实体引用(或字符编码),如小于号将转换为&lt;大于号将转换为&gt;如果escapeXml的值为false将不转换。(不转换时写<br>会输出空行,相当于模板文本,转换时则输出<br>)。

<c:set var="varName" value="value" [scope="{page|request|session|application}"]/>
<c:set var="varName" [scope="{page|request|session|application}"]>
body content</c:set>

作用域默认值为page作用域。

<c:set target="target" property="propertyName" value="value"/>
<c:set target="target" property="propertyName">body content</c:set>

target属性指定对象名,对象可以是JavaBeans或Map对象,property属性指定对象的属性名。

<c:remove var="varName" [scope="{page|request|session|application}"]/>

如果没有指定作用域,则从小到大删除,且<c:remove>标签不能用于删除JavaBeans或Map对象。

<c:catch [var="varName"]>body content</c:catch>
<c:catch var="myexception">
<%
int i=0;
int j=10/i;
%>
</c:catch>
<c:out value="${myexception}"/><br>//  java.lang.ArithmeticException:/by zero
<c:out value="${myexception.message}"/>//  /by zero
<c:if test="testCondition" var="varName" [scope="{page|request|session|application}"]/>
<c:if test="testCondition" var="varName" [scope="{page|request|session|application}"]>
body content</c:if>

若test的结果为true,则执行标签体。

<c:set var="color" value="white" scope="session"/>
<c:choose>
<c:when test="${color=='white'}">
白色
</c:when>
<c:when test="${color=='black'}">
黑色!
</c:when>
<c:otherwise>
其他颜色!
</c:otherwise>
</c:choose>
<c:forEach [var="varName"][begin="begin" end="end" step="step"] [varStatus="varStatusName"]>
body content
</c:forEach>
<c:forEach var="varName" items="collection" [varStatus="statusName"][begin="begin" end="end" step="step"]>
body content</c:forEach>

这种迭代主要用于对Java集合对象的元素迭代,集合对象如List、Set或Map等。

<c:forTokens items="stringOfTokens" delims="delimiters" [var="varName"] [varStatus="varStatusName"] [begin="begin"] [end="end"] [step="step"]>
body content
</c:forTokens>
<c:param name="name" value="value"/>
<c:param name="name">param value</c:param>
<c:import url="url" [context="context"] [var="varName"] [scope="{page|request|session|application}"] [charEncoding="charEncoding"]>
body content</c:import>
<c:import url="url" [context="context"] [varReader="varreaderName"][charEncoding="charEncoding"]>
body content</c:import>

var表示变量名,用来存放包含的内容的HTML代码,varReader用于表示读取的文件的内容。在标签体中可以使用<c:param>指定URL的请求参数。

  • 使用<jsp:include>标准动作能够包含的文件类型有一定限制,它所包含的内容必须位于与所在页面相同的上下文(ServletContext)中,而使用<c:import>标签所包含的资源可以位于不同的上下文。
<c:redirect url="url" [context="context"]/>
<c:redirect url="url" [context="context"]> <c:param>subtags</c:redirect>
<c:url value="value" [context="context"] [var="varName"] [scope="{page|request|session|application}"]/>
<c:url value="value" [context="context"] [var="varName"] [scope="{page|request|session|application}"]>
<c:param name="name" value="value">
</c:url>

如果用户浏览器不接受Cookie,那么就需要重写URL来维护会话状态。为此核心库提供了<c:url>标签。通过value属性来指定一个基URL,而转换的URL由JspWriter显示出来或者保存到由可选的var属性命名的变量中。

JspWriter out = jspContext.getOut();
PrintWriter out= resp.getWriter();

SimpleTag接口的方法

方法描述
public void setJspContext(JspContext pc)该方法由容器调用,用来设置JspContext对象,使其在标签处理类中可用
public void setParent(JspTag parent)该方法由容器调用,用来设置父标签对象。
public void setJspBody(JspFragment jspBody)若标签带标签体,容器调用该方法将标签体内容存放到JspFragment中。
public JspTag getParent()返回当前标签的父标签
public void doTag() throws JspException,IOException该方法是简单标签的核心方法,由容器调用完成简单标签的操作。

当容器在JSP页面中遇到自定义标签时,它将加载标签处理类并创建一个实例,然后调用标签类的生命周期方法。简单标签的生命周期有下面几个主要阶段:

  1. 调用setJspContext()
  2. 调用setParent()
  3. 调用属性的修改方法
  4. 调用setJspBody()
  5. 调用doTag()

SimpleTagSupport类是SimpleTag接口的实现类,它除实现了SimpleTag接口中的方法外,还提供了另外三个方法。

  • protected JspContext getJspContext():返回标签中要处理的JspContext对象。
  • protected JspFragment getJspBody():返回JspFragment对象,它存放了标签体的内容。
  • public static final JspTag findAncestorWithClass(JspTag from,Class klass):根据给定的实例和类型查找最接近的实例。该方法主要用在开发协作标签中。

<taglib>的子元素

元素说明
description描述该标签库的文本
display-name图形工具可显示的简短名称
icon图形工具可显示的图标
tlib-version指定标签库的版本,该元素是必需的
short-name为库中的标签指定一个名称
uri指定使用该标签库中标签的URI
validator关于该库的TagLibraryValidator信息
listener指定事件监听器类
tag定义一个标签
function定义一个在EL中使用的函数
  • <taglib>元素中只有<tlib-version>和<short-name>元素是必需的,其他元素都是可选的。
  • 这里的<uri>元素值看上去像一个Web资源的URI,但实际上它仅仅是一个逻辑名称,并不与任何Web资源对应,容器使用它仅完成URI与TLD文件的映射。
  • 容器可以自动建立一个URI与TLD之间的映射,也可以在web.xml中定义URI与TLD之间的映射关系。
<jsp-config>
	<taglib>
		<taglib-uri>http://www.mydomain.com/sample</taglib-uri>
		<taglib-location>/WEB-INF/mytaglib.tld</taglib-location>
	</taglib>
</jsp-config>

<tag>元素的子元素

子元素说明
description指定针对标签的信息
display-name开发工具用于显示的一个简短名称
icon可被开发工具使用的图标
name唯一的标签名JSP页面中将使用该名称
tag-class标签处理类的完整名称
tei-classjavax.servlet.jsp.tagext.TagExtraInfo类的一个可选子类
body-content标签体的内容类型,其值可以为scriptless、tagdependent或empty,默认值为empty
variable定义一个作用域变量名称,它可设置JspContext属性
attribute定义该标签可接受的属性
example使用该标签例子的可选的非正式描述

在一个TLD中不能定义多个同名的标签,但是可以使用一个标签处理类定义多个名称不同的标签。

<attribute>的子元素

元素说明
description有关描述的文本信息
name在JSP标签中使用的属性名称
required指定属性是必需的还是可选的,默认值为false,表示属性可选。如果该值设置为true,则JSP页面必须为该属性提供一个值。该元素可能的值包括true、false、yes和no
rtexprvalue指定属性是否能接收请求时表达式的值,默认值为false,表示不能接收请求时表达式的值。可能的值包括true、false、yes和no
type属性的数据类型,该元素只能用在当<rtexprvalue>设置为true时,它指定当使用请求时属性表达式的返回类型,默认值为String
  • 在JSP页面中访问空标签有两种写法,一种写法是由一对开始标签和结束标签组成,中间不含任何内容,例如:<prefix:tagName></prefix:tagName>
  • 另一种写法是简化的格式,即在开始标签末尾使用一个斜线(/)表示标签结束,例如:<prefix:tagName>
<tag>
	<name>welcome</name>
	<tag-class>com.mytag.WelcomeTag</tag-class>
	<body-content>scriptless</body-content>
	<attribute>
		<name>user</name>
		<required>false</required>
		<rtexprvalue>false</rtexprvalue>
	</attribute>
</tag>

<demo:welcome user="${param.userName}"></demo:hello>
<demo:welcome>
	<jsp:attribute name="user">${param.userName}</jsp:attribute>
</demo:welcome>
Web监听器、过滤器和异步处理

在ServletContext对象上可能发生两种事件,对这些事件可使用两个事件监听器接口处理。

  1. 处理ServletContextEvent事件
    该事件是Web应用程序生命周期事件,当容器对ServletContext对象进行初始化或销毁操作时,将发生ServletContextEvent事件。要处理这类事件,需实现ServletContextListener接口。
package javax.servlet;

import java.util.EventListener;

public interface ServletContextListener extends EventListener {
    default void contextInitialized(ServletContextEvent sce) {
    }

    default void contextDestroyed(ServletContextEvent sce) {
    }
}
package javax.servlet;

import java.util.EventObject;

public class ServletContextEvent extends EventObject {
    private static final long serialVersionUID = 1L;

    public ServletContextEvent(ServletContext source) {
        super(source);
    }

    public ServletContext getServletContext() {
        return (ServletContext)super.getSource();
    }
}
  1. 处理ServletContextAttributeEvent事件
    当ServletContext对象上属性发生改变时,如添加属性、删除属性或替换属性等,将发生ServletContextAttributeEvent事件,要处理该类事件,需要实现ServletContextAttributeListener接口。
package javax.servlet;

import java.util.EventListener;

public interface ServletContextAttributeListener extends EventListener {
    default void attributeAdded(ServletContextAttributeEvent scae) {
    }

    default void attributeRemoved(ServletContextAttributeEvent scae) {
    }

    default void attributeReplaced(ServletContextAttributeEvent scae) {
    }
}
package javax.servlet;

public class ServletContextAttributeEvent extends ServletContextEvent {
    private static final long serialVersionUID = 1L;
    private final String name;
    private final Object value;

    public ServletContextAttributeEvent(ServletContext source, String name, Object value) {
        super(source);
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return this.name;
    }

    public Object getValue() {
        return this.value;
    }
}

在ServletRequest对象上可能发生两种事件,对这些事件使用两个事件监听器处理。

  1. ServletRequestEvent事件是请求对象生命周期事件,当一个请求对象初始化或销毁时将发生该事件,处理该类事件需要使用ServletRequestListener接口。
package javax.servlet;

import java.util.EventListener;

public interface ServletRequestListener extends EventListener {
    default void requestDestroyed(ServletRequestEvent sre) {
    }

    default void requestInitialized(ServletRequestEvent sre) {
    }
}
package javax.servlet;

import java.util.EventObject;

public class ServletRequestEvent extends EventObject {
    private static final long serialVersionUID = 1L;
    private final transient ServletRequest request;

    public ServletRequestEvent(ServletContext sc, ServletRequest request) {
        super(sc);
        this.request = request;
    }

    public ServletRequest getServletRequest() {
        return this.request;
    }

    public ServletContext getServletContext() {
        return (ServletContext)super.getSource();
    }
}
  1. 在请求对象上添加、删除和替换属性时将发生ServletRequestAttributeEvent事件,处理该类事件需要使用ServletRequestAttributeListener接口。
package javax.servlet;

import java.util.EventListener;

public interface ServletRequestAttributeListener extends EventListener {
    default void attributeAdded(ServletRequestAttributeEvent srae) {
    }

    default void attributeRemoved(ServletRequestAttributeEvent srae) {
    }

    default void attributeReplaced(ServletRequestAttributeEvent srae) {
    }
}
package javax.servlet;

public class ServletRequestAttributeEvent extends ServletRequestEvent {
    private static final long serialVersionUID = 1L;
    private final String name;
    private final Object value;

    public ServletRequestAttributeEvent(ServletContext sc, ServletRequest request, String name, Object value) {
        super(sc, request);
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return this.name;
    }

    public Object getValue() {
        return this.value;
    }
}

在HttpSession对象上可能发生两种事件,对这些事件可使用4个事件监听器处理。

  1. HttpSessionEvent事件是会话对象生命周期事件,当一个会话对象被创建和销毁时发生该事件,处理该事件应该使用HttpSessionListener接口。
package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionListener extends EventListener {
    default void sessionCreated(HttpSessionEvent se) {
    }

    default void sessionDestroyed(HttpSessionEvent se) {
    }
}
//HttpSessionEvent只定义了getSession()方法返回一个HttpSession。
  1. 当在会话对象上添加属性、删除属性、替换属性时将发生HttpSessionBindingEvent事件,处理该事件需使用HttpSessionAttributeListener接口。
package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionAttributeListener extends EventListener {
    default void attributeAdded(HttpSessionBindingEvent se) {
    }

    default void attributeRemoved(HttpSessionBindingEvent se) {
    }

    default void attributeReplaced(HttpSessionBindingEvent se) {
    }
}
//注意参数是HttpSessionBindingEvent,该类定义了三个方法:
public HttpSession getSession()   //会话对象
public String getName()   //属性名
public Object getValue()   //属性值
  1. 当一个对象绑定到会话对象或从会话对象中解除绑定时发生HttpSessionBindingEvent事件,应该使用HttpSessionBindingListener接口来处理这类事件。
package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionBindingListener extends EventListener {
    default void valueBound(HttpSessionBindingEvent event) {
    }

    default void valueUnbound(HttpSessionBindingEvent event) {
    }
}

HttpSessionAttributeListener与HttpSessionBindingListener这两个接口都用来监听会话中属性改变的事件,但二者不同,区别如下:

  • 实现HttpSessionAttributeListener接口的类与一般监听器一样需要用@WebListener注解或在DD文件中注册该监听器类,而实现HttpSessionBindingListener接口的监听器,不必注册
    而是当相应事件发生时由容器调用对象相应的方法。
  • 所有会话中产生的属性改变事件都被发送到实现HttpSessionAttributeListener接口的对象。对HttpSessionBindingListener接口来说,只有当实现该接口的对象添加到会话中或从会话中删除时,容器才在对象上调用有关方法。

过滤器使用的接口和类

接口或抽象类说明
Filter接口所有的过滤器都需要实现该接口
FilterConfig接口过滤器配置对象。容器提供了该对象,其中包含了该过滤器的初始化参数
GenericFilter抽象类该抽象类实现了FilterConfig接口的方法和Filter接口的init()方法,没有抽象方法的抽象类,但继承了Filter接口的doFilter()抽象方法需要重写
HttpFilter抽象类该抽象类扩展了GenericFilterl类,实现针对HTTP协议的过滤器,没有抽象方法的抽象类,实现了doFilter()方法
FilterChain接口过滤器链对象
public interface Filter {
    default void init(FilterConfig filterConfig) throws ServletException {
    }

    void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

    default void destroy() {
    }
}
package javax.servlet;

import java.util.Enumeration;

public interface FilterConfig {
    String getFilterName();

    ServletContext getServletContext();

    String getInitParameter(String var1);

    Enumeration<String> getInitParameterNames();
}
//容器提供了FilterConfig接口的一个具体实现类,容器创建该类的一个实例、使用初始化参数值对它初始化,然后将它作为一个参数传递给过滤器的init()。
public interface FilterChain {
    void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
}
//在Filter对象的doFilter()中调用该方法使过滤器继续执行,它将控制转到过滤器链的下一个过滤器或实际的资源。
//容器提供了该接口的一个实现并将它的一个实例作为参数传递给Filter接口的doFilter()。在doFilter()内,可以使用该接口将请求传递给链中的下一个组件,它可能是另一个过滤器或实际的资源。该方法的两个参数将被链中下一个过滤器的doFilter()或Servlet的service()接收。

GenericFilter抽象类实现了FilterConfig接口的方法和Filter接口的init()方法。HttpFilter抽象类扩展了GenericFilter类,实现了doFilter()方法。

public abstract class HttpFilter extends GenericFilter {
    private static final long serialVersionUID = 1L;

    public HttpFilter() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)) {
            throw new ServletException(request + " not HttpServletRequest");
        } else if (!(response instanceof HttpServletResponse)) {
            throw new ServletException(request + " not HttpServletResponse");
        } else {
            this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain);
        }
    }

    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        chain.doFilter(request, response);
    }
}

@WebFilter注解的常用属性

属性名类型说明
filterNameString指定过滤器的名称,等价于web.xml中的<filter-name>元素。如果没有显示指定,则使用Filter的完全限定名作为名称
urlPatternsString[]指定一组过滤器的URL匹配模式,该元素等价于web.xml文件中的<url-pattern>元素
valueString[]该属性等价于urlPatterns元素。两个元素不能同时使用
servletNamesString[]指定过滤器应用于哪些Servlet。取值是@WebServlet中name属性值,或者是web.xml中<servlet-name>的取值
dispatcherTypesDispatcherType指定过滤器的转发类型。具体取值包括ASYNC、ERROR、FORWARD、INCLUDE和REQUEST
initParamsWebInitPara[]指定一组过滤器初始化参数,等价于<init-param>元素
asyncSupportedboolean声明过滤器是否支持异步调用,等价于<async-supported>元素
descriptionString指定该过滤器的描述信息,等价于<description>元素
displayNameString指定该过滤器的显示名称,等价于<display-name>
<filter>
	<filter-name>validatorFilter</filter-name>
	<filter-class>filter.ValidatorFilter</filter-class>
		<init-param>
			<param-name>locale</param-name>
			<param-value>USA</param-value>
		</init-param>
</filter>
<filter-mapping>
	<filter-name>validatorFilter</filter-name>
	<url-pattern>*.jsp</url-pattern>
	<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<filter-mapping>
	<filter-name>validatorFilter</filter-name>
	<servlet-name>reportServlet</servlet-name>
</filter-mapping>

当容器接收到一个请求,它将查找所有与请求URI匹配的过滤器映射的URL模式,这是过滤器链中的第一组过滤器。接下来,它将查找与请求URI匹配的Servlet名,这是过滤器链中的第二组过滤器。简言之就是先找urlPatterns然后找servletName,在这基础上如果通过注解声明过滤器则比较过滤器名字,如果通过web.xml定义过滤器则按DD文件中定义的顺序。

Servlet 3.0增加了异步处理支持,Servlet的执行过程调整如下:Web容器接收到用户对某个Servlet请求之后启动一个线程,在该线程中对请求的数据进行预处理;接着,Servlet线程将请求转交给一个异步线程来执行业务处理,Servlet线程本身返回至容器,此时Servlet还没有生成响应数据。异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有ServletRequest和ServletResponse对象的引用),或者将请求转发给其他Servlet或JSP页面。这样,Servlet线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。默认情况下,Servlet和过滤器并没有开启异步处理特性。
Servlet 3.0的异步处理时通过AsyncContext类来实现的,Servlet可以通过ServletRequest的如下两个方法创建AsyncContext对象,开启异步调用。

  • public AsyncContext startAsync():开始异步调用并返回AsyncContext对象,其中包含最初的请求和响应对象。
  • public AsyncContext startAsync(ServletRequest request,ServletResponse response):开启异步调用,并传递经过包装的请求和响应对象。

在Servlet 3.0中增加了一个AsyncListener接口来处理异步操作事件。当Servlet启用异步调用时发生AsyncEvent事件。该接口定义了如下4个方法:

  • void onStartAsync(AsyncEvent var1) throws IOException;
  • void onComplete(AsyncEvent var1) throws IOException;
  • void onError(AsyncEvent var1) throws IOException;
  • void onTimeout(AsyncEvent var1) throws IOException;

AsyncEvent类对象的常用方法:

  • public AsyncContext getAsyncContext() {return this.context;}
  • public ServletRequest getSuppliedRequest() {return this.request;}
  • public ServletResponse getSuppliedResponse() {return this.response;}

注意,AsyncListener监听器需要使用AsyncContext对象的addListener()进行手工注册。

req.setAttribute("a","b");
req.setAttribute("a","c");
会触发public void attributeReplaced(ServletRequestAttributeEvent srae)

在chain.doFilter(req,res);之后可以继续使用request对象。

Web安全性

Web应用的安全性措施主要包括下面4个方面:

  • 身份验证(authentication)
    在Internet领域,验证一个用户的基本方法通常是用户名和口令。
  • 授权(authorization)
    授权是确定用户是否被允许访问他所请求的资源的过程。授权通常通过一个访问控制列表(Access Control List,ACL)强制实施,该列表指定了用户和他所访问的资源类型。
  • 数据完整性(integrity)
    数据完整性是指数据从发送者传输到接收者的过程中数据不被破坏。数据完整性通常是通过与数据一起发送一个哈希码或签名保证的。在接收端需要验证数据和哈希码。
  • 数据保密性(confidentiality)
    数据保密性是保证除应该访问它的用户外,别人不能访问敏感信息。目前,大多数Web站点使用HTTPS协议来对信息加密,这样,即使黑客分析数据,也不能对它解密,所以也不能使用它。
    授权与保密的区别是二者对信息保护的方式不同。授权是首先防止信息到达无权访问的用户,而保密是保证即使信息被非法获得,也不能被使用。

在Servlet规范中定义了4种用户验证的机制,这些验证机制都是基于用户名/口令的机制,在该机制中,服务器维护一个所有用户名和口令的列表以及需要保护的资源列表。

  1. HTTP Basic验证
    HTTP基本验证是由HTTP 1.1规范定义的,这是一种保护资源的最简单和最常用的验证机制。当浏览器请求任何受保护资源时,服务器都要求一个用户名和口令。如果用户输入了合法的用户名/口令,服务器才发送资源。
    HTTP基本验证的优点是:实现较容易,所有的浏览器都支持。缺点是:因为用户名/口令没有加密,而是采用Base64编码,所以不是安全的;不能自定义对话框的外观。
  2. HTTP Digest验证
    HTTP摘要验证,它除了口令是以加密的方式发送的,其他与基本验证都一样,但比基本验证安全。
    HTTP摘要验证的优点有:它比基本验证更安全。缺点有:它只能被IE5以上版本的浏览器支持;许多Servlet容器不支持,因为规范并没有强制要求。
  3. FORM-based验证
    基于表单的验证类似于基本验证,但它使用用户自定义的表单来获得用户名和口令而不是使用浏览器的弹出对话框。开发人员必须创建包含表单的HTML页面,对表单外观可以定制。
    基于表单验证的优点是:所有的浏览器都支持,且很容易实现,客户可以定制登录页面的外观(Look And Feel)。缺点是:它不是安全的,因为用户名/口令没有加密。
  4. HTTPS Client验证
    客户证书验证采用HTTPS传输信息。HTTPS是在安全套接层(Secure Socket Layer,SSL)之上的HTTP,SSL可以保证Internet上敏感数据传输的保密性。在这种机制中,当浏览器和服务器之间建立起SSL连接后,所有的数据都以加密的形式传输。
    这种验证的优点有:它是4种验证类型中最安全的;所有常用的浏览器都支持这种验证。缺点有:它需要一个证书授权机构(如 VeriSign)的证书;它的实现和维护的成本较高。

实施Web应用程序的安全性可以有两种方法:声明式安全和编程式安全。

  • 所谓声明式安全(declarative security)是一个应用程序的安全结构,包括角色、访问控制及验证需求都在应用程序外部表示。在应用程序内通过部署描述文件(web.xml)声明安全约束。应用程序部署人员把应用的安全逻辑需求映射到运行时环境的安全策略表示,容器使用该安全策略实施验证和授权。
    安全模型可以应用在Web应用程序的静态内容部分,也可以应用在客户请求的Servlet和过滤器上,但不能应用在Servlet使用RequestDispatcher调用的静态资源和使用forward和include转发和包含的资源上。
  • 声明式安全在资源(如Servlet)中不包含任何有关安全的代码。但有时可能需要更精细的安全约束或声明式安全不足以表示应用的安全模型,这时可采用编程式安全。编程式安全(programmatic security)主要使用HttpServletRequest接口中的有关方法实现。

安全域是Web服务器保护Web资源的一种机制。所谓安全域(realm)是标识一个Web应用程序的合法的用户名和口令的“数据库”,其中包括与用户相关的角色。
安全域是Tomcat内置的功能,它通过org.apache.catalina.Realm接口把一组用户名、口令及用户所关联的角色集成到Tomcat中。Tomcat提供了5个实现这一接口的类,它们分别代表5种安全域模型。

Tomcat的安全域类型

安全域类型类名说明
内存域MemoryRealm在Tomcat服务器初始化阶段,从一个XML文档中读取验证信息,并把它们以一组对象的形式存放在内存中
JDBC域JDBCRealm通过JDBC驱动程序访问存放在数据库中的验证信息
数据源域DataSourceRealm通过JNDI数据源访问存放在数据库中的验证信息
JNDI域JNDIRealm通过JNDI provider访问存放在基于LDAP的目录服务器中的安全验证信息
JAAS域JAASRealm通过JAAS(Java 验证授权服务)框架访问验证信息
//内存域安全模型示例
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
                <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
  <user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
  <user username="role1" password="<must-be-changed>" roles="role1"/>
</tomcat-users>

为Web资源定义安全约束是通过web.xml文件实现的,这里主要配置哪些角色可以访问哪些资源。
<login-config>元素定义使用的验证机制。

<login-config>
    <auth-method>BASIC</auth-method>  <!--指定验证方法,BASIC,DIGEST,FORM,CLIENT-CERT-->
    <realm-name>Security Test</realm-name>  <!--仅用在HTTP基本验证中,指定安全域名称-->
    <form-login-config>                  <!--仅用在基于表单的验证中-->
        <form-login-page>/login.do</form-login-page>
        <form-error-page>/statusServlet</form-error-page>
    </form-login-config>
</login-config>
<security-role>
    <role-name>manager</role-name>
</security-role>
<security-role>
    <role-name>member</role-name>
</security-role>
<security-role>  <!--用来定义安全约束中引用的角色名,Servlet4.0中只能有一个<role-name>子元素-->
    <description></description>
    <role-name>manager</role-name>  <!--定义角色名,与tomcat-users.xml相匹配。-->
</security-role>
<security-role>
    <role-name>member</role-name>
</security-role>
<security-role>
    <role-name>*</role-name>
</security-role>
<security-constraint>  <!--该元素用来定义受保护的Web资源集合、访问资源的角色以及用户数据的约束。-->
    <display-name></display-name>  <!--一个可选的元素。它为安全约束指定一个易于识别的名称-->
    <web-resource-collection>  <!--指定该安全约束所应用的资源集合-->
        <web-resource-name>Security Account Servlet</web-resource-name>  <!--指定资源的名称-->
        <url-pattern>/account.do</url-pattern>  <!--指定受保护的资源-->
        <url-pattern>/securityAccount</url-pattern>
        <http-method>GET</http-method>  <!--指定该约束适用的HTTP方法,如果不写则适用所有HTTP请求,如果写了则适用写了的HTTP请求。-->
        <http-method>OPTIONS</http-method>  <!--这里的资源实际是资源和方法的组合。-->
    </web-resource-collection>
    <auth-constraint>  <!--指定可以访问在<web-resource-collection>部分中指定的资源的角色-->
        <description></description>
        <role-name>manager</role-name>  <!--指定可以访问受限资源的角色,*则表示Web应用程序中定义的所有角色。-->
        <role-name>member</role-name>
        <role-name>*</role-name>  <!--*也要在<security-role>元素中声明,然后才能在这里使用。-->
    </auth-constraint>
    <user-data-constraint>  <!--指定数据应该如何在客户与服务器之间通信-->
        <description></description>
        <transport-guarantee>NONE</transport-guarantee>  <!--NONE表示不对传输的数据有任何完整性和保密性的要求,通常使用普通的HTTP协议。-->
        <!--INTEGRAL和CONFIDENTIAL则分别表示要求传输的数据的完整性和保密性,此时使用HTTPS协议。-->
    </user-data-constraint>
</security-constraint>
<login-config>
    <auth-method>BASIC</auth-method>  <!--定义使用的验证机制。-->
    <realm-name>Security Account Servlet</realm-name>  <!--仅用在HTTP基本验证(BASIC)中,指定安全域(realm)名称。-->
    <!--<form-login-config>元素仅用在基于表单的验证(FORM)中,指定登录页面的URL和指定错误页面的URL。-->
</login-config>

注意,对于使用基于表单验证方法的登录页面,表单的action属性值必须为j_security_check,用户名输入域的name属性值必须是j_username,口令输入域的name属性值必须是j_password。对基于表单的验证,没有写任何的Servlet来处理表单。j_security_check动作触发容器本身完成处理。如果用户名和密码全部正确但是没有授权不会跳转到错误页面,而是返回403未授权。
声明式的安全优点是实现了应用程序的开发者和部署者的分离。

Sevlet的安全API。

  • public String getAuthType():返回用来保护Servlet的认证方案,如果没有安全约束则返回null。
  • public String getRemoteUser():如果用户已被验证,该方法返回验证用户名。如果用户没有被验证,则返回null。
  • public boolean isUserInRole(String rolename):返回一个布尔值,表示验证的用户是否属于指定的角色。如果用户没有被验证或不属于指定角色,将返回false。
  • public Principal getUserPrincipal():返回java.security.Principal对象,它包含当前验证的用户名。如果用户没有被验证,则返回null。
  • public boolean authenticate(HttpServletResponse response):通过指示浏览器显示登陆表单来验证用户。
  • public void login(String username,String password):试图使用所提供的用户名和密码进行登录。该方法没有返回,如果登录失败,将抛出一个ServletException异常。
  • public void logout():注销用户登录。
AJAX技术
  • AJAX是Asynchronous JavaScript and XML的缩写,含义是异步JavaScript与XML。AJAX技术是一种客户端技术,它实现客户浏览器与服务器的异步交互。AJAX可以在不刷新整个页面的情况下用JavaScript操作DOM以实现页面动态更新。
  • 开发人员可以使用HTML和CSS实现数据信息的统一化、标准化显示;使用DOM实现浏览器丰富的动态显示效果;使用XML和XSTL进行浏览器和服务器的数据交换和处理;使用XMLHttpRequest实现客户与服务器之间的异步请求和响应;使用JavaScript脚本语言对所有数据进行处理。
  • XML(eXtensible Markup Language)称为可扩展的标记语言。XSL(eXtensible Stylesheet Language)称为可扩展的样式单语言,它是一种用来转换XML文档结构的语言。

XMLHttpRequest对象的属性

属性名说明
onreadystatechange为异步请求设置事件处理程序。每当XMLHttpRequest对象的状态改变时都会触发这个事件处理器,通常会调用一个JavaScript函数
readyState该属性表示请求的状态。它可有下面几个不同的值:0:未初始化;1:正在加载;2:已加载;3:交互中;4:完成
responseText检索服务器响应,并表示为文本
responseXML检索服务器响应,并表示为XML DOM对象
status检索服务器的HTTP状态码。如404表示Not Found,200表示OK
statusText检索服务器的HTTP状态码的文本
responseBody检索响应体。该属性是IE7及以后版本的window对象,但不是W3C的规范

XMLHttpRequest对象的常用方法

方法名说明
abort()取消当前HTTP请求
getAllResponseHeaders()返回所有的响应头。如果readyState属性的值不是3或4,将返回null
getResponseHeader(String header)返回指定的响应头。如果readyState属性的值不是3或4,将返回null
open(String method,String url,boolean asych,String username,String password)打开一个HTTP请求,但还没有发送请求。调用open()将readyState属性设置为1,responseText、responseXML、status和statusText属性设置为初始值。在open()中需要指定使用的HTTP方法(GET、POST或PUT)和服务器的URL(相对或绝对)。另外,还可以传递一个boolean值,指示这个调用是异步的还是同步的,默认值为true,表示请求是异步的。如果这个参数是false,浏览器就会等待,直到从服务器返回响应为止。username和password指定服务器验证的用户名和密码,后三个参数是可选的
send(data)向服务器发送HTTP请求并检索响应。数据可以是字符串、无符号字节数组或XML DOM对象等。发送的数据是可选的,其值可以为null。根据open()中asych参数的值不同,send()可以是同步的,也可以是异步的。如果是同步的,send()只有在接收到全部响应才返回,如果是异步的,该方法立即返回。在调用send()后,readyState属性被设置为2,当请求完成时,readyState属性被设置为4
setRequestHeaders(String header,String value)设置请求的HTTP头。header为请求头名,value为请求头的值

当前浏览器都使用DOM来表示Web页面内容,使用JavaScript可以很容易访问页面元素。

动态创建内容的DOM属性和方法

属性/方法说明
document.createElement(tagName)创建由tagName指定的元素
document.createTextNode(text)创建一个包含静态文本的节点
.appendChild(childNode)将指定的节点添加到当前元素的子节点列表,作为一个新的子节点
.getAttribute(name)获取元素中name属性的值
.setAttribute(name,value)设置元素中name属性的值
.insertBefore(newNode,targetNode)将节点newNode作为当前元素的子节点插入到targetNode元素的前面
.removeAttribute(name)从元素中删除属性
.removeChild(childNode)从元素中删除子元素
.replaceChild(newNode,oldNode)用newNode节点替换oldNode节点
.hasChildNodes()返回一个布尔值,判断该元素是否有子元素

AJAX工具提示示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AJAX Tool Tip</title>
    <script type="text/javascript">
        var xmlHttp;
        var dataDiv;
        var dataTable;
        var dataTableBody;
        var offsetEl;
        function createXMLHttpRequest() {
            if (window.XMLHttpRequest){
                xmlHttp=new XMLHttpRequest();
            }else {
                xmlHttp=new ActiveXObject("Microsoft:XMLHTTP");
            }
        }
        function initVars() {
            dataTableBody=document.getElementById("dogDataBody");
            dataTable=document.getElementById("dogData");
            dataDiv=document.getElementById("popup");
        }
        function getDogData(element) {
            initVars();
            createXMLHttpRequest();
            offsetEl=element;
            var url="/toolTip.do?key="+escape(element.id);
            xmlHttp.open("GET",url,true);
            xmlHttp.onreadystatechange=handleStateChange;
            xmlHttp.send(null);
        }
        function handleStateChange() {
            if (xmlHttp.readyState==4){
                if (xmlHttp.status==200){
                    setData(xmlHttp.responseXML);
                }
            }
        }
        function setData(dogData) {
            clearData();
            setOffsets();
            var desc=dogData.getElementsByTagName("description")[0].firstChild.nodeValue;
            var row=createRow(desc);
            dataTableBody.appendChild(row);
        }
        function createRow(data) {
            var row,cell,txtNode;
            row=document.createElement("tr");
            cell=document.createElement("td");
            cell.setAttribute("bgcolor","$FFFAFA");
            cell.setAttribute("border","0");
            txtNode=document.createTextNode(data);
            row.appendChild(cell);
            cell.appendChild(txtNode);
            return row;
        }
        function setOffsets() {
            var top=offsetEl.offsetHeight;
            var left=calculateOffsetLeft(offsetEl);
            dataDiv.style.border="blue 1px solid";
            dataDiv.style.left=left+20+"px";
            dataDiv.style.top=top+50+"px";
        }
        function calculateOffsetLeft(field) {
            var offset=0;
            while (field){
                offset+=field["offsetLeft"];
                field=field.offsetParent;
            }
            return offset;
            alert(offset);
        }
        function clearData() {
            var ind=dataTableBody.childNodes.length;
            for (var i=ind-1;i>=0;i--){
                dataTableBody.removeChild(dataTableBody.childNodes[i]);
            }
            dataDiv.style.border="none";
        }
    </script>
</head>
<body>
<h4>AJAX工具提示示例</h4>
<table id="dogs" bgcolor="#FFFAFA" border="1" cellspacing="0" cellpadding="2">
    <tbody>
    <tr><td id="dog1" onmouseover="getDogData(this);" onmouseout="clearData()"><img src="/images/dog0.jpg" width="128" height="96" alt="未找到图片位置"></td>
    <td id="dog2" onmouseover="getDogData(this);" onmouseout="clearData()"><img src="/images/dog1.jpg" width="128" height="96" alt="未找到图片位置"></td>
    <td id="dog3" onmouseover="getDogData(this);" onmouseout="clearData()"><img src="/images/dog2.jpg" width="128" height="96" alt="未找到图片位置"></td></tr>
    </tbody>
</table>
<div style="position: absolute;" id="popup">
        <table id="dogData" bgcolor="#FFFAFA" border="0" cellspacing="2" cellpadding="2">
            <tbody id="dogDataBody"></tbody>
        </table>
    </div>
</body>
</html>

package Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@WebServlet("/toolTip.do")
public class ToolTipServlet extends HttpServlet {
    private Map<String,String> dogs=new HashMap<>();

    @Override
    public void init() throws ServletException {
        dogs.put("dog1","It is a dog!");
        dogs.put("dog2","It is a lovely dog!");
        dogs.put("dog3","It is a very lovely dog!");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String key=req.getParameter("key");
        String data = dogs.get(key);
        PrintWriter out = resp.getWriter();
        resp.setContentType("text/xml;charset=utf-8");
        resp.setHeader("Cache-Control","no-cache");
        out.println("<response>");
        out.println("<description>"+data+"</description>");
        out.println("</response>");
        out.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doGet(req, resp);
    }
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值