一、简单标签(SimpleTag)

  由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java

实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:

  • setJspContext方法
  • setParent和getParent方法
  • setJspBody方法
  • doTag方法(非常重要),简单标签使用这个方法就可以完成所有的业务逻辑

二、SimpleTag方法介绍

2.1、setJspContext方法

  用于把JSP页面的pageContext对象传递给标签处理器对象

2.2、setParent方法

  用于把父标签处理器对象传递给当前标签处理器对象

2.3、getParent方法

用于获得当前标签的父标签处理器对象

2.4、setJspBody方法

  用于把代表标签体的JspFragment对象传递给标签处理器对象

2.5、doTag方法

  用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回​​Tag.SKIP_PAGE​​常量的情况。

三、SimpleTag接口方法的执行顺序

  当web容器开始执行标签时,会调用如下方法完成标签的初始化:

  1. WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。
  2. WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法。
  3. 如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
  4. 如果简单标签有标签体,WEB容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。
  5. 执行标签时WEB容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_02

四、开发简单标签实现页面逻辑

  SUN公司针对SimpleTag接口提供了一个默认的实现类SimpleTagSupport,SimpleTagSupport类中实现了SimpleTag接口的所有方法,因此我们可以编写一个类继承SimpleTagSupport类,然后根据业务需要再重写doTag方法。

4.1、控制jsp页面某一部分内容是否执行

 编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面不调用​​jspFrament.invoke​​方法即可。

示例代码如下:

SimpleTagDemo1.java

package me.gacl.web.simpletag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo1类继承SimpleTagSupport
*/
public class SimpleTagDemo1 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签体是否执行
*/
@Override
public void doTag() throws JspException, IOException {
//得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();

//得到jsp页面的的PageContext对象
//PageContext pageContext = (PageContext) jspFragment.getJspContext();
//调用JspWriter将标签体的内容输出到浏览器
//jspFragment.invoke(pageContext.getOut());

//将标签体的内容输出到浏览器
jspFragment.invoke(null);

}
}

  在WEB-INF目录下新建一个​​simpletag.tld​​​文件,然后在​​simpletag.tld​​文件中添加对该标签处理类的描述,如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_03

  simpletag.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用来添加对taglib(标签库)的描述 -->
<description>孤傲苍狼开发的SimpleTag自定义标签库</description>
<!--taglib(标签库)的版本号 -->
<tlib-version>1.0</tlib-version>
<short-name>GaclSimpleTagLibrary</short-name>
<!--
为自定义标签库设置一个uri,uri以/开头,/后面的内容随便写,如这里的/simpletag ,
在Jsp页面中引用标签库时,需要通过uri找到标签库
在Jsp页面中就要这样引入标签库:<%@taglib uri="/simpletag" prefix="gacl"%>
-->
<uri>/simpletag</uri>

<!--一个taglib(标签库)中包含多个自定义标签,每一个自定义标签使用一个tag标记来描述 -->
<!-- 一个tag标记对应一个自定义标签 -->
<tag>
<description>SimpleTag(简单标签)Demo1</description>
<!--
为标签处理器类配一个标签名,在Jsp页面中使用标签时是通过标签名来找到要调用的标签处理器类的
通过demo1就能找到对应的me.gacl.web.simpletag.SimpleTagDemo1类
-->
<name>demo1</name>
<!-- 标签对应的处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo1</tag-class>
<!--
tld文件中有四种标签体类型 :empty JSP scriptless tagdepentend
在简单标签(SampleTag)中标签体body-content的值只允许是empty和scriptless,不允许设置成JSP,如果设置成JSP就会出现异常
在传统标签中标签体body-content的值只允许是empty和JSP
如果标签体body-content的值设置成tagdepentend,那么就表示标签体里面的内容是给标签处理器类使用的,
例如:开发一个查询用户的sql标签,此时标签体重的SQL语句就是给SQL标签的标签处理器来使用的
<gacl:sql>SELECT * FROM USER</gacl:sql>
在这种情况下,sql标签的<body-content>就要设置成tagdepentend,tagdepentend用得比较少,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
</taglib>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%@taglib uri="/simpletag" prefix="gacl" %>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签体是否执行</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 demo1标签是带有标签体的,标签体的内容是"孤傲苍狼"这几个字符串--%>
<gacl:demo1>
孤傲苍狼
</gacl:demo1>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_04

