文章目录
一、基础
1.1 目的
目的:取代JSP文件中的脚本代码,避免JSP 脚本和 HTML 代码混杂。
取代前 | 取代后 |
---|---|
难以阅读。 | 易于阅读 |
维护成本高。 | 便于维护 |
美工人员难以参与开发。 | 便于协同开发 |
1.2 含义
设计自己的JSP标签库,使用自己的标签。
1.3 步骤
第一步:开发自定义标签处理类;
第二步:建立一个 myFirstTagLib.tld 文件。每个 myFirstTagLib.tld 文件对应一个标签库,每个标签库对应多个标签;
第三步:在 JSP 文件中使用自定义标签。
1.4 自定义标签继承结构
二、修改标签体内容案例
2.1 开发自定义标签处理类
注意:如果标签类包含属性,每个属性都有对应的 getter 和 setter 方法。
2.1.1 BodyTagSupport开发
这是JSP 1.1 版中,不推荐,原因太烦锁。推荐JSP 2 ,SimpleTagSupport开发。
实现:extends BodyTagSupport,重写doEndTag()。
常量 | 含义 |
---|---|
EVAL_BODY_INCLUDE | 执行标签体里的内容。 |
SKIP_BODY | 跳过标签体里的内容。 |
EVAL_PAGE | 继续后面操作 |
SKIP_PAGE | 跳过后面操作 |
BodyTagSupportDemo.java
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
// 功能:修改标签体的内容。
public class BodyTagSupportDemo extends BodyTagSupport{
// 存储标签体中的内容。
private BodyContent bodyContent;
// 重写setBodyContent()方法。
// 目的:接收外部传来的标签体,以便后续对变迁体的操作。
@Override
public void setBodyContent(BodyContent b){
this.bodyContent = b;
}
// 设置结束标签的方法。
@Override
public int doEndTag() throws JspException,IOException{
//获取标签体中的内容。
String content = bodyContent.getString();
// 向控制台输出
System.out.println(content);
//修改后的内容。
String newStr = "这是新的内容";
//获取页面输出流。
JspWriter jspWriter = bodyContent.getEnclosingWriter();
// 将修改后的内容再写回到页面中。
jspWriter.write(newStr);
// 继续后面操作。
return EVAL_PAGE;
}
}
2.1.2 SimpleTagSupport开发
推荐JSP 2 ,SimpleTagSupport开发。
实现:extends SimpleTagSupport,重写doTag()。
注意: doTag() 方法负责生成页面内容。
SimpleTagSupport.java
package com.my.EL;
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import javax.servlet.jsp.tagext.SimpleTagSupport;
// 功能:修改标签体的内容。
public class SimpleTagSupportDemo3 extends SimpleTagSupport{
@Override
public void doTag() throws JspException,IOException{
// ========获得标签体中的内容。===============
JspFragment jspFragment = getJspBody(); // 放入缓冲区中。
// 2.但JspFragment类对象无法取到缓冲区的数据。因此使用StringWright类对象获取。
StringWriter stringWriter = new StringWriter();
// 3.JspFragment类对象转移标签体内容至StringWright类对象中。A.invoke(B),将A中内容写入到B中。映射关系,同时变化。
jspFragment.invoke(stringWriter);
// 4.获取标签体内容。
String content = stringWriter.toString();
// ================更改标签体中的内容。===============
// 5. 更改标签体内容。
content = "更改标签体内容后的内容";
// ================输出标签体中的内容。======================
// 6.输出新内容。先写如PageContext类对象中。
PageContext pageContext = (PageContext) getJspContext();
// 7.输出到网页上。利用输出流类。
JspWriter out = pageContext.getOut();
// 8. 输出到网页上。
out.write(content);
}
}
2.2 建立 TLD 文件
TLD : Tag Library Definition。
每个 TLD 文件对应一个标签库,一个标签库中可包含多个标签,TLD 文件也称为标签库定义文件。
路径:放置到 Web 应用的 WEB-INF/ 路径,或 WEB-INF 的任意子路径下。
myFirstTagLib.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>SimpleTagLibrary</short-name>
<!-- 非常重要,它指定该标签库的 URI,相当于指定该标签库的唯一标识。 -->
<uri>firstTag</uri>
<!-- 自定义标签1:输出当前日期 -->
<tag>
<!-- 非常重要。该标签的名称,JSP 页面中就是根据该名称来使用此标签的。 -->
<name>nowFormat</name>
<!-- 非常重要。指定标签的处理类,指定了标签由哪个 Java 类来处理。 -->
<tag-class>com.my.EL.NowFormat</tag-class>
<!-- 非常重要。指定标签体内容。 -->
<body-content>empty</body-content>
<!--
empty:禁止标签中写入内容。
scriptless:允许写入内容(文本、EL表达式或JSP动作)。
JSP:允许出现脚本代码。不推荐。
tagdependent:标签体内容可以写入到<boy-content>,由自定义标签体类处理,即传参。不推荐。 -->
</tag>
<!-- 自定义标签2: 控制标签体内容是否显示。-->
<tag>
<name>skipBodyOrEvalBodyInclude</name>
<tag-class>com.my.EL.SkipBodyOrEvalBodyIncludeTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 自定义标签3 :控制标签结束后,结后标签后面的代码是否执行。-->
<tag>
<name>skipPageOrEvalPage</name>
<tag-class>com.my.EL.SkipPageOrEvalPageTag</tag-class>
<body-content>empty</body-content>
</tag>
<!-- 自定义标签4 -->
<tag>
<name>dbConnectionTag</name>
<tag-class>com.my.EL.DBConnectionTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>driver</name>
<!-- 设置是否为必须属性 -->
<required>true</required>
<!-- 设置属性中属性值是否可以用EL表达式 -->
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>url</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>user</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>password</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>sql</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 自定义标签5 :控制标签体的内容是否重复执行。循环结构。-->
<tag>
<name>iterationTag</name>
<tag-class>com.my.EL.IterationTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>var</name>
<required>true</required>
</attribute>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 自定义标签6 :修改标签体内容,输出新内容。-->
<tag>
<name>bodyTagSupportTag</name>
<tag-class>com.my.EL.BodyTagSupportDemo</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 自定义标签7 -->
<tag>
<name>simpleTagSupportDemoTag</name>
<tag-class>com.my.EL.SimpleTagSupportDemo</tag-class>
<body-content>empty</body-content>
</tag>
<!-- 自定义标签8 -->
<tag>
<name>simpleTagSupportDemo2Tag</name>
<tag-class>com.my.EL.SimpleTagSupportDemo2</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 自定义标签9 -->
<tag>
<name>simpleTagSupportDemo3Tag</name>
<tag-class>com.my.EL.SimpleTagSupportDemo3</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 自定义标签10 -->
<tag>
<name>IfTag</name>
<tag-class>com.my.EL.IfTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 自定义标签11 -->
<tag>
<name>chooseTag</name>
<tag-class>com.my.EL.ChooseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
<!-- 自定义标签12 -->
<tag>
<name>whenTag</name>
<tag-class>com.my.EL.WhenTag</tag-class>
<body-content>scriptless</body-content>
<attribute>
<name>test</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<!-- 自定义标签13 -->
<tag>
<name>otherWiseTag</name>
<tag-class>com.my.EL.OtherWiseTag</tag-class>
<body-content>scriptless</body-content>
</tag>
</taglib>
2.3 使用标签库
在 JSP 页面中确定指定标签需要 2 点:
1、标签库 URI:确定使用哪个标签库。
2、标签名:确定使用哪个标签。
使用步骤:
1、导入标签库:使用 taglib 编译指令导入标签库,就是将标签库和指定前缀关联起来。
<%@ taglib uri="tagliburi" prefix="tagPrefix" %>
2、使用标签:在 JSP 页面中使用自定义标签。
<!-- 有标签体时 -->
<tagPrefix:tagName tagAttribute=”tagValue” … >
<tagBody/>
</tagPrefix:tagName>
<!-- 没有标签体时 -->
<tagPrefix:tagName tagAttribute=”tagValue” … />
JSTL_createLable.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.text.*,java.util.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<%@ taglib uri="firstTag" prefix="my" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>自定义标签</title>
</head>
<body>
<h1>自定义标签 </h1><hr/>
<!-- 原始设置日期格式的方法 -->
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Date now = new Date();
String nowString = sdf.format(now);
%>
现在时间:<%=nowString %><br/>
<!-- 原始设置日期格式的方法 -->
我现在时间:<my:nowFormat /><br/>
<hr/><h1>设置标签体是否显示。</h1>
<my:skipBodyOrEvalBodyInclude>
<h1>88888888888888</h1>
</my:skipBodyOrEvalBodyInclude>
<%-- <hr/><h1>设置标签后的内容是否显示</h1>
<my:skipPageOrEvalPage />
<h3>本种方法skipPageOrEvalPage,本文字不显示。</h3> --%>
<hr/><h1>设置标签的属性。</h1>
静态创建
<my:dbConnectionTag user="root" password="123456" url="jdbc:mysql://localhost:3306/shopping" driver="com.mysql.jdbc.Driver" sql="SELECT `name` FROM items"/>
动态创建
<%
String user = "root";
String password = "123456";
String url = "jdbc:mysql://localhost:3306/shopping";
String driver = "com.mysql.jdbc.Driver";
String sql = "SELECT `name` FROM items";
// 封装到pageContext对象中,以便后续EL表达式调用。
pageContext.setAttribute("user", user);
pageContext.setAttribute("password", password);
pageContext.setAttribute("url", url);
pageContext.setAttribute("driver", driver);
pageContext.setAttribute("sql", sql);
//
%>
<my:dbConnectionTag user="${user}" password="${password}" url="${url}" driver="${driver}" sql="${sql}"/>
<hr/><h1>设置标签。迭代器</h1>
<%
String[] person = {"Tom","Jack","Lily"};
// 封装到pageContext对象中,以便后续EL表达式调用。
pageContext.setAttribute("person",person);
%>
<my:iterationTag items="${person}" var="name">${name}</my:iterationTag>
<hr/><h1>修改标签体中的内容。</h1>
<my:bodyTagSupportTag>hello World!</my:bodyTagSupportTag>
</body>
</html>
三、带属性的标签案例
3.1 开发自定义标签处理类
注意:带属性标签必须为每个属性提供对应的 setter 和 getter 方法。
DBConnectionTag.java:
package com.my.EL;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
// 带属性的标签。
public class DBConnectionTag extends SimpleTagSupport {
//标签的属性
private String driver;
private String url;
private String password;
private String user;
private String sql;
//执行数据库访问的对象
private Connection conn = null;
private PreparedStatement ps = null;
private ResultSet rs = null;
private ResultSetMetaData rsmd = null;
//标签属性的getter和setter方法
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
// 重写doTag()方法。
@Override
public void doTag() throws JspException {
try {
//注册驱动
Class.forName(driver);
//获取数据库连接
conn = DriverManager.getConnection(url,user,password);
//创建PreparedStatement对象
ps = conn.prepareStatement(sql);
// 创建ResultSet对象
rs = ps.executeQuery();
// 创建ResultSetMetaData对象
rsmd = rs.getMetaData();
// 获取列数目
int columnCount = rsmd.getColumnCount();
//获取页面输出流
Writer out = getJspContext().getOut();
//在页面输出表格
out.write("<table border='1' bgColor='9999cc' width='400'>");
if (rs != null) {
//遍历结果集
while (rs.next()) {
out.write("<tr>");
//逐列输出查询到的数据
for (int i = 1 ; i <= columnCount ; i++ ){
out.write("<td>");
out.write(rs.getString(i));
out.write("</td>");
}
out.write("</tr>");
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
3.2 建立 TLD 文件
<attribute>的子元素:
1、name:设置属性名。
2、required:该属性是否为必需。
3、fragment | rtexprvalue:该属性是否支持 JSP 脚本、表达式等动态内容。
<tag>
<!-- 定义标签名 -->
<name>dbConnectionTag</name>
<!-- 指定标签处理类 -->
<tag-class>com.my.EL.DBConnectionTag</tag-class>
<!-- 定义标签体为空 -->
<body-content>empty</body-content>
<!-- 配置标签属性:driver -->
<attribute>
<name>driver</name>
<!-- 设置是否为必须属性 -->
<required>true</required>
<!-- 设置属性中属性值是否可以用EL表达式 -->
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 配置标签属性:url -->
<attribute>
<name>url</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 配置标签属性:user -->
<attribute>
<name>user</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 配置标签属性:password -->
<attribute>
<name>password</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<!-- 配置标签属性:sql -->
<attribute>
<name>sql</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
3.3 使用标签库
静态创建:
<my:dbConnectionTag user="root" password="123456" url="jdbc:mysql://localhost:3306/shopping" driver="com.mysql.jdbc.Driver" sql="SELECT `name` FROM items"/>
动态创建:
<%
String user = "root";
String password = "123456";
String url = "jdbc:mysql://localhost:3306/shopping";
String driver = "com.mysql.jdbc.Driver";
String sql = "SELECT `name` FROM items";
// 封装到pageContext对象中,以便后续EL表达式调用。
pageContext.setAttribute("user", user);
pageContext.setAttribute("password", password);
pageContext.setAttribute("url", url);
pageContext.setAttribute("driver", driver);
pageContext.setAttribute("sql", sql);
%>
<my:dbConnectionTag user="${user}" password="${password}" url="${url}" driver="${driver}" sql="${sql}"/>
四、带标签体的标签案例
带标签体的标签,可以在标签内嵌入其他内容(包括静态的 HTML 内容和动态的 JSP 内容),通常用于完成一些逻辑运算,例如判断和循环等。
4.1 开发自定义标签处理类
IterationTag.java:
package com.my.EL;
import javax.servlet.jsp.JspContext;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
// 带标签体的标签
public class IterationTag extends SimpleTagSupport{
// 标签属性。
private String collection; // 集合。
private String item; // 集合的元素。
public String getCollection() {
return collection;
}
public void setCollection(String collection) {
this.collection = collection;
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
@Override
public void doTag() throws JspException,IOException{
// 创建JspContext实例对象。
JspContext jspContext = getJspContext();
// //从page scope中获取属性名为collection的集合
Collection itemList = (Collection)jspContext.getAttribute(collection);
//遍历集合
for (Object s : itemList){
//将集合的元素设置到page 范围
getJspContext().setAttribute(item, s );
//输出标签体
getJspBody().invoke(null);
}
}
}
4.2 建立 TLD 文件
<tag>
<name>iterator</name>
<tag-class>com.my.EL.IteratorTag</tag-class>
<body-content>scriptless</body-content>
<!-- 配置标签属性:collection -->
<attribute>
<name>collection</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
<!-- 配置标签属性:item -->
<attribute>
<name>item</name>
<required>true</required>
<fragment>true</fragment>
</attribute>
</tag>
4.3 使用标签库
<%
//创建一个List对象
List<String> a = new ArrayList<String>();
a.add("hello");
a.add("world");
a.add("java");
//将List对象放入page范围内
pageContext.setAttribute("a" , a);
%>
<table border="1" bgcolor="aaaadd" width="300">
<!-- 使用迭代器标签,对a集合进行迭代 -->
<mytag:iterator collection="a" item="item">
<tr>
<td>${pageScope.item}</td>
<tr>
</mytag:iterator>
</table>