JSP笔记
1、什么是Jsp?
l JSP全称是JavaServerPages,它是SUN公司定义的一种用于开发动态web资源的技术。
因为JSP技术允许在页面中嵌套Java代码,以产生动态数据,并且web服务器在执行jsp时,web服务器会传递web开发相关的对象给jsp,jsp通过这些对象,可以与浏览器进行交互,所以jsp是一种动态web资源开发技术。
l JSP快速入门:在Jsp页面中输出当前时间。
1.jsp代码如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JSP入门案例:显示用户当前时间 </title> </head> <body> 当前时间值: <% Date date = new Date(); out.write(date.toLocaleString()); %> </body> </html> |
l JSP的运行原理:
(1)服务器是如何运行一个jsp的?
(2)Jsp页面中的静态页面是如何发送给浏览器的?
(3)Jsp页面的Java代码是如何被执行的?
答:jsp页面在第一次被访问的时候,web容器(jsp引擎)会将jsp翻译成一个Servlet,然后调用Service方法;
Jsp翻译后的servlet会被放到%Tomcat安装目录%\work\Catalina\localhost\webcontext,如1.jsp代码在本机上被放到的位置为:D:\MyProgram\tomcat6.0\work\Catalina\localhost\day08\org\apache\jsp";
当Jsp页面被再次访问的时候,web容器会去直接调用Servlet的Service方法,所以通常来讲jsp只是在第一次访问的时候会比较慢;
如果jsp页面做了修改,此时web容器会重新翻译jsp。
2、JSP用在哪里以及与Servlet的区别
l Jsp和Servlet都可以用于开发动态web资源,它们的具体用途如下所示:
(1)Servlet作为web应用中的控制器组件来使用
在Servlet中作数据美化很麻烦,但是适合写大量的Java代码,所以Servlet用于对请求进行处理;
(2)Jsp技术作为数据显示模版来使用
在Jsp页面中不适合写复杂的Java代码,但是做数据美化很方便,相当于在写html,所以一般用jsp来做显示。
(3)项目中的web层通常使用mvc设计模式即Servlet + jsp +javabean,其中,Servlet用做控制器,处理用户请求,jsp作为显示模版,javabean作为封装数据的实体。
(4)如何养成一种良好的编码风格?
A、在Servlet中应避免做任何的数据输出;
B、在jsp中应避免去直接书写Java代码,需要用到EL表达式和JSTL标签。
3、JSP语法
l JSP模版元素
(1)JSP页面中的HTML内容称之为JSP模版元素;
(2)JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。
l JSP脚本表达式
(1)JSP脚本表达式(expression)用于将程序数据输出到客户端
语法:<%=变量或表达式%>
举例:在JSP中显示当前时间:
<%= new java.util.Date() %> |
(2)JSP引擎在翻译脚本表达式时,然后在相应位置使用out.print(....)将数据输给客户端
注意:JSP脚本表达式中的变量或表达式后面不能有分号(;)
l JSP脚本片断
(1)JSP脚本片断(scrtptlet)用于在JSP页面中编写多行Java代码;
语法:<%多行Java代码%>
(2)JSP脚本片段中的内容就是在写Java代码,所以必须严格遵循Java语法,脚本片段会被原封不懂地放到service方法中;
(3)JSP页面中可以初相多个脚本片段,两个或多个脚本片段之间还可以嵌套文本、HTML标记和其他JSP元素;
(4)多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况;
(5)单个脚本片断中的Java语句可以是不完整的,但是多个脚本片断组合后的结果必须是完整的Java语句。
l JSP声明
(1)Jsp声明中的Java代码被翻译到_jspService方法的外面;语法如下所示:
<%! Java代码 %> |
(2)JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法;
(3)多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中;
(4)JSP隐士对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。
l JSP注释
(1)格式:<%--注释信息--%>;
(2)JSP引擎在将JSP页面翻译成Servlet程序是,会忽略JSP页面中被注释的内容。
l JSP指令
(1)JSP指令用于告诉JSP引擎如何处理jsp页面;
(2)JSP2.0中定义了三个指令:page指令(页面)、include指令(包含)、taglib指令(标签);
(3)JSP指令的语法格式:
<%@ 指令名 属性名=属性值 .....%> |
多个属性可以分为多个指令来书写。
(4)page指令
A、page指令用于定义JSP页面中的各种属性,page指令一般都是放在整个JSP页面的起始位置,但是无论写在哪里都是对整个页面起作用;
B、page指令的常用属性:
1)import导包语句,多个包之间使用逗号隔开,JSP引擎会自动导入如下包:
java.lang、javax.servlet、javax.servlet.jsp、javax.servlet.http |
2) session =true|false:说明是否使用Session
默认值为true,被翻译的Servlet中会自动获得Session;如果将该值指定为false时被翻译的Servlet的Service方法中将不获取Session。
3)errorPage:用于指定错误页面,jsp出错时会自动跳转到该页面;
4)isErrorPage:为true时,说明是错误页面,才能使用Exception对象;
5)ContentType:指定页面类型,相当于response的setContentType;
6)pageEncoding:向jsp引擎说明当前页面的编码,相当于指定的ContentType指令,即response.setContentType(“text/htnl;chatrset =utf-8”)。
7)扩展JSP的乱码问题:
在Tomcat6以上的版本JSP就没有乱码问题了,如果使用Tomcat5才会出现乱码问题;
JSP乱码解决:告诉JSP引擎JSP页面是什么编码,这样翻译才不会错误,或者告诉response用什么编码再发给浏览器。
l JSP的九大隐式对象(重点)
(1)JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面是获得这些WEB对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。
编号 | 对象变量名,可直接在jsp中使用 | 对象类型 |
1 | request | HttpServletRequest |
2 | response | HttpServletResponse |
3 | config | ServletConfig |
4 | application | ServletContext |
5 | exception | Throwable |
6 | session | HttpSession |
7 | page | This(翻译完的Servlet对象) |
8 | out | JspWriter |
9 | pageContext | PageContext |
(2)out隐式对象
A、out隐式对象用于想客户端发送文本数据;
B、out对象是通过调用pageContext对象的getOut方法返回的;
C、JSP页面中的out隐式度机箱的类型为JspWriter,JspWriter相当于一种带缓存功能的PrintWriter,设置JSP页面的page指令的buffer属性可以调整它的缓存大小,甚至关闭它的缓存;
D、向out对象中写入的内容,将被写入一个缓冲区,如需将数据刷新到response,必须满足以下任意的一个条件:
1)设置page指令的buffer属性关闭了out对象的缓存功能;
2)out对象的缓冲区已经满了(默认大小为8kb, 可以在page指令中通过 buffer属性设置缓冲区大小);
3)整个JSP页面结束。
E、out隐式对象的工作原理图:
F、out隐式对象的注意事项:
1)同时使用out和response.getWriter()输出数据;
2)用JSP不适合做文件下载,原因如下:
由于文件下载需要用到自截留,而JSP中的内置对象out是一个字符流,在Servlet中不能同时去获取自截留和字符流,所以会报错;
除非,夸奖JSP页面所有的内容都删除,JSP对应的Servlet就不会去获得字符流了,但这样做不太现实。
(3)pageContext对象
A、pageContext对象是JSP技术中最重要的一个对象,它代表JSP页面的运行环境,该对象提供了三个功能:
1)获得其他8个隐式对象;
2)作为域对象存储数据,被称为page域;
3)提供了一些常用操作,比如forward请求转发和include包含。
B、重点:
pageContext对象主要作用就是提供了方法用于获得8大内置对象,这样做的原因如下:在Jsp页面中不希望出现Java代码,那么Java代码就需要写到另外一个Java类的方法中,在Jsp页面通过某种方式来调研那个那个Java类的方法为页面服务,那个Java类必须获得8大内置对象,才能为jsp服务,此时就可以将pageContext这一个对象传过去就OK了!
C、pageContext作为域对象
1)pageContext对象作为域提供的方法:setAttribute、getAttribute、removeAttribute
作用范围: 只在当前jsp页面有效;
2)pageContext对象中还封装了访问其它域的方法:setAttribute(name,scope)、 setAttribute(name,scope)、getAttribute、removeAttribute;
3)代表各个域的常量:APPLICATION_SCOPE、SESSION_SCOPE、REQUEST_SCOPE、PAGE_SCOPE
4)findAttribute方法:此方法会依次在四个域中查找属性。
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>pageContext对象访问其他域</title> </head> <body> <% request.setAttribute("data","aaa"); String data = (String)pageContext.getAttribute("data",PageContext.REQUEST_SCOPE); out.write(data); pageContext.findAttribute("data");//page request session application %> </body> </html> |
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>pageContext对象的其他方法</title> </head> <body> <% pageContext.forward("/index.jsp"); pageContext.include("/index.jsp"); %> </body> </html> |
l JSP映射
(1)JSP和Servlet一样也可以在web.xml中做映射;
(2)映射格式如下所示:
<servlet> <servlet-name>SimpleJspServlet</servlet-name> <jsp-file>/jsp/simple.jsp</jsp-file> <load-on-startup>1</load-on-startup > </servlet> …… <servlet-mapping> <servlet-name>SimpleJspServlet</servlet-name> <url-pattern>/xxx/yyy.html</url-pattern> </servlet-mapping> |
l JSP标签
(1)SUN公司允许用户开发自定义标签来封装页面的Java代码,目的是为了在JSP页面中不出现Java代码,同时sun公司在Jsp页面中也内置了一些标签,开发人员使用这些标签可以完成页面的一些常用业务逻辑;Jsp标签也被称为Jsp动作元素;
(2)常见Jsp标签
A、<jsp:include>标签:
1)<jsp:include>标签用于引入另外一个页面,引入的页面会和当前页面被翻译成两个Servlet,称为动态引入;
2)语法:
<jsp:include page="relativeURL | <%=expression%>" flush="true|false" /> |
3)include指令也可以实现引入,但是被引入的页面和当前页面被翻译成一个Servlet,被称为静态引入;
4)细节:
被引入的页面不能和当前页面的指定发生冲突,其中个pageEncoding和导包可以冲突;
被引入的页面可以使任意扩展名,例如html,但是必须遵循jsp语法规范;
被引入的页面不用写jsp模版元素。
B、<jsp:forward>标签:
1)<jsp:forward>标签用于把请求转发给另外一个资源;
2)语法:
<jsp:forward page="relativeURL | <%=expression%>" /> |
Page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式获得。
C、<jsp:param>标签:
1)当使用<jsp:param>和<jsp:forward>标签引入或请求转发其他资源时,可以嵌套<jsp:param>标签进行参数的传递;
2)语法1:
<jsp:include page="relativeURL | <%=expression%>"> <jsp:param name="parameterName" value="parameterValue|<%= expression %>" /> </jsp:include> |
语法2:
<jsp:forward page="relativeURL | <%=expression%>"> <jsp:param name="parameterName" value="parameterValue|<%= expression %>" /> </jsp:include> |
3)<jsp:param>标签的name属性用于指定参数名,alue属性用于指定参数值。在<jsp:include>和<jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数;
4)<jsp:param>标签来传递多个参数。
示例代码如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>JSP常用标签</title> </head> <body> <jsp:include page="/index.jsp"></jsp:include> <!-- 相当于pageContext.include("/index.jsp") 都是动态包含 --> <jsp:forward page="/servlet/IndexServlet"> <jsp:param value="username" name="xxxxx"/> </jsp:forward> </body> </html> |
l 如何查找JSP页面中的错误
在JSP页面中出错主要有三种情况:
(1)Jsp的语法格式有问题,这样会导致翻译后的Servlet编译不能通过,jsp引擎会报告导致不能翻译的代码所在的行号;
(2)Jsp页面语法没有问题,可以成功被翻译成Servlet,但是Servlet文件中语法有问题,此时会导致Servlet不能被编译,这样情况之下同样会报出jsp出错的行号;
(3)Jsp被翻译成Servlet,Servlet特被成功编译了,但是程序在运行期间出现了异常,此时,jsp引擎同样会报告jsp出错的行号,并进一步告知导致错误的原因,也就是Servlet中出错的行号。
4、JavaBean
l 什么是JavaBean?
(1)JavaBean是一个遵循特定写法的Java类;
(2)JavaBean的特点:
1)这个类必须有一个无参的构造方法;
2)属性必须私有化;
3)正对每个私有属性对外暴露对应的共有的getter和setter方法;
4)getter方法称为属性的读方法,setter方法称为属性的写方法。
l 为什么JavaBean要有这些规定?
(1)在Java中,通常使用JavaBean来封装数据;
(2)多数JavaBean封装工具都是通过反射来实现的;
(3)既然要反射JavaBean,就应该知道JavaBean有什么样的构造函数和哪些方法。
5、内省(Introspector)
l 什么是内省?
(1)内省是Java语言对Bean属性、事件的一种处理方式;
(2)简单的理解就是通过反射读写JavaBean的属性。
l 访问JavaBean属性的两种方式:
(1)直接调用bean的setXXX或者getXXX方法;
(2)通过内省技术访问(java.beans包内提供了内省的API),内省技术方位也提供了两种方式。
1)通过PropertyDescription类操作Bean的属性;
2)通过Introspector类获得Bean对象的BeanInfo,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获得某个属性对应的getter或setter 方法,然后通过反射机制来调用这些方法。
l 内省——Beanutils工具
Beanutils工具包的常用类:
(1)BeanUtils
(2)PropertyUtils
(3)CovertUtils.register(Converter convert,Class clazz)
(4)自定义转换器
6、在JSP中使用JavaBean
l JSP技术提供了三个关于JavaBean组件的动作元素,即JSP标签,它们分别是:
(1)<jsp:useBean>标签:用于在JSP页面中查找或实例化一个JavaBean组件;
1)<jsp:useBean>标签on关于在指定的域范围内查找指定明晨的JavaBean对象
如果存在则返回该JavaBean对象的引用,如果不存在则实例化一个新的JavaBean对象并将它指定的名称存储到指定的域范围中。
2)语法:
<jsp:useBean id="beanName" class="package.class" scope="page|request|session|application"/> |
id属性用于指定JavaBean实例对象的引用名称和其存储在域范围中的名称。
class属性用于指定JavaBean的完整类名(即必须带有包名)。
scope属性用于指定JavaBean实例对象所存储的域范围,其取值只能是page、request、session和application等四个值中的一个,其默认值是page。
3)标签体只在对象被创建时才执行。
示例代码如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <!-- usebean标签体只在对象被创建时才执行 --> <head> <title>jsp:useBean标签的使用</title> </head> <body> <jsp:useBeanid="person" class="cn.itcast.domain.Person" scope="page"></jsp:useBean> <%=person.getName()%> </body> </html> |
(2)<jsp:setProperty>标签:用于在JSP页面中设置一个JavaBean组件的属性;
1)<jsp:setProperty>标签用于设置JavaBean对象的属性;
2)语法:
<jsp:setProperty name="beanName" { property="propertyName" value="string " | property="propertyName" [ param="parameterName" ] | property= "*" }/> |
name属性用于指定JavaBean对象的名称。
property属性用于指定JavaBean实例对象的属性名。
value属性用于指定JavaBean对象的某个属性的值,value的值可以是字符串,也可以是表达式。为字符串时,该值会自动转化为JavaBean属性相应的类型,如果value的值是一个表达式,那么该表达式的计算结果必须与所要设置的JavaBean属性的类型一致。
param属性用于将JavaBean实例对象的某个属性值设置为一个请求参数值,该属性值同样会自动转换成要设置的JavaBean属性的类型。
(3)<jsp:getProperty>标签:用于在JSP页面中获取一个JavaBean组件的属性。
1)<jsp:getProperty>标签用于读取JavaBean对象的属性,也就是调用JavaBean对象的getter方法,然后将读取的属性值转换成字符串后插入进输出的响应正文中。
2)语法:
<jsp:getProperty name="beanInstanceName" property="PropertyName" /> |
name属性用于指定JavaBean实例对象的名称,其值应与<jsp:useBean>标签的id属性值相同。
property属性用于指定JavaBean实例对象的属性名。
3)如果一个JavaBean实例对象的某个属性的值为null,那么,使用<jsp:getProperty>标签输出该属性的结果将是一个内容为“null”的字符串。
示例代码如下所示:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@page import="cn.itcast.domain.Person"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>jsp:setProperty标签</title> </head> <body> <jsp:useBean id="person" class="cn.itcast.domain.Person" /> <!-- 手工为useBean属性赋值 --> <jsp:setProperty name="person" property="name" value="xxxxx" /> <%=person.getName()%><br /> <!-- 用请求参数为useBean属性赋值 http://localhost/day09/2.jsp?name=yyyy&age=23--> <jsp:setProperty property="name" name="person" param="name" /> <!-- 支持8种基本数据类型的转换(把客户机提供的字符串转换成8种基本属性,赋到bean的属性上) --> <jsp:setProperty property="age" name="person" param="age" /> <!--为生日赋值必须使用才行得通,若直接以字符串的方式提交会有错误--> <jsp:setProperty property="birthday" name="person" value="<%=new Date() %>" /> <%=person.getName()%><br /> <%=person.getAge()%><br /> <%=person.getBirthday()%><br /> <br/>---------<br/> <!-- 用所有的请求参数为useBean赋值 --> <jsp:setProperty property="*" name="person"/> <%=person.getName()%><br /> <%=person.getAge()%><br /> <!-- jsp:getProperty --><br/> <jsp:getProperty property="name" name="person"/> <jsp:getProperty property="age" name="person"/> </body> </html> |
7、EL表达式和JSTL标签快速入门
l EL表达式用于获取数据,在JSP页面中可使用${标识符}的形式,通知JSP引擎调用pageContext.findAttribute()方法,分别从page、request、session、application四个域中查找相应的对象找到则返回相应对象,找不到则返回”” (注意,不是null,而是空字符串)
l 结合JSTL标签,EL表达式也可轻松获取各种集合中的元素。
l EL表达式也可使用例如${1==1}的形式进行简单的逻辑判断。
l EL表达式支持二元表达式非常好用
语法:${user!=null?user.name : “”}
l JSTL标签库
(1)JSTL是sun公司开发的一套标签库,使用JSTL可以在页面中实现一些简单的逻辑,从而替换页面中的脚本代码
(2)在页面中使用JSTL标签需完成以下2个步骤:
1)导入jstl.jar和standerd.jar这两个JSTL的jar文件。
2)在JSP页面中使用<%@ taglib url=“” prifix=“” %>元素导入标签库。
3)JSTL标签库中常用标签:
<c:foreach var=“” items=“”> |
<c:if test=“”> |
l 综合案例:简单购物车项目以及用户管理系统,分别对应day10和day09_user
8、标签和简单标签
l 自定义标签简介:
(1)开发自定义标签的目的只有一个:移除jsp页面中的java代码
(2)实现方式:
1)编写一个实现Tag接口的Java类,并覆盖doStartTag方法,把jsp页面中的java代码写到doStartTag方法中
2)编写标签库描述符(tld)文件,在tld文件中对自定义标签进行描述
入门案例:
package cn.itcast.web.tag; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.Tag; import javax.servlet.jsp.tagext.TagSupport; publicclass TagDemo1 extends TagSupport { @Override publicint doStartTag() throws JspException { //Evaluate body into existing out stream. Valid return value for doStartTag //return Tag.EVAL_BODY_INCLUDE; //Skip body evaluation. Valid return value for doStartTag and doAfterBody. return Tag.SKIP_BODY; } } |
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib uri="http://www.itcast.cn" prefix="itcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP '1.jsp' starting page</title> </head> <body> 您的IP地址: <itcast:viewIP/> </body> </html> |
(3)tld文件中的四种标签体类型
1)empty 空元素 单标签
2)jsp 标签体不为空,可以写jsp脚本
3)scriptless 标签体不为空,不能写jsp脚本,但是可以写表达式和el
4)tagdependent 将原始内容提交给jsp引擎处理
(4)Jsp还提供了两个子接口分别是IterationTag和BodyTag
1)一般来讲通过继承实现类来实现接口
2)TagSupport 和BodyTagSupport
使用标签控制页面内容(标签体)是否输出:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <%@taglib uri="http://www.itcast.cn" prefix="itcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>使用标签控制页面内容(标签体)是否输出</title> </head> <body> <itcast:demo1> aaaa </itcast:demo1> </body> </html> |
publicclass TagDemo2 extends TagSupport { //Returns:indication of whether to continue evaluating the JSP page @Override publicint doEndTag() throws JspException { //Skip the rest of the page. Valid return value for doEndTag. //return Tag.SKIP_PAGE; //Continue evaluating the page. Valid return value for doEndTag(). return Tag.EVAL_PAGE; } } |
执行标签体执行5次
//执行标签体执行5次 publicclass TagDemo3 extends TagSupport { intx = 5; @Override publicint doStartTag() throws JspException { return Tag.EVAL_BODY_INCLUDE; } @Override publicint doAfterBody() throws JspException { x--; if(x>0){ return IterationTag.EVAL_BODY_AGAIN; }else{ return IterationTag.SKIP_BODY; } }} |
<html> <head> <title>执行标签体执行5次</title> </head> <body> <itcast:demo3> This is my JSP page. <br> </itcast:demo3> </body> </html> |
修改标签体(把标签体的字体改为大写)
//修改标签体(把标签体的字体改为大写) publicclass TagDemo4 extends BodyTagSupport { @Override publicint doStartTag() throws JspException { // Request the creation of new buffer, a BodyContent on which to // evaluate the body of this tag. Returned from doStartTag when it // implements BodyTag. This is an illegal return value for doStartTag // when the class does not implement BodyTag. return BodyTag.EVAL_BODY_BUFFERED; } @Override publicint doEndTag() throws JspException { // An encapsulation of the evaluation of the body of an action so it is // available to a tag handler BodyContent bc = this.getBodyContent();//得到标签体 String content = bc.getString(); content = content.toUpperCase(); try { this.pageContext.getOut().write(content); } catch (IOException e) { thrownew RuntimeException(e); } // Continue evaluating the page. Valid return value for doEndTag(). return Tag.EVAL_PAGE; } } |
<html> <head> <title>用标签修改jsp页面内容</title> </head> <body> <itcast:demo4>aaaaaaaa</itcast:demo4> </body> </html> |
Itcast.tld文件代码:
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>A tag library exercising SimpleTag handlers.</description> <tlib-version>1.0</tlib-version> <short-name>itcast</short-name> <uri>http://www.itcast.cn</uri> <tag> <name>viewIP</name> <tag-class>cn.itcast.web.tag.ViewIPTag</tag-class> <body-content>empty</body-content> </tag> <tag> <name>demo1</name> <tag-class>cn.itcast.web.tag.TagDemo1</tag-class> <body-content>JSP</body-content> </tag> <tag> <name>demo2</name> <tag-class>cn.itcast.web.tag.TagDemo2</tag-class> <body-content>empty</body-content> </tag> <tag> <name>demo3</name> <tag-class>cn.itcast.web.tag.TagDemo3</tag-class> <body-content>JSP</body-content> </tag> <tag> <name>demo4</name> <tag-class>cn.itcast.web.tag.TagDemo4</tag-class> <body-content>JSP</body-content> </tag> </taglib> |
9、简单标签
l 由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:
(1)setJspContext方法
用于把JSP页面的pageContext对象传递给标签处理器对象
(2)setParent方法
用于把父标签处理器对象传递给当前标签处理器对象
(3)getParent方法
用于获得当前标签的父标签处理器对象
(4)setJspBody方法
用于把代表标签体的JspFragment对象传递给标签处理器对象
(5)doTag方法
用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况。
l SimpleTag接口方法的执行顺序
当web容器开始执行标签时,会调用如下方法完成标签的初始
1)调用setJspContext方法,将pageContext对象传递给标签处理器对象。
2)调用setParent方法,将父标签处理器对象传递给这个标签处理器对象。
如果当前标签不存在父标签,web容器将不会调用此方法
3)根据标签的属性描述,容器将调用每个属性对应的setter方法将属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
4)调用setJspBody方法把代表标签体的JspFragment对象传递进来。
调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。
l JspFragment类
(1)javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。
(2)WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象。JspFragment类中只定义了两个方法,如下所示:
1) getJspContext方法:用于返回代表调用页面的JspContext对象.
2)publicabstract void invoke(java.io.Writer out) :用于执行JspFragment对象所代表的JSP代码片段,参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)
l invoke方法详解
(1)JspFragment.invoke方法可以说是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。
例如:
1)在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;
2)在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;
3)若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。
案例详解:
用简单标签控制是否执行标签体
publicclass SimpleTagDemo1 extends SimpleTagSupport { // Called by the container to invoke this tag. The implementation of this // method is provided by the tag library developer, and handles all tag // processing, body iteration, etc. @Override publicvoid doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); jf.invoke(this.getJspContext().getOut()); } } |
<%@taglib uri="/simpletag" prefix="sitcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>用简单标签控制是否执行标签体</title> </head> <body> <sitcast:demo1> aaaaaaa </sitcast:demo1> </body> </html> |
控制标签体的执行的次数
//控制标签体的执行的次数 publicclass SimpleTagDemo2 extends SimpleTagSupport { // Called by the container to invoke this tag. The implementation of this // method is provided by the tag library developer, and handles all tag // processing, body iteration, etc. @Override publicvoid doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); for(int i=0;i<5;i++){ jf.invoke(null); } //jf.invoke(this.getJspContext().getOut()); } } |
<%@taglib uri="/simpletag" prefix="sitcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>控制标签体的执行的次数</title> </head> <body> <sitcast:demo2> aaaaaaa </sitcast:demo2> </body> </html> |
修改标签体
//修改标签体 publicclass SimpleTagDemo3 extends SimpleTagSupport { // Called by the container to invoke this tag. The implementation of this // method is provided by the tag library developer, and handles all tag // processing, body iteration, etc. @Override publicvoid doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); StringWriter sw = new StringWriter(); jf.invoke(sw); String content = sw.toString(); content = content.toUpperCase(); this.getJspContext().getOut().write(content); } } |
<%@taglib uri="/simpletag" prefix="sitcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>修改标签体</title> </head> <body> <sitcast:demo3>aaaaaaa</sitcast:demo3> </body> </html> |
控制标签余下的jsp不执行
//控制标签余下的jsp不执行 publicclass SimpleTagDemo4 extends SimpleTagSupport { // Called by the container to invoke this tag. The implementation of this // method is provided by the tag library developer, and handles all tag // processing, body iteration, etc. @Override publicvoid doTag() throws JspException, IOException { // SkipPageException:Exception to indicate the calling page must cease // evaluation. thrownew SkipPageException(); } } |
<%@taglib uri="/simpletag" prefix="sitcast"%> <sitcast:demo4/> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>修改标签体</title> </head> <body>xxxxxxx</body> </html> |
Simpleitcast.tld
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>A tag library exercising SimpleTag handlers.</description> <tlib-version>1.0</tlib-version> <short-name>itcast</short-name> <uri>/simpletag</uri> <tag> <name>demo1</name> <tag-class>cn.itcast.web.simpletag.SimpleTagDemo1</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>demo2</name> <tag-class>cn.itcast.web.simpletag.SimpleTagDemo2</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>demo3</name> <tag-class>cn.itcast.web.simpletag.SimpleTagDemo3</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>demo4</name> <tag-class>cn.itcast.web.simpletag.SimpleTagDemo4</tag-class> <body-content>empty</body-content> </tag> </taglib> |
10、开发带属性的标签
l 为自定义标签增加属性,需要完成三个步骤:
在标签处理器类中声明一个属性
在标签处理器类中编写每个属性对应的setter方法
在TLD文件中描术标签的属性
l 注意:
标签属性的setter方法必须严格按照javabean的规范来书写
例如 : 标签有个url属性,在类中需要定义url属性,对应的setter方法为setUrl(String url)
如果标签声明了属性,web容器在调用doTag方法之前,调用对应的setter方法,将属性是值传递给该方法。
l 在TLD中描述标签属性
开发带属性的标签
//开发带属性的标签 publicclass SimpleTagDemo5 extends SimpleTagSupport { privateintcount; private Date date; publicvoid setCount(int count) { this.count = count; } publicvoid setDate(Date date) { this.date = date; } @Override publicvoid doTag() throws JspException, IOException { JspFragment jf = this.getJspBody(); this.getJspContext().getOut().write(date. for (int i = 0; i < 5; i++) { jf.invoke(null); } } } |
<%@taglib uri="/simpletag" prefix="sitcast"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>开发带属性的标签</title> </head> <body> <sitcast:demo5 count="9" date="<%=new Date()%>"> aaaaaaa</sitcast:demo5> </body> </html> |
对应的tld中的声明:
<tag> <name>demo5</name> <tag-class>cn.itcast.web.simpletag.SimpleTagDemo5</tag-class> <body-content>scriptless</body-content> <attribute> <name>count</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>date</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
11、开发常用自定义标签
l 开发防盗链标签
publicclass RefererTag extends SimpleTagSupport { private String site; private String page; publicvoid setPage(String page) { this.page = page; } publicvoid setSite(String site) { this.site = site; } @Override publicvoid doTag() throws JspException, IOException { PageContext pageContext = (PageContext) this.getJspContext(); HttpServletRequest request = (HttpServletRequest) pageContext .getRequest(); HttpServletResponse response = (HttpServletResponse) pageContext .getResponse(); // 1.得到来访问者referer String referer = request.getHeader("referer"); if (referer == null || !referer.startsWith(site)) { if (page.startsWith(request.getContextPath())) { response.sendRedirect(page); } elseif (page.startsWith("/")) { response.sendRedirect(request.getContextPath() + page); } else { response.sendRedirect(request.getContextPath() + "/" + page); } thrownew SkipPageException(); } } } |
<%@taglib uri="/example" prefix="e"%> <e:referer site="http://localhost" page="index.jsp" /> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>My JSP '1.jsp' starting page</title> </head> <body> This is fengjie diary.<br /> </body> </html |
<tag> <name>referer</name> <tag-class>cn.itcast.web.example.RefererTag</tag-class> <body-content>empty</body-content> <attribute> <name>site</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>page</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
l 开发<c:if>标签
publicclass IfTag extends SimpleTagSupport { privatebooleantest; publicvoid setTest(boolean test) { this.test = test; } @Override publicvoid doTag() throws JspException, IOException { if(test){ this.getJspBody().invoke(null); } } } |
<%@taglib uri="/example" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>if标签</title> </head> <body> <%session.setAttribute("user","aaaaaaa");%> <c:if test="${user==null}"> this is jsp page! </c:if> <c:if test="${user!=null}"> this is my jsp page! </c:if> </body> </html> |
<tag> <name>if</name> <tag-class>cn.itcast.web.example.IfTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
l 开发<c:if><c:else>标签choose when otherwise
publicclass ChooseTag extends SimpleTagSupport { privatebooleanisDo; publicboolean isDo() {//getter returnisDo; } publicvoid setDo(boolean isDo) {//setter this.isDo = isDo; } @Override publicvoid doTag() throws JspException, IOException { this.getJspBody().invoke(null); } } publicclass OtherWiseTag extends SimpleTagSupport { @Override publicvoid doTag() throws JspException, IOException { ChooseTag parent = (ChooseTag) this.getParent(); if(!parent.isDo()){ this.getJspBody().invoke(null); parent.setDo(true); } } } publicclass WhenTag extends SimpleTagSupport { privatebooleantest;
publicvoid setTest(boolean test) { this.test = test; } @Override publicvoid doTag() throws JspException, IOException { ChooseTag parent = (ChooseTag) this.getParent(); if (test && !parent.isDo()) { this.getJspBody().invoke(null); parent.setDo(true); } } } |
<%@taglib uri="/example" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>choose when otherwise标签</title> </head> <body> <c:choose> <c:when test="${user==null}">aaaa</c:when> <c:otherwise>bbbbb</c:otherwise> </c:choose> </body> </html> |
<tag> <name>choose</name> <tag-class>cn.itcast.web.example.ChooseTag</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>when</name> <tag-class>cn.itcast.web.example.WhenTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>test</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> <tag> <name>otherwise</name> <tag-class>cn.itcast.web.example.OtherWiseTag</tag-class> <body-content>scriptless</body-content> </tag> |
l 开发迭代标签itcast:foreach
方法一: <%@taglib uri="/example" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>foreach:list</title> </head> <body> <% List list = new ArrayList(); list.add("aaaa"); list.add("bbbb"); list.add("cccc"); request.setAttribute("list",list); %> <c:foreach var="str" items="${list}"> ${str} </c:foreach> </body> </html> |
publicclass ForeachTag extends SimpleTagSupport { private Object items; private String var; publicvoid setItems(Object items) { this.items = items; } publicvoid setVar(String var) { this.var = var; } @Override publicvoid doTag() throws JspException, IOException { List list = (List) items; Iterator it = list.iterator(); while(it.hasNext()){ Object value = it.next(); this.getJspContext().setAttribute(var, value); this.getJspBody().invoke(null); } } } |
<tag> <name>foreach</name> <tag-class>cn.itcast.web.example.ForeachTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
方法二:(此方法很经典,需要仔细体会!) publicclass ForeachTag2 extends SimpleTagSupport { private Object items; private String var; private Collection collection; publicvoid setItems(Object items) { this.items = items; if (items instanceof Collection) {// list set collection = (Collection) items; } if (items instanceof Map) { Map map = (Map) items; collection = map.entrySet();// set } if(items.getClass().isArray()){ this.collection = new ArrayList(); int length = Array.getLength(items); for(int i=0;i<length;i++){ Object value = Array.get(items, i); this.collection.add(value); } } /* if (items instanceof Object[]) { Object obj[] = (Object[]) items; collection = Arrays.asList(obj); } if(items instanceof int[]){ int arr[] = (int[]) items; this.collection = new ArrayList(); for(int i : arr){ this.collection.add(i); } } */ } publicvoid setVar(String var) { this.var = var; } @Override publicvoid doTag() throws JspException, IOException { // 做迭代 Iterator it = this.collection.iterator(); while (it.hasNext()) { Object value = it.next(); this.getJspContext().setAttribute(var, value); this.getJspBody().invoke(null); } } } |
<%@taglib uri="/example" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>foreach:list、map</title> </head> <body> <% List list = new ArrayList(); list.add("aaaa"); list.add("bbbb"); list.add("cccc"); request.setAttribute("list", list); %> <c:foreach2 var="str" items="${list}">${str}</c:foreach2> <br/>-------------------------<br/> <% Map map = new HashMap(); map.put("aa", "111"); map.put("bb", "222"); map.put("cc", "333"); map.put("dd", "444"); request.setAttribute("map", map); %> <c:foreach2 var="entry" items="${map}">${entry.key} = ${entry.value} </c:foreach2> <br/>-------------------------<br/> <% Integer num[] = { 1, 2, 3, 4 }; request.setAttribute("num", num); %> <c:foreach2 var="i" items="${num}">${i} </c:foreach2> <br/>-------------------------<br/> <% int arr[] = { 1, 2, 3, 4 }; request.setAttribute("arr", arr); %> <c:foreach2 var="i" items="${arr}">${i} </c:foreach2> </body> </html> |
<tag> <name>foreach2</name> <tag-class>cn.itcast.web.example.ForeachTag2</tag-class> <body-content>scriptless</body-content> <attribute> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> |
l 开发html转义标签
publicclass HtmlFilterTag extends SimpleTagSupport { @Override publicvoid doTag() throws JspException, IOException { StringWriter sw = new StringWriter(); JspFragment jf = this.getJspBody(); jf.invoke(sw); sw.getBuffer().toString(); String content = sw.getBuffer().toString(); content = filter(content); this.getJspContext().getOut().write(content); } // 下面的方法可以在D:\My // Program\tomcat6.0\webapps\examples\WEB-INF\classes\HTMLFilter类中获取 publicstatic String filter(String message) { if (message == null) return (null); char content[] = newchar[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case'<': result.append("<"); break; case'>': result.append(">"); break; case'&': result.append("&"); break; case'"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } } |
<%@taglib uri="/example" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>转义标签</title> </head> <body> <c:htmlfilter><a href="">点点 </a></c:htmlfilter> </body> </html> |
<tag> <name>htmlfilter</name> <tag-class>cn.itcast.web.example.HtmlFilterTag</tag-class> <body-content>scriptless</body-content> </tag> |
12、JSTL标签库
l <c:out>标签
<c:out> 标签用于输出一段文本内容到pageContext对象当前保存的“out”对象中。
l <c:set>标签
<c:set>标签用于把某一个对象存在指定的域范围内,或者设置Web域中的java.util.Map类型的属性对象或JavaBean类型的属性对象的属性。
l <c:remove>标签
<c:remove>标签用于删除各种Web域中的属性。
其语法格式如下:
<c:remove var="varName" [scope="{page|request|session|application}"] /> |
l <c:catch>标签
(1)<c:catch>标签用于捕获嵌套在标签体中的内容抛出的异常,其语法格式如下:
<c:catch [var="varName"]>nested actions</c:catch> |
(2)var属性用于标识<c:catch>标签捕获的异常对象,它将保存在page这个Web域中。
l <c:if>标签
<c:if test=“”>标签可以构造简单的“if-then”结构的条件表达式
l <c:choose>标签
<c:choose>标签用于指定多个条件选择的组合边界,它必须与<c:when>和<c:otherwise>标签一起使用。使用<c:choose>,<c:when>和<c:otherwise>三个标签,可以构造类似 “if-else if-else” 的复杂条件判断结构。
l <c:forEach>标签
<c:forEach>标签用于对一个集合对象中的元素进行循环迭代操作,或者按指定的次数重复迭代执行标签体中的内容。
l <c:param>标签
在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。<c:param>标签可以嵌套在<c:import>、<c:url>或<c:redirect>标签内,为这些标签所使用的URL地址附加参数。<c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处。
l <c:url>标签
<c:url>标签用于在JSP页面中构造一个URL地址,其主要目的是实现URL重写。
URL重写就是将会话标识号以参数形式附加在URL地址后面
l <c:redirect>标签
<c:redirect>标签用于将当前的访问请求转发或重定向到其他资源,它可以根据url属性所指定的地址,执行类似<jsp:forward>这个JSP标准标签的功能,将访问请求转发到其他资源;或执行response.sendRedirect()方法的功能,将访问请求重定向到其他资源。 也会进行url重写。
案例:
<%@ page language="java" import="java.util.*" import="cn.itcast.domain.*" pageEncoding="UTF-8"%> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>测试jstl中的c:out标签</title> </head> <body> <br />--------------------c:out------------------------------<br /> <%request.setAttribute("data", "bbbbbbb");%> <c:out value="${data}" default="aaaa" escapeXml="true"></c:out> <br />---c:set可以操纵各个域 JavaBean Map集合----<br /> <c:set var="data" value="xxx" scope="page" /> ${data} <% Map map = new HashMap(); request.setAttribute("map", map); %> <c:set property="dd" value="xxx" target="${map}" /> ${map.dd } <% Person p = new Person(); request.setAttribute("p", p); %> <c:set property="name" value="uuuu" target="${p}" /> ${p.name } <br />--------------------c:catch------------------------------<br /> <c:catch var="myex"> <%int x = 1 / 0;%> </c:catch> ${myex.message } <br />--------------------c:if-----------------------------<br /> <c:if var="aaa" test="${user==null}" scope="page"> aaaaa </c:if>${aaa } <br />--------------------c:foreach---------------------------<br /> <c:forEach var="num" begin="1" end="9" step="1"> ${num } </c:forEach> <br />--------------------c:foreach实现表格间色显示--------------<br /> <% List list = new ArrayList(); list.add("aaaa"); list.add("bbbb"); list.add("cccc"); request.setAttribute("list", list); %> <%--${status} 获取到了一个对象,这个对象记住了当前是第几次迭代 --%> <style> .odd {background-color: #FF99FF} .even {background-color: #FF6633} tr:hover {background-color: #0000FF} </style> <table border="1" width="20%"> <c:forEach var="str" items="${list}" varStatus="status"> <tr class="${status.count%2==0?'even':'odd' }"> <td>${str}</td> </tr> </c:forEach> </table> <br />--------------------c:url标签--------------<br /> <c:url var="url" value="/day11/xx"> <a href="url">购买</a> </c:url> </body> </html> |