4.2、控制jsp页面内容重复执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面重复调用​​jspFrament.invoke​​方法即可。

示例代码如下:

SimpleTagDemo2.java

package me.gacl.web.simpletag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo2类继承SimpleTagSupport
*/
public class SimpleTagDemo2 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签执行5次
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
for (int i = 0; i < 5; i++) {
// 将标签体的内容输出到浏览器
jspFragment.invoke(null);
}
}
}

  在WEB-INF目录下的​​simpletag.tld​​文件中添加对该标签处理类的描述,如下:

<tag>
<!-- 标签名 -->
<name>demo2</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%@taglib uri="/simpletag" prefix="gacl" %>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签体执行5次</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 --%>
<gacl:demo2>
孤傲苍狼<p/>
</gacl:demo2>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_05

 

4.3、修改jsp页面内容输出

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法调用jspFrament.invoke方法时,让执行结果写一个自定义的缓冲中即可,然后开发人员可以取出缓冲的数据修改输出。

示例代码如下:

SimpleTagDemo3.java

package me.gacl.web.simpletag;

import java.io.IOException;
import java.io.StringWriter;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo3类继承SimpleTagSupport
*/
public class SimpleTagDemo3 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,修改标签体里面的内容,将标签体的内容转换成大写
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
StringWriter sw = new StringWriter();
//将标签体的内容写入到sw流中
jspFragment.invoke(sw);
//获取sw流缓冲区的内容
String content = sw.getBuffer().toString();
content = content.toUpperCase();
PageContext pageContext = (PageContext) this.getJspContext();
//将修改后的content输出到浏览器中
pageContext.getOut().write(content);
}
}

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

<tag>
<!-- 标签名 -->
<name>demo3</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo3</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签修改jsp页面内容输出</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 --%>
<gacl:demo3>
gacl_xdp
</gacl:demo3>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java web_06

 

4.4、控制整个jsp页面是否执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法抛出SkipPageException异常即可,jsp收到这个异常,将忽略标签余下jsp页面的执行。

示例代码如下:

SimpleTagDemo4.java

package me.gacl.web.simpletag;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo4类继承SimpleTagSupport
*/
public class SimpleTagDemo4 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签余下的Jsp不执行
*/
@Override
public void doTag() throws JspException, IOException {
//抛出一个SkipPageException异常就可以控制标签余下的Jsp不执行
throw new SkipPageException();
}
}

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

复制代码

<tag>
<!-- 标签名 -->
<name>demo4</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo4</tag-class>
<!-- 标签体允许的内容 ,empty表示该标签没有标签体-->
<body-content>empty</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签余下的Jsp不执行</title>
</head>

<body>
<h1>孤傲苍狼</h1>
<%--在jsp页面中使用自定义标签 --%>
<gacl:demo4/>
<!-- 这里的内容位于 <gacl:demo4/>标签后面,因此不会输出到页面上 -->
<h1>白虎神皇</h1>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_07

 

五、简单标签开发的一些注意细节

5.1、标签类编写细节

  开发标签类时,不要直接去实现SimpleTag接口,而是应该继承SimpleTagSupport类,SimpleTagSupport类是SimpleTag接口的一个默认实现类,通过继承SimpleTagSupport类,就可以直接使用SimpleTagSupport类已经实现的那些方法,如果SimpleTagSupport类的方法实现不满足业务要求,那么就可以根据具体的业务情况将相应的方法进行重写。

5.2、tld文件中标签体类型设置细节

  我们开发好一个简单标签后,需要在tld文件中添加对该标签的描述,例如:

<tag>
<!-- 标签名 -->
<name>demo2</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  开发好一个标签后,在tld文件中使用​​<tag>​​来描述一个标签,描述的内容包括标签名(name),标签处理器类(tag-class),标签体的内容(body-content)。

  tld文件中有四种标签体(body-content)类型 :empty、JSP、scriptless、tagdependent

简单标签标签体的细节注意问题:
在简单标签(SampleTag)中标签体body-content的值只允许是empty、scriptless、tagdependent,不允许设置成JSP,如果设置成JSP就会出现异常:

The TLD for the class me.gacl.web.simpletag.SimpleTagDemo1 specifies an invalid body-content (JSP) for a SimpleTag

  body-content的值如果设置成empty,那么就表示该标签没有标签体,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体中的内容不可以是​​<%java代码%>​​,例如:

