简介
JSTL是一个JSP标准标签库,可以解决大部分问题,但是如果我们需要一些更特殊的功能,就需要自定义类似JSTL中标签的标签。如果EL表达式无法满足我们的需求,我们也可以自定义EL函数。
tld后缀的文件为标签库描述符,它是一个XML格式的文件,顾名思义,就是用来描述标签库的文件,编写自定义标签和EL函数时都需要用到。
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/web-jsptaglibrary_2_0.xsd"
version="2.0">
<!--标签库描述-->
<description>测试标签库</description>
<!--标签版版本-->
<tlib-version>1.0</tlib-version>
<!--标签库名称-->
<short-name>testTagLibrary</short-name>
<!--定义一个唯一标识当前版本标签库的公开的URI,taglib指令的uri属性必须等于该值。
如果没有将标签处理器和tld文件打成jar包,那么taglib指令的uri属性的值也可以本文件的相对路径,如/WEB-INF/test.tld-->
<uri>http://test/tag-library.com</uri>
<tag>
<!--标签描述-->
<description>经典标签库</description>
<!--标签的名称,引用标签时使用-->
<name>classic</name>
<!--标签处理器的完全限定类名,不能放到默认包下-->
<tag-class>com.ClassicCustomTag</tag-class>
<!--
指定标签有效的body类型。
JSP容器使用此值来验证标签body类型是否正确,并通过页面组合工具帮助页面作者提供有效的标签body。
tagdependent:标签的body由标签处理器解析,可以是另外一种语言,如SQL。
JSP:标签body可以包含jsp的语法,如jsp脚本元素,使用简单标签处理器不能设置为该值。
empty:标签body必须为空。
scriptless:标签body可以是静态HTML元素,EL表达式,jsp动作元素,但不允许出现jsp脚本元素
-->
<body-content>JSP</body-content>
<!--标签的属性-->
<attribute>
<!--属性描述-->
<description>权限</description>
<!--属性的名称-->
<name>permission</name>
<!--true/yes表示必须,false/no表示可选-->
<required>true</required>
<!--true/yes表示属性值支持jsp脚本元素和EL表达式,false/no表示不支持-->
<rtexprvalue>true</rtexprvalue>
<!--属性值的类型,如果rtexprvalue为false/no,则该值永远为java.lang.String-->
<type>java.lang.String</type>
</attribute>
</tag>
<!--用于提供标签库中要暴露给EL的每个函数的信息-->
<function>
<!--函数的唯一名称-->
<name>customPrint</name>
<!--函数的完全限定类名,不能放到默认包下,该类包含实现该函数的静态方法-->
<function-class>com.CustomElFunction</function-class>
<!--实现该函数的静态方法的签名,可以使用基本数据类型以及void。
如java.lang.String nickName(java.lang.String,int)-->
<function-signature>java.lang.String print(java.lang.String)</function-signature>
</function>
</taglib>
自定义标签
创建自定义标签分为两个步骤,编写标签处理器和注册标签。标签处理器在JSP2.0之后新增了一种实现方式。注册标签就是在tld文件中描述标签,并且把tld文件放到WEB-INF目录下。如果把标签处理器和tld文件打成jar包,那么tld文件应该放在jar包的META-INF目录下。
JSP2.0之前
JSP2.0之前的标签处理器要实现Tag、IterationTag、BodyTag等接口,称为典型标签处理器。
TagSupport
标签处理器继承TagSupport类,然后重写doStartTag、doAfterBody、doEndTag方法既可。
如果标签有属性,那么需要在标签处理器中定义该属性。
doStartTag方法是Tag接口定义的,在处理开始标签时调用,返回Tag.EVAL_BODY_INCLUDE表示执行标签body,返回Tag.SKIP_BODY表示不执行标签body。
doEndTag方法是Tag接口定义的,在处理结束标签时调用,返回Tag.EVAL_PAGE表示执行页面的剩余部分,返回Tag.SKIP_PAGE表示不执行页面的剩余部分。
doAfterBody是IterationTag接口定义的,在处理完标签body后调用,返回IterationTag.EVAL_BODY_AGAIN表示再次执行标签body,返回Tag.SKIP_BODY表示不再执行标签body。
执行顺序为,doStartTag->body->doAfterBody->doEndTag。
下面为一个简单的例子。
ClassicCustomTag.java
package com;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class ClassicCustomTag extends TagSupport {
private String permission;
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
@Override
public int doStartTag() throws JspException {
if ("query".equals(permission) || "add".equals(permission)
|| "delete".equals(permission) || "edit".equals(permission)) {
return SKIP_BODY;
} else {
return EVAL_BODY_INCLUDE;
}
}
@Override
public int doAfterBody() throws JspException {
return super.doAfterBody();
}
@Override
public int doEndTag() throws JspException {
return super.doEndTag();
}
}
test.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/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>测试标签库</description>
<tlib-version>1.0</tlib-version>
<short-name>testTagLibrary</short-name>
<uri>http://test/tag-library.com</uri>
<tag>
<description>经典标签库</description>
<name>classic</name>
<tag-class>com.ClassicCustomTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>权限</description>
<name>permission</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://test/tag-library.com" prefix="test" %>
<html>
<head>
<title>index</title>
</head>
<body>
<div>
<button onclick="alert(this.innerHTML);" <test:classic permission="query">disabled title="没有权限" </test:classic>>查询
</button>
</div>
<div>
<button onclick="alert(this.innerHTML);" <test:classic permission="edit">disabled title="没有权限" </test:classic>>编辑
</button>
</div>
<div>
<button onclick="alert(this.innerHTML);" <test:classic permission="export">disabled title="没有权限" </test:classic>>导出
</button>
</div>
</body>
</html>
BodyTagSupport
如果标签处理类需要和标签body交互如读取、重写标签body,那么需要继承BodyTagSupport类,BodyTagSupport继承了TagSupport,因此可以实现TagSupport的全部功能,因此可以重写doStartTag、doAfterBody、doEndTag,还可以重写getBodyContent、doInitBody。
此时doStartTag应该返回BodyTag.EVAL_BODY_BUFFERED,表示申请缓冲区,由setBodyContent方法得到的BodyContent对象来处理标签的body,否则getBodyContent返回null。body.getEnclosingWriter可以返回一个JspWriter对象,用于将响应发送到客户端。
doInitBody方法表示准备执行标签body时调用。
执行顺序为,doStartTag->doInitBody->body->doAfterBody->doEndTag。
下面为一个简单的例子。
ClassicCustomBodyTag.java
package com;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
public class ClassicCustomBodyTag extends BodyTagSupport {
private String permission;
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
@Override
public int doStartTag() throws JspException {
if ("query".equals(permission) || "add".equals(permission)
|| "delete".equals(permission) || "edit".equals(permission)) {
return EVAL_BODY_BUFFERED;
} else {
return SKIP_BODY;
}
}
@Override
public void doInitBody() throws JspException {
}
@Override
public int doAfterBody() throws JspException {
try {
BodyContent body = getBodyContent();
System.out.println("body:" + body.getString());
if ("query".equals(permission)) {
body.getEnclosingWriter().print("查询");
} else if ("add".equals(permission)) {
body.getEnclosingWriter().print("新增");
} else if ("delete".equals(permission)) {
body.getEnclosingWriter().print("删除");
} else {
body.getEnclosingWriter().print("无");
}
} catch (Exception e) {
e.printStackTrace();
}
return super.doAfterBody();
}
@Override
public int doEndTag() throws JspException {
return super.doEndTag();
}
}
test.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/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>测试标签库</description>
<tlib-version>1.0</tlib-version>
<short-name>testTagLibrary</short-name>
<uri>http://test/tag-library.com</uri>
<tag>
<description>经典标签库</description>
<name>classic</name>
<tag-class>com.ClassicCustomBodyTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<description>权限</description>
<name>permission</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://test/tag-library.com" prefix="test" %>
<html>
<head>
<title>index</title>
</head>
<body>
<div>
<button onclick="alert(this.innerHTML);"><test:classic permission="query">***</test:classic>
</button>
</div>
<div>
<button onclick="alert(this.innerHTML);"><test:classic permission="edit">***</test:classic>
</button>
</div>
</body>
</html>
JSP2.0之后
JSP2.0之后新增了SimpleTag接口,实现该接口的标签处理器称为简单标签处理器。
简单标签处理器必须有无参构造函数。JSP容器调用setJspContext方法传递JspContext对象,从JspContext对象中可以获取JspWriter,用于将响应发送到客户端。如果标签有body,JSP容器调用setJspBody方法传递JspFragment对象,如果没有body,就不会调用这个方法。最后JSP容器在标签执行时调用doTag方法,并且只调用一次。
JspFragment有两个方法,一个是获取JspContext对象,一个是执行标签body并且输出到指定的Writer。
SimpleTagSupport
该类实现了SimpleTag接口,可以方便开发。
下面是一个简单的例子。
SimpleCustomTag.java
package com;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
public class SimpleCustomTag extends SimpleTagSupport {
private String permission;
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
@Override
public void doTag() throws JspException, IOException {
try {
JspContext jspContext = getJspContext();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
OutputStreamWriter outputStreamWriter=new OutputStreamWriter(byteArrayOutputStream);
getJspBody().invoke(outputStreamWriter);
outputStreamWriter.close();
System.out.println("body:" + byteArrayOutputStream.toString());
if ("query".equals(permission)) {
jspContext.getOut().print("查询");
} else if ("add".equals(permission)) {
jspContext.getOut().print("新增");
} else if ("delete".equals(permission)) {
jspContext.getOut().print("删除");
} else {
jspContext.getOut().print("无");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
test.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/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>测试标签库</description>
<tlib-version>1.0</tlib-version>
<short-name>testTagLibrary</short-name>
<uri>http://test/tag-library.com</uri>
<tag>
<description>简单标签库</description>
<name>simple</name>
<tag-class>com.SimpleCustomTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<description>权限</description>
<name>permission</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
<type>java.lang.String</type>
</attribute>
</tag>
</taglib>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://test/tag-library.com" prefix="test" %>
<html>
<head>
<title>index</title>
</head>
<body>
<div>
<button onclick="alert(this.innerHTML);"><test:simple permission="query">***</test:simple>
</button>
</div>
<div>
<button onclick="alert(this.innerHTML);"><test:simple permission="edit">***</test:simple>
</button>
</div>
</body>
</html>
自定义EL函数
函数必须是静态方法。
下面为一个简单的例子。
CustomElFunction.java
package com;
public class CustomElFunction {
public static String print(String message) {
return "自定义打印:" + message;
}
}
test.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/web-jsptaglibrary_2_0.xsd"
version="2.0">
<description>测试标签库</description>
<tlib-version>1.0</tlib-version>
<short-name>testTagLibrary</short-name>
<uri>http://test/tag-library.com</uri>
<function>
<name>customPrint</name>
<function-class>com.CustomElFunction</function-class>
<function-signature>java.lang.String print(java.lang.String)</function-signature>
</function>
</taglib>
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib uri="http://test/tag-library.com" prefix="test" %>
<html>
<head>
<title>${test:customPrint("index.jsp")}</title>
</head>
<body>
</body>
</html>
- JSP2的自定义标签和方法
Jsp2的自定义标签 Jsp2 开发标签库的几个步骤: 开发自定义标签处理类. 建立一个*.tld文件,每个tld文件对应一个标签库,每个标签库可对应多个标签. 在jsp文件中使用自定义标签 空标签 ...
- 自定义一个EL函数
自定义一个EL函数 一般就是一下几个步骤,顺便提供一个工作常用的 案例: 1.编写一个java类,并编写一个静态方法(必需是静态方法),如下所示: public class DateTag { pri ...
- Jsp2.0自定义标签(第三天)——EL表达式的使用
1.提出问题: 我们经常会看到这样的jsp页面代码: 浏览器显示: 为什么会在页面输出:Hello World ,${per}究竟是如何找到“Hello World”的呢? 2.分析问题: 要想解决 ...
- Jsp2.0自定义标签(第一天)——一个简单的例子
今天是学习自定义标签的第一天 Jsp2.0以来,自定义标签的实现比传统标签的实现容易了很多,一般只要extends类SimpleSupport重写doTag()方法即可. 先看最简单的例子,输出一个H ...
- JSP2 的自定义标签
在 JSP 中开发标签库只需如下几个步骤 1.开发自定义标签处理类 2.建立一个 *.tld 文件,每个 *.tld 文件对应一个标签库,每个标签库可包含多个标签 3.在 JSP 文件中使用自定义标签 ...
- Jsp2.0自定义标签(第二天)——自定义循环标签
今天是学习自定义标签的第二天,主要是写一个自定义的循环标签. 先看效果图: 前台页面Jsp代码 <%@ page language="java" contentType=&q ...
- JSP2.0自定义标签
JSP1.0中可以通过继承TagSupport或者BodyTagSupport来实现自定义的tag处理方法. JSP2.0中也支持另外一种更为简单的自定tag的方法,那就是直接讲JSP代码保存成*.t ...
- EL函数以及自定义标签的应用
一.EL函数(调用普通类的静态方法) 编写步骤(自定义EL函数的编写步骤即自定义标签的编写步骤): ①编写一个普通的java类,提供一个静态方法,功能自定,例如下: package cn.wzbril ...
- JavaWeb(七):EL表达式、自定义标签和JSTL
一.EL表达式 语法 el.jsp <%@page import="java.util.Date"%> <%@page import="com.atgu ...
随机推荐
- 最强云硬盘来了,让AI模型迭代从1周缩短到1天
摘要:华为云擎天架构+ Flash-Native存储引擎+低时延CurreNET,数据存储和处理还有啥担心的? 虽然我们已经进入大数据时代,但多数企业数据利用率只有10%,数据的价值没有得到充分释放. ...
- Javascript数组与函数初识
1 - 数组 1.1 数组的概念 数组可以把一组相关的数据一起存放,并提供方便的访问(获取)方式. 数组是指一组数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素.数组是一种将一组数 ...
- windows环境安装vue-cli及webpack并创建vueJs项目
1. 安装node.js 2. 如果安装的是旧版本的 npm,可以通过 npm 命令来进行版本升级,命令如下: npm install npm -g npm网站服务器位于国外,所以经常下载缓慢或出现异 ...
- 封装Vue Element的dialog弹窗组件
我本没有想着说要封装一个弹窗组件,但有同行的朋友在问我,而且弹窗组件也确实在项目开发中用的比较多.思前想后,又本着样式统一且修改起来方便的原则,还是再为大家分享一个我所封装的弹窗组件吧. 其实,并不是 ...
- unity2D物理引擎之-Rigidbody 2D
虽然Rigidbody 2D大致上可以看成是Rigidbody的2D化,大部分功能也是一致的,但是还是有一些细节问题. 一些事项: 1.任何添加到同一个 GameObject身上或者其子物体身上的 2 ...
- C012:颠倒显示两位数
代码: #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { int original; do{ printf(&q ...
- C006:多项式求值 horner法则
代码: #include "stdafx.h" int _tmain(int argc, _TCHAR* argv[]) { float x; do{ printf("E ...
- vue安装pubsub-js 库的命令
1.查看pubsub-js 库是否已经存在该库命令: npm info pubsub-js 2.若不存在,则先安装pubsub-js 库,命令如下: npm install --save pubsub ...
- TG
telegram windous版 安装包 代理 安装好了,却没有网(ssr+PAC) 解决办法 汉化 在telegram 搜索 " zh_cn"
- Java多线程类FutureTask源码阅读以及浅析
FutureTask是一个具体的实现类,实现了RunnableFuture接口,RunnableFuture分别继承了Runnable和Future接口,因此FutureTask类既可以被线程执行,又 ...