<gacl:xdpdemo1>
<%
//嵌套在标签体中的java代码
int i= 0;
%>
孤傲苍狼
</gacl:xdpdemo1>

  否则运行标签时就会出现如下错误:

Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here

  jsp标签技术出现的目的就是为了移除在jsp页面上编写的java代码的,如果在jsp标签中允许出现java代码,那么就违背了jsp标签技术设计时的初衷了,所以在简单标签的标签体中是不允许出现java代码的。

传统标签标签体的细节注意问题:

在传统标签中标签体body-content的值允许是empty、JSP、scriptless、tagdependent,body-content的值如果是设置成JSP,那么表示该标签是有标签体的,并且标签体的内容可以是任意的,包括java代码,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体的内容不能是java代码

  如果传统标签和简单标签的标签体body-content的值设置成tagdependent,那么就表示标签体里面的内容是给标签处理器类使用的,tagdependent用得比较少,了解一下即可

5.3、tld文件中标签库的uri设置细节

  如果在一个项目中使用或者开发了多个标签库,例如:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_08

  那么标签库的uri不能设置成相同的,否则在Jsp页面中通过uri引用标签库时就不知道引用哪一个标签库了,如果真的有那么巧,两个标签库的uri是刚好一样的,如下图所示:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java web_09

  那么在jsp页面中引用标签库时如果“<%@taglib uri=”/gacl” prefix=”gacl” %>“这样引用,那么就无法判断当前引用的标签库到底是gacl.tld标签库中的标签还是simpletag.tld标签库中的标签,因为两个标签库的uri刚好都是​​"/gacl"​​,在两个标签库的引用uri一样的情况下,为了能够在jsp中区别到底引用的是哪个标签库,可以换一种引用方式:<%@taglib uri=”要引用的标签库的tld文件目录” prefix=”gacl”%>,使用taglib指令引入标签库时,taglib指令的uri属性指定为标签库的tld文件目录,这样就可以区别开了,例如:

  引用gacl.tld标签库:​​<%@taglib uri="/WEB-INF/gacl.tld" prefix="gacl"%>、​

  引用simpletag.tld标签库:​​<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>​

  所以当在项目中引用了多个标签库,如果标签库的uri刚好是一样的,就可以用这种方式解决。

六、简单标签开发步骤总结

  1、编写一个类继承SimpleTagSupport类,然后根据业务需要重写SimpleTagSupport类中已经实现了的方法,一般情况下只需要重写doTag()方法即可。

  2、在WEB-INF目录下创建一个tld文件,在tld文件中添加对该标签的描述。tld文件不一定放在WEB-INF目录下,也可以放在别的目录,习惯是放在WEB-INF目录下。

七、JspFragment类介绍

  javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。
  
  **WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象。**JspFragment类中只定义了两个方法,如下所示:

  getJspContext方法

    用于返回代表调用页面的JspContext对象.
  

public abstract void invoke(java.io.Writer out)

    用于执行JspFragment对象所代表的JSP代码片段,参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果 传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)

7.1、invoke方法详解

  JspFragment.invoke方法是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。例如:
  
  在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;
  在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;

若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。

八、开发带属性的标签

  自定义标签可以定义一个或多个属性,这样,在JSP页面中应用自定义标签时就可以设置这些属性的值,通过这些属性为标签处理器传递参数信息,从而提高标签的灵活性和复用性。
  要想让一个自定义标签具有属性,通常需要完成两个任务:

  1. 在标签处理器中编写每个属性对应的setter方法
  2. 在TLD文件中描术标签的属性
      为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收 JSP页面调用自定义标签时传递进来的属性值。 例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法。
      
      在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法,为标签设置属性。

8.1、开发带属性的标签范例

范例1:通过标签的属性控制标签体的执行次数

示例代码如下:

SimpleTagDemo5.java

package me.gacl.web.simpletag;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo5类继承SimpleTagSupport
* 通过标签的属性控制标签体的执行次数
*/
public class SimpleTagDemo5 extends SimpleTagSupport {

/**
* 定义标签的属性
*/
private int count;

/**count属性对应的set方法
* @param count
*/
public void setCount(int count) {
this.count = count;
}

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,通过标签的属性控制标签体的执行次数
*/
@Override
public void doTag() throws JspException, IOException {
for (int i = 0; i < count; i++) {
this.getJspBody().invoke(null);
}
}

}

  在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:

<tag>
<!-- 标签名 -->
<name>demo5</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo5</tag-class>
<!-- 标签体允许的内容-->
<body-content>scriptless</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的count属性</description>
<!-- 标签的count属性 -->
<name>count</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

  在jsp页面引入标签库并使用自定义标签

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>通过标签的属性控制标签体的执行次数</title>
</head>

<body>
<%--在jsp页面中使用自定义标签,标签有一个count属性 --%>
<gacl:demo5 count="2">
<h1>孤傲苍狼</h1>
</gacl:demo5>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_10

  如果标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型,但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的

范例2:标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值

示例代码如下:

SimpleTagDemo6.java

package me.gacl.web.simpletag;

import java.io.IOException;
import java.util.Date;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo6类继承SimpleTagSupport
* 标签的属性说明
*/
public class SimpleTagDemo6 extends SimpleTagSupport {

/**
* 定义标签的属性
*/
private Date date;

/**date属性对应的set方法
* @param date
*/
public void setDate(Date date) {
this.date = date;
}

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,输出date属性值
*/
@Override
public void doTag() throws JspException, IOException {
this.getJspContext().getOut().write(date.toLocaleString());
}
}

  在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:

<tag>
<!-- 标签名 -->
<name>demo6</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo6</tag-class>
<!-- 标签体允许的内容-->
<body-content>empty</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的date属性</description>
<!-- 标签的date属性,复合数据类型 -->
<name>date</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

  在jsp页面引入标签库并使用自定义标签

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>如果标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值</title>
</head>

<body>
<%--
在jsp页面中使用自定义标签,标签有一个date属性 ,是一个复合数据类型
如果标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型
但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的,
这里将一个字符串赋值给demo6标签的date属性,在运行标签时就会出现如下错误:
Unable to convert string "1988-05-07" to class "java.util.Date" for attribute "date":
Property Editor not registered with the PropertyEditorManager
<gacl:demo6 date="1988-05-07">
</gacl:demo6>
--%>
<%--如果一定要给标签的复合属性赋值,那么可以采用表达式的方式给复合属性赋值,如下所示: --%>

<%
Date d = new Date();
request.setAttribute("date", d);
%>

<gacl:demo6 date="${date}"/>
<hr/>
<gacl:demo6 date="<%=new Date()%>"/>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_11

8.2、tld文件中用于描述标签属性的元素说明

  ​​<tag>​​​元素的​​<attribute>​​​子元素用于描述自定义标签的一个属性,自定义标签所具有的每个属性都要对应一个​​<attribute>​​元素

例如:

<tag>
<!-- 标签名 -->
<name>demo5</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo5</tag-class>
<!-- 标签体允许的内容-->
<body-content>scriptless</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的count属性</description>
<!-- 标签的count属性 -->
<name>count</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

​<attribute>​​元素的子元素说明:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_12

  到此,简单标签的开发技术就算是全部讲完了,在下一篇博文中会编写一些自定义标签的案例来加深自定标签技术的学习和理解。


一、简单标签(SimpleTag)

  由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java

实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:

  • setJspContext方法
  • setParent和getParent方法
  • setJspBody方法
  • doTag方法(非常重要),简单标签使用这个方法就可以完成所有的业务逻辑

二、SimpleTag方法介绍

2.1、setJspContext方法

  用于把JSP页面的pageContext对象传递给标签处理器对象

2.2、setParent方法

  用于把父标签处理器对象传递给当前标签处理器对象

2.3、getParent方法

用于获得当前标签的父标签处理器对象

2.4、setJspBody方法

  用于把代表标签体的JspFragment对象传递给标签处理器对象

2.5、doTag方法

  用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回​​Tag.SKIP_PAGE​​常量的情况。

三、SimpleTag接口方法的执行顺序

  当web容器开始执行标签时,会调用如下方法完成标签的初始化:

  1. WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。
  2. WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法。
  3. 如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
  4. 如果简单标签有标签体,WEB容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。
  5. 执行标签时WEB容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_02

四、开发简单标签实现页面逻辑

  SUN公司针对SimpleTag接口提供了一个默认的实现类SimpleTagSupport,SimpleTagSupport类中实现了SimpleTag接口的所有方法,因此我们可以编写一个类继承SimpleTagSupport类,然后根据业务需要再重写doTag方法。

4.1、控制jsp页面某一部分内容是否执行

 编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面不调用​​jspFrament.invoke​​方法即可。

示例代码如下:

SimpleTagDemo1.java

package me.gacl.web.simpletag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo1类继承SimpleTagSupport
*/
public class SimpleTagDemo1 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签体是否执行
*/
@Override
public void doTag() throws JspException, IOException {
//得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();

//得到jsp页面的的PageContext对象
//PageContext pageContext = (PageContext) jspFragment.getJspContext();
//调用JspWriter将标签体的内容输出到浏览器
//jspFragment.invoke(pageContext.getOut());

//将标签体的内容输出到浏览器
jspFragment.invoke(null);

}
}

  在WEB-INF目录下新建一个​​simpletag.tld​​​文件,然后在​​simpletag.tld​​文件中添加对该标签处理类的描述,如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_03

  simpletag.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用来添加对taglib(标签库)的描述 -->
<description>孤傲苍狼开发的SimpleTag自定义标签库</description>
<!--taglib(标签库)的版本号 -->
<tlib-version>1.0</tlib-version>
<short-name>GaclSimpleTagLibrary</short-name>
<!--
为自定义标签库设置一个uri,uri以/开头,/后面的内容随便写,如这里的/simpletag ,
在Jsp页面中引用标签库时,需要通过uri找到标签库
在Jsp页面中就要这样引入标签库:<%@taglib uri="/simpletag" prefix="gacl"%>
-->
<uri>/simpletag</uri>

<!--一个taglib(标签库)中包含多个自定义标签,每一个自定义标签使用一个tag标记来描述 -->
<!-- 一个tag标记对应一个自定义标签 -->
<tag>
<description>SimpleTag(简单标签)Demo1</description>
<!--
为标签处理器类配一个标签名,在Jsp页面中使用标签时是通过标签名来找到要调用的标签处理器类的
通过demo1就能找到对应的me.gacl.web.simpletag.SimpleTagDemo1类
-->
<name>demo1</name>
<!-- 标签对应的处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo1</tag-class>
<!--
tld文件中有四种标签体类型 :empty JSP scriptless tagdepentend
在简单标签(SampleTag)中标签体body-content的值只允许是empty和scriptless,不允许设置成JSP,如果设置成JSP就会出现异常
在传统标签中标签体body-content的值只允许是empty和JSP
如果标签体body-content的值设置成tagdepentend,那么就表示标签体里面的内容是给标签处理器类使用的,
例如:开发一个查询用户的sql标签,此时标签体重的SQL语句就是给SQL标签的标签处理器来使用的
<gacl:sql>SELECT * FROM USER</gacl:sql>
在这种情况下,sql标签的<body-content>就要设置成tagdepentend,tagdepentend用得比较少,了解一下即可
-->
<body-content>scriptless</body-content>
</tag>
</taglib>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%@taglib uri="/simpletag" prefix="gacl" %>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签体是否执行</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 demo1标签是带有标签体的,标签体的内容是"孤傲苍狼"这几个字符串--%>
<gacl:demo1>
孤傲苍狼
</gacl:demo1>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_04

4.2、控制jsp页面内容重复执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面重复调用​​jspFrament.invoke​​方法即可。

示例代码如下:

SimpleTagDemo2.java

package me.gacl.web.simpletag;

import java.io.IOException;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo2类继承SimpleTagSupport
*/
public class SimpleTagDemo2 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签执行5次
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
for (int i = 0; i < 5; i++) {
// 将标签体的内容输出到浏览器
jspFragment.invoke(null);
}
}
}

  在WEB-INF目录下的​​simpletag.tld​​文件中添加对该标签处理类的描述,如下:

<tag>
<!-- 标签名 -->
<name>demo2</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%@taglib uri="/simpletag" prefix="gacl" %>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签体执行5次</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 --%>
<gacl:demo2>
孤傲苍狼<p/>
</gacl:demo2>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_05

 

4.3、修改jsp页面内容输出

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法调用jspFrament.invoke方法时,让执行结果写一个自定义的缓冲中即可,然后开发人员可以取出缓冲的数据修改输出。

示例代码如下:

SimpleTagDemo3.java

package me.gacl.web.simpletag;

import java.io.IOException;
import java.io.StringWriter;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo3类继承SimpleTagSupport
*/
public class SimpleTagDemo3 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,修改标签体里面的内容,将标签体的内容转换成大写
*/
@Override
public void doTag() throws JspException, IOException {
// 得到代表jsp标签体的JspFragment
JspFragment jspFragment = this.getJspBody();
StringWriter sw = new StringWriter();
//将标签体的内容写入到sw流中
jspFragment.invoke(sw);
//获取sw流缓冲区的内容
String content = sw.getBuffer().toString();
content = content.toUpperCase();
PageContext pageContext = (PageContext) this.getJspContext();
//将修改后的content输出到浏览器中
pageContext.getOut().write(content);
}
}

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

<tag>
<!-- 标签名 -->
<name>demo3</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo3</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签修改jsp页面内容输出</title>
</head>

<body>

<%--在jsp页面中使用自定义标签 --%>
<gacl:demo3>
gacl_xdp
</gacl:demo3>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java web_06

 

4.4、控制整个jsp页面是否执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法抛出SkipPageException异常即可,jsp收到这个异常,将忽略标签余下jsp页面的执行。

示例代码如下:

SimpleTagDemo4.java

package me.gacl.web.simpletag;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo4类继承SimpleTagSupport
*/
public class SimpleTagDemo4 extends SimpleTagSupport {

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,控制标签余下的Jsp不执行
*/
@Override
public void doTag() throws JspException, IOException {
//抛出一个SkipPageException异常就可以控制标签余下的Jsp不执行
throw new SkipPageException();
}
}

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

复制代码

<tag>
<!-- 标签名 -->
<name>demo4</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo4</tag-class>
<!-- 标签体允许的内容 ,empty表示该标签没有标签体-->
<body-content>empty</body-content>
</tag>

  在jsp页面中导入并使用自定义标签,如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>用简单标签控制标签余下的Jsp不执行</title>
</head>

<body>
<h1>孤傲苍狼</h1>
<%--在jsp页面中使用自定义标签 --%>
<gacl:demo4/>
<!-- 这里的内容位于 <gacl:demo4/>标签后面,因此不会输出到页面上 -->
<h1>白虎神皇</h1>
</body>
</html>

  运行效果如下:

 

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_07

 

五、简单标签开发的一些注意细节

5.1、标签类编写细节

  开发标签类时,不要直接去实现SimpleTag接口,而是应该继承SimpleTagSupport类,SimpleTagSupport类是SimpleTag接口的一个默认实现类,通过继承SimpleTagSupport类,就可以直接使用SimpleTagSupport类已经实现的那些方法,如果SimpleTagSupport类的方法实现不满足业务要求,那么就可以根据具体的业务情况将相应的方法进行重写。

5.2、tld文件中标签体类型设置细节

  我们开发好一个简单标签后,需要在tld文件中添加对该标签的描述,例如:

<tag>
<!-- 标签名 -->
<name>demo2</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
<!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
<body-content>scriptless</body-content>
</tag>

  开发好一个标签后,在tld文件中使用​​<tag>​​来描述一个标签,描述的内容包括标签名(name),标签处理器类(tag-class),标签体的内容(body-content)。

  tld文件中有四种标签体(body-content)类型 :empty、JSP、scriptless、tagdependent

简单标签标签体的细节注意问题:
在简单标签(SampleTag)中标签体body-content的值只允许是empty、scriptless、tagdependent,不允许设置成JSP,如果设置成JSP就会出现异常:

The TLD for the class me.gacl.web.simpletag.SimpleTagDemo1 specifies an invalid body-content (JSP) for a SimpleTag

  body-content的值如果设置成empty,那么就表示该标签没有标签体,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体中的内容不可以是​​<%java代码%>​​,例如:

<gacl:xdpdemo1>
<%
//嵌套在标签体中的java代码
int i= 0;
%>
孤傲苍狼
</gacl:xdpdemo1>

  否则运行标签时就会出现如下错误:

Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here

  jsp标签技术出现的目的就是为了移除在jsp页面上编写的java代码的,如果在jsp标签中允许出现java代码,那么就违背了jsp标签技术设计时的初衷了,所以在简单标签的标签体中是不允许出现java代码的。

传统标签标签体的细节注意问题:

在传统标签中标签体body-content的值允许是empty、JSP、scriptless、tagdependent,body-content的值如果是设置成JSP,那么表示该标签是有标签体的,并且标签体的内容可以是任意的,包括java代码,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体的内容不能是java代码

  如果传统标签和简单标签的标签体body-content的值设置成tagdependent,那么就表示标签体里面的内容是给标签处理器类使用的,tagdependent用得比较少,了解一下即可

5.3、tld文件中标签库的uri设置细节

  如果在一个项目中使用或者开发了多个标签库,例如:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_标签库_08

  那么标签库的uri不能设置成相同的,否则在Jsp页面中通过uri引用标签库时就不知道引用哪一个标签库了,如果真的有那么巧,两个标签库的uri是刚好一样的,如下图所示:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java web_09

  那么在jsp页面中引用标签库时如果“<%@taglib uri=”/gacl” prefix=”gacl” %>“这样引用,那么就无法判断当前引用的标签库到底是gacl.tld标签库中的标签还是simpletag.tld标签库中的标签,因为两个标签库的uri刚好都是​​"/gacl"​​,在两个标签库的引用uri一样的情况下,为了能够在jsp中区别到底引用的是哪个标签库,可以换一种引用方式:<%@taglib uri=”要引用的标签库的tld文件目录” prefix=”gacl”%>,使用taglib指令引入标签库时,taglib指令的uri属性指定为标签库的tld文件目录,这样就可以区别开了,例如:

  引用gacl.tld标签库:​​<%@taglib uri="/WEB-INF/gacl.tld" prefix="gacl"%>、​

  引用simpletag.tld标签库:​​<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>​

  所以当在项目中引用了多个标签库,如果标签库的uri刚好是一样的,就可以用这种方式解决。

六、简单标签开发步骤总结

  1、编写一个类继承SimpleTagSupport类,然后根据业务需要重写SimpleTagSupport类中已经实现了的方法,一般情况下只需要重写doTag()方法即可。

  2、在WEB-INF目录下创建一个tld文件,在tld文件中添加对该标签的描述。tld文件不一定放在WEB-INF目录下,也可以放在别的目录,习惯是放在WEB-INF目录下。

七、JspFragment类介绍

  javax.servlet.jsp.tagext.JspFragment类是在JSP2.0中定义的,它的实例对象代表JSP页面中的一段符合JSP语法规范的JSP片段,这段JSP片段中不能包含JSP脚本元素。
  
  **WEB容器在处理简单标签的标签体时,会把标签体内容用一个JspFragment对象表示,并调用标签处理器对象的setJspBody方法把JspFragment对象传递给标签处理器对象。**JspFragment类中只定义了两个方法,如下所示:

  getJspContext方法

    用于返回代表调用页面的JspContext对象.
  

public abstract void invoke(java.io.Writer out)

    用于执行JspFragment对象所代表的JSP代码片段,参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果 传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)

7.1、invoke方法详解

  JspFragment.invoke方法是JspFragment最重要的方法,利用这个方法可以控制是否执行和输出标签体的内容、是否迭代执行标签体的内容或对标签体的执行结果进行修改后再输出。例如:
  
  在标签处理器中如果没有调用JspFragment.invoke方法,其结果就相当于忽略标签体内容;
  在标签处理器中重复调用JspFragment.invoke方法,则标签体内容将会被重复执行;

若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的。

八、开发带属性的标签

  自定义标签可以定义一个或多个属性,这样,在JSP页面中应用自定义标签时就可以设置这些属性的值,通过这些属性为标签处理器传递参数信息,从而提高标签的灵活性和复用性。
  要想让一个自定义标签具有属性,通常需要完成两个任务:

  1. 在标签处理器中编写每个属性对应的setter方法
  2. 在TLD文件中描术标签的属性
      为自定义标签定义属性时,每个属性都必须按照JavaBean的属性命名方式,在标签处理器中定义属性名对应的setter方法,用来接收 JSP页面调用自定义标签时传递进来的属性值。 例如属性url,在标签处理器类中就要定义相应的setUrl(String url)方法。
      
      在标签处理器中定义相应的set方法后,JSP引擎在解析执行开始标签前,也就是调用doStartTag方法前,会调用set属性方法,为标签设置属性。

8.1、开发带属性的标签范例

范例1:通过标签的属性控制标签体的执行次数

示例代码如下:

SimpleTagDemo5.java

package me.gacl.web.simpletag;

import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo5类继承SimpleTagSupport
* 通过标签的属性控制标签体的执行次数
*/
public class SimpleTagDemo5 extends SimpleTagSupport {

/**
* 定义标签的属性
*/
private int count;

/**count属性对应的set方法
* @param count
*/
public void setCount(int count) {
this.count = count;
}

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,通过标签的属性控制标签体的执行次数
*/
@Override
public void doTag() throws JspException, IOException {
for (int i = 0; i < count; i++) {
this.getJspBody().invoke(null);
}
}

}

  在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:

<tag>
<!-- 标签名 -->
<name>demo5</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo5</tag-class>
<!-- 标签体允许的内容-->
<body-content>scriptless</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的count属性</description>
<!-- 标签的count属性 -->
<name>count</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

  在jsp页面引入标签库并使用自定义标签

<%@ page language="java" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>通过标签的属性控制标签体的执行次数</title>
</head>

<body>
<%--在jsp页面中使用自定义标签,标签有一个count属性 --%>
<gacl:demo5 count="2">
<h1>孤傲苍狼</h1>
</gacl:demo5>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_10

  如果标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型,但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的

范例2:标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值

示例代码如下:

SimpleTagDemo6.java

package me.gacl.web.simpletag;

import java.io.IOException;
import java.util.Date;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

/**
* @author gacl
* SimpleTagSupport类实现了SimpleTag接口,
* SampleTagDemo6类继承SimpleTagSupport
* 标签的属性说明
*/
public class SimpleTagDemo6 extends SimpleTagSupport {

/**
* 定义标签的属性
*/
private Date date;

/**date属性对应的set方法
* @param date
*/
public void setDate(Date date) {
this.date = date;
}

/* 简单标签使用这个方法就可以完成所有的业务逻辑
* @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
* 重写doTag方法,输出date属性值
*/
@Override
public void doTag() throws JspException, IOException {
this.getJspContext().getOut().write(date.toLocaleString());
}
}

  在WEB-INF目录下的tld文件中添加对该标签的描述,如下所示:

<tag>
<!-- 标签名 -->
<name>demo6</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo6</tag-class>
<!-- 标签体允许的内容-->
<body-content>empty</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的date属性</description>
<!-- 标签的date属性,复合数据类型 -->
<name>date</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

  在jsp页面引入标签库并使用自定义标签

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%--在jsp页面中导入自定义标签库 --%>
<%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>
<%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>
<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>
<!DOCTYPE HTML>
<html>
<head>
<title>如果标签接收的属性值是一个复合数据类型,该如何给标签的属性赋值</title>
</head>

<body>
<%--
在jsp页面中使用自定义标签,标签有一个date属性 ,是一个复合数据类型
如果标签的属性值是8种基本数据类型,那么在JSP页面在传递字符串时,JSP引擎会自动转换成相应的类型
但如果标签的属性值是复合数据类型,那么JSP引擎是无法自动转换的,
这里将一个字符串赋值给demo6标签的date属性,在运行标签时就会出现如下错误:
Unable to convert string "1988-05-07" to class "java.util.Date" for attribute "date":
Property Editor not registered with the PropertyEditorManager
<gacl:demo6 date="1988-05-07">
</gacl:demo6>
--%>
<%--如果一定要给标签的复合属性赋值,那么可以采用表达式的方式给复合属性赋值,如下所示: --%>

<%
Date d = new Date();
request.setAttribute("date", d);
%>

<gacl:demo6 date="${date}"/>
<hr/>
<gacl:demo6 date="<%=new Date()%>"/>
</body>
</html>

  运行效果如下:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_java_11

8.2、tld文件中用于描述标签属性的元素说明

  ​​<tag>​​​元素的​​<attribute>​​​子元素用于描述自定义标签的一个属性,自定义标签所具有的每个属性都要对应一个​​<attribute>​​元素

例如:

<tag>
<!-- 标签名 -->
<name>demo5</name>
<!-- 标签处理器类-->
<tag-class>me.gacl.web.simpletag.SimpleTagDemo5</tag-class>
<!-- 标签体允许的内容-->
<body-content>scriptless</body-content>

<!-- 标签的属性描述 -->
<attribute>
<description>描述标签的count属性</description>
<!-- 标签的count属性 -->
<name>count</name>
<required>true</required>
<!-- rtexprvalue用来指示标签的属性值是否可以是一个表达式,
一般设置为true,true就表示允许标签的属性值可以是一个表达式-->
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>

​<attribute>​​元素的子元素说明:

  

深入分析JavaWeb 24 -- jsp2.X自定义标签开发进阶_jsp页面_12

  到此,简单标签的开发技术就算是全部讲完了,在下一篇博文中会编写一些自定义标签的案例来加深自定标签技术的学习和理解。