实现步骤
- 实现自定义标签的处理类
继承javax.servlet.jsp.tagext.SimpleTagSupport,并重写doTag方法 - 建立标签库配置文件
- 在jsp中使用自定义标签
一个简单的标签
文件目录结构
src/main/java |---- cn.ljl.javaweb.demo.jsp2taglib.servlet |---- HelloWorldServlet.java |---- cn.ljl.javaweb.demo.jsp2taglib |---- HelloWordTag.javasrc/main/webapp |---- WEB-INF |---- jsp2taglib |---- demo.tld |---- demo/jsp2taglib |---- helloword.jsp
文件解析:
- HelloWorldServlet.java
一个Servlet,简单的forward到helloword.jsp;其实直接请求jsp也可以,只是考虑到MVC的原则添加了这个Servlet - HelloWorldTag.java
自定义标签的处理类 - demo.tld
标签库配置文件,可以放在WEB-INF/下,或其任意子路径下。 - helloword.jsp
这个jsp使用了自定义的标签
文件内容
HelloWordTag.java
package cn.ljl.javaweb.demo.jsp2taglib;import java.io.IOException;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.SimpleTagSupport;/** * 一个简单的标签实现类,在页面输出一行hello word内容. * @author lijinlong * */public class HelloWordTag extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { getJspContext().getOut().print("Hello World" + new java.util.Date()); }}
说明:
- 标签处理类应该继承javax.servlet.jsp.tagext.SimpleTagSupport
- 重写doTag方法,这个方法负责往页面写内容
demo.tld
<??><taglib ="http://java.sun.com/ ="http://www.w3.org/2001/ xsi:schemaLocation="http://java.sun.com/ version="2.0"> <tlib-version>1.0</tlib-version> <short-name>demo</short-name> <uri>http://www.lijinlong.cn/demotaglib</uri> <tag> <description>输出Hello World,这个标签没有属性和标签体</description> <name>helloword</name> <tag-class>cn.ljl.javaweb.demo.jsp2taglib.HelloWordTag</tag-class> <body-content>empty</body-content> </tag></taglib>
tld是Tag Library Definition的缩写。每个tld文件定义一个标签库,一个标签库可以包含多个标签。
根元素taglib定义标签库:
- tlib-version
标签库的版本号,对程序没有多少作用 - short-name
标签库的名称,对程序没有多少作用 - uri
标签库的唯一标识,这个很重要;JSP要根据这个来引入指定的标签库
子元素tag用于定义标签,可以出现多次。tag元素常用的子元素如下:
- description
标签的描述性信息 - name
标签的名称,jsp中根据这个来指定使用的标签 - tag-class
标签的处理类 - body-content
标签体的类型 - dynamic-attributes
定义动态属性
body-content的类型有:
- tagdependent
指定标签处理类自己负责处理标签体 - empty
只作为空标签来使用(比如<br />就是一个空标签) - scriptless
标签体中不能含有JSP脚本。可以是静态的html、表达式语言。 - JSP
可以使用JSP脚本 - JSP2.0不再支持这个类型
helloword.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%@ taglib uri="http://www.lijinlong.cn/demotaglib" prefix="demo"%><html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>自定义标签 - Hello World</title></head><body><h2>显示自定义标签 - Hello World的内容:</h2><demo:helloword /></body></html>
使用自定义标签需要做两件事:
- 使用taglib编译指令,导入指定uri的标签库,并绑定到指定的prefix。
- 使用标签,以prefix:tagname的形式
HelloWordServlet.java
package cn.ljl.javaweb.demo.jsp2taglib.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name="helloWorldServlet", urlPatterns = {"/demo/jsp2taglib/helloword"})public class HelloWorldServlet extends HttpServlet { private static final long serialVersionUID = -297481388442806334L; private static final String VIEW_HELLOWORLD = "/demo/jsp2taglib/helloword.jsp"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher(VIEW_HELLOWORLD).forward(req, resp); } }
测试
支持属性的标签
定义一个支持属性的标签 - add,它接收2个参数,并输出求和的结果。
文件目录结构
src/main/java |---- cn.ljl.javaweb.demo.jsp2taglib.servlet |---- AddServlet.java |---- cn.ljl.javaweb.demo.jsp2taglib |---- AddTag.javasrc/main/webapp |---- WEB-INF |---- jsp2taglib |---- demo.tld |---- demo/jsp2taglib |---- add.jsp
文件内容
AddTag.java
package cn.ljl.javaweb.demo.jsp2taglib;import java.io.IOException;import javax.servlet.jsp.JspException;import javax.servlet.jsp.JspWriter;import javax.servlet.jsp.tagext.SimpleTagSupport;/** * 用来演示带属性的标签.<br/> * 实现求和的功能. * @author lijinlong * */public class AddTag extends SimpleTagSupport { private int arg1; private int arg2; public int getArg1() { return arg1; } public void setArg1(int arg1) { this.arg1 = arg1; } public int getArg2() { return arg2; } public void setArg2(int arg2) { this.arg2 = arg2; } @Override public void doTag() throws JspException, IOException { int result = arg1 + arg2; String template = "the result of arg1 + arg2 is:%s"; String content = String.format(template, String.valueOf(result)); JspWriter out = getJspContext().getOut(); out.print(content); }}
注意:需要将哪一个field作为标签的属性,就要为这个field添加对应的getter和setter。
demo.tld
这里只给出添加的内容。
<tag> <description>实现求和的功能 - 用来演示带属性的标签</description> <name>add</name> <tag-class>cn.ljl.javaweb.demo.jsp2taglib.AddTag</tag-class> <body-content>empty</body-content> <attribute> <name>arg1</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>arg2</name> <required>true</required> <fragment>true</fragment> </attribute> </tag>
attribute元素为标签添加属性,可以出现多次,每一次声明一个属性:
- name
属性的名称 - required
属性是否必需,true 或 false - fragment
是否支持jsp脚本、表达式等动态内容,true 或 false
add.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%@ taglib uri="http://www.lijinlong.cn/demotaglib" prefix="demo"%><html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>自定义标签 - Add</title></head><body><h2>求3+5的和:</h2><demo:add arg1="3" arg2="5" /></body></html>
AddServlet.java
package cn.ljl.javaweb.demo.jsp2taglib.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name = "addServlet", urlPatterns = { "/demo/jsp2taglib/add" })public class AddServlet extends HttpServlet { private static final long serialVersionUID = 3526679109725991549L; private static final String VIEW_ADD = "/demo/jsp2taglib/add.jsp"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher(VIEW_ADD).forward(req, resp); }}
测试
支持标签体的标签
定义一个支持标签体的标签 - iterator,它将对一个字符串列表进行迭代,每一次迭代都动态输出标签体的内容。
文件目录结构
src/main/java |---- cn.ljl.javaweb.demo.jsp2taglib.servlet |---- IteratorServlet.java |---- cn.ljl.javaweb.demo.jsp2taglib |---- IteratorTag.javasrc/main/webapp |---- WEB-INF |---- jsp2taglib |---- demo.tld |---- demo/jsp2taglib |---- iterator.jsp
文件内容
IteratorTag.java
package cn.ljl.javaweb.demo.jsp2taglib;import java.io.IOException;import java.util.List;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.SimpleTagSupport;/** * 实现迭代输出器的功能.<br> * 用来演示带有标签体的标签. * * @author lijinlong * */public class IteratorTag extends SimpleTagSupport { private List<String> collection; private String eleAttrName; public List<String> getCollection() { return collection; } public void setCollection(List<String> collection) { this.collection = collection; } public String getEleAttrName() { return eleAttrName; } public void setEleAttrName(String eleAttrName) { this.eleAttrName = eleAttrName; } @Override public void doTag() throws JspException, IOException { for (String ele : collection) { getJspContext().setAttribute(eleAttrName, ele); getJspBody().invoke(null); } }}
注意:
- 在标签处理类中,可以通过getJsoContext()来设置/获取共享变量。
- 输出标签体的内容:getJspBody().invoke(null);
demo.tld
这里仅给出新增的内容:
<tag> <description>迭代输出所有字符串</description> <name>iterator</name> <tag-class>cn.ljl.javaweb.demo.jsp2taglib.IteratorTag</tag-class> <body-content>scriptless</body-content> <attribute> <name>collection</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>eleAttrName</name> <required>true</required> <fragment>true</fragment> </attribute> </tag>
注意:为了支持标签体,body-content要设置成scriptless。
iterator.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%@ taglib uri="http://www.lijinlong.cn/demotaglib" prefix="demo"%><html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>自定义标签 - iterator</title></head><body> <h2>我们给您的建议:</h2> <ol> <demo:iterator collection="${requestScope.adviceList}" eleAttrName="item"> <li>${pageScope.item}</li> </demo:iterator> </ol></body></html>
说明:
- 这里从request scope取得的adviceList是在Servlet中放进去的。
- iterator标签设计为,将集合中的每一个元素设置到page scope的属性中,其name由eleAttrName指定。
- 我们的jsp非常的简洁。
IteratorServlet.java
package cn.ljl.javaweb.demo.jsp2taglib.servlet;import java.io.IOException;import java.util.ArrayList;import java.util.List;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name = "iteratorServlet", urlPatterns = { "/demo/jsp2taglib/iterator" })public class IteratorServlet extends HttpServlet { private static final long serialVersionUID = 4201830343915991973L; private static final String VIEW_SHOW = "/demo/jsp2taglib/iterator.jsp"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<String> advices = new ArrayList<String>(); advices.add("早睡早起。"); advices.add("坚持锻炼身体。"); advices.add("每天留半个小时发呆或者冥想。"); advices.add("保持心情平和。"); advices.add("专注认真但不要急功近利。"); advices.add("赶紧脱单。"); req.setAttribute("adviceList", advices); req.getRequestDispatcher(VIEW_SHOW).forward(req, resp); } }
测试
支持JspFragment的标签
API javax.servlet.jsp.tagext.JspFragment
- javax.servlet.jsp.JspContext getJspContext()
返回绑定到当前JspFragment的JspContext - void invoke(java.io.Writer out)
将当前的jsp片段输出到指定的writer。如果参数out为null,就输出到绑定的JspContext调用getOut()返回的,javax.servlet.jsp.JspWriter对象。
另外,在支持标签体的标签-iterator中,我们曾这样输出标签体的内容:
getJspBody().invoke(null);// getJspBody()返回的就是JspFragment的对象
支持JspFragment的标签,就是支持JspFragment类型的属性的标签,而且可以定义多个这样的属性。另外,标签体可以视为一个隐藏的JspFragment属性。所以,只支持标签体的标签,只能获取一个JspFragment;支持JspFragment的标签,可以获取多个JspFragment。
设计demo
我们应该经历过一些讨论会、辩论会,抛出一个问题,试图获得一个被普遍认可的答案。或许有的问题是没有答案的,有的问题又不止一个答案。在这里我们简化其模型,将一些问题及其唯一的答案输出到页面——关键是“问题”和“答案”拥有不同的样式,所以我们需要为标签处理类添加两个JspFragment的属性;一个定义怎么输出“问题”,一个定义怎么输出“答案”。
当然,这并不是一个很好的demo,因为使用只支持标签体的标签可以实现同样的功能;大家可以自行尝试改版。
文件目录结构
src/main/java |---- cn.ljl.javaweb.demo.jsp2taglib.domain |---- Topic.java |---- cn.ljl.javaweb.demo.jsp2taglib.servlet |---- TopicServlet.java |---- cn.ljl.javaweb.demo.jsp2taglib |---- TopicTag.javasrc/main/webapp |---- WEB-INF |---- jsp2taglib |---- demo.tld |---- demo/jsp2taglib |---- topic.jsp
说明:这一次增加了一个domain(Topic.java),用来封装问题和答案。
文件内容
Topic.java
package cn.ljl.javaweb.demo.jsp2taglib.domain;/** * 这是一个pojo,用于封装一个问题及其唯一的答案. * @author lijinlong * */public class Topic { private String question; private String answer; public Topic(String question, String answer) { super(); this.question = question; this.answer = answer; } public String getQuestion() { return question; } public void setQuestion(String question) { this.question = question; } public String getAnswer() { return answer; } public void setAnswer(String answer) { this.answer = answer; }}
TopicTag.java
package cn.ljl.javaweb.demo.jsp2taglib;import java.io.IOException;import java.util.List;import javax.servlet.jsp.JspException;import javax.servlet.jsp.tagext.JspFragment;import javax.servlet.jsp.tagext.SimpleTagSupport;import cn.ljl.javaweb.demo.jsp2taglib.domain.Topic;/** * 用来演示支持JspFragment的标签.<br> * 这个标签将迭代一个话题的列表,允许jsp分别定义如何输出问题和答案. * @author lijinlong * */public class TopicTag extends SimpleTagSupport { /** 话题列表 */ private List<Topic> topicList; /** 问题attribute的name */ private String quesAttrName; /** 答案attribute的name */ private String answAttrName; /** 问题的JspFragment */ private JspFragment quesFrag; /** 答案的JspFragment */ private JspFragment answFrag; public List<Topic> getTopicList() { return topicList; } public void setTopicList(List<Topic> topicList) { this.topicList = topicList; } public String getQuesAttrName() { return quesAttrName; } public void setQuesAttrName(String quesAttrName) { this.quesAttrName = quesAttrName; } public String getAnswAttrName() { return answAttrName; } public void setAnswAttrName(String answAttrName) { this.answAttrName = answAttrName; } public JspFragment getQuesFrag() { return quesFrag; } public void setQuesFrag(JspFragment quesFrag) { this.quesFrag = quesFrag; } public JspFragment getAnswFrag() { return answFrag; } public void setAnswFrag(JspFragment answFrag) { this.answFrag = answFrag; } @Override public void doTag() throws JspException, IOException { for (Topic t : topicList) { getJspContext().setAttribute(quesAttrName, t.getQuestion()); getJspContext().setAttribute(answAttrName, t.getAnswer()); quesFrag.invoke(null); answFrag.invoke(null); } }}
demo.tld
<tag> <description>输出话题</description> <name>topic</name> <tag-class>cn.ljl.javaweb.demo.jsp2taglib.TopicTag</tag-class> <body-content>empty</body-content> <attribute> <name>topicList</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>quesAttrName</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>answAttrName</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>quesFrag</name> <required>true</required> <fragment>true</fragment> </attribute> <attribute> <name>answFrag</name> <required>true</required> <fragment>true</fragment> </attribute> </tag>
注意:
- 这里的body-content要设置为empty
- 支持JspFragment的配置和支持属性的配置并没有什么区别,JspFragment也只是属性的类型罢了
topic.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%@ taglib uri="http://www.lijinlong.cn/demotaglib" prefix="demo"%><html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>自定义标签 - topic</title></head><body> <h2>我们讨论了下述的问题,并统一了其答案:</h2> <demo:topic topicList="${requestScope.topicList }" quesAttrName="ques" answAttrName="answ"> <jsp:attribute name="quesFrag"> <div style="font-weight: bold;"> ${pageScope.ques} </div> </jsp:attribute> <jsp:attribute name="answFrag"> <div style="margin-top: 5px; margin-bottom: 15px;"> ${pageScope.answ} </div> </jsp:attribute> </demo:topic></body></html>
说明:为JspFragment类型的属性赋值,要使用<jsp:attribute>标签。
TopicServlet.java
package cn.ljl.javaweb.demo.jsp2taglib.servlet;import java.io.IOException;import java.util.ArrayList;import java.util.List;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import cn.ljl.javaweb.demo.jsp2taglib.domain.Topic;@WebServlet(name = "topicServlet", urlPatterns = { "/demo/jsp2taglib/topic" })public class TopicServlet extends HttpServlet { private static final long serialVersionUID = -2717086554249858215L; private static final String VIEW_SHOW = "/demo/jsp2taglib/topic.jsp"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { List<Topic> topicList = new ArrayList<Topic>(); topicList.add(new Topic("我很想做一件事,但是又害怕失败,怎么办?", "害怕失败比失败本身更可怕。失败只是一时的,害怕失败却会纠缠你很久。")); topicList.add(new Topic("最近被一些人否定了,我觉得自己很没用。", "别人轻视了你,是轻视你的过往;你轻视了自己,是放弃了你的未来。平静地对待,平静地加油。")); topicList.add(new Topic("我被一个同学骗了,再也不敢相信任何人了。", "所有的人都靠谱,就没有欺骗;所有的人都不靠谱,就欺骗不了。就是有人不可信,才会有些人可信,你要学会鉴别,不要失望。")); req.setAttribute("topicList", topicList); req.getRequestDispatcher(VIEW_SHOW).forward(req, resp); }}
测试
总结
有必要对这一块做个总结,因为它看起来要“复杂”一点。
与支持属性的标签相比,在编写标签处理类、添加配置上都没有什么区别,只是在jsp中要使用<jsp:attribute>来为JspFragment类型的属性赋值。
与支持标签体的标签相比,支持JspFragment看起来可以把标签体划分成多个部分,每一个部分作为标签处理类的一个属性。
支持动态属性的标签
支持动态属性,即支持不限定属性的个数和名称。
文件目录结构
src/main/java |---- cn.ljl.javaweb.demo.jsp2taglib.servlet |---- DynaAttrServlet.java |---- cn.ljl.javaweb.demo.jsp2taglib |---- DynamicAttributesTag.javasrc/main/webapp |---- WEB-INF |---- jsp2taglib |---- demo.tld |---- demo/jsp2taglib |---- dynaAttr.jsp
文件内容
DynamicAttributesTag.java
package cn.ljl.javaweb.demo.jsp2taglib;import java.io.IOException;import java.util.ArrayList;import java.util.List;import javax.servlet.jsp.JspException;import javax.servlet.jsp.JspWriter;import javax.servlet.jsp.tagext.DynamicAttributes;import javax.servlet.jsp.tagext.SimpleTagSupport;/** * 动态属性标签库.<br> * 不限制属性的个数和属性的名称.<br> * @author lijinlong * */public class DynamicAttributesTag extends SimpleTagSupport implements DynamicAttributes { private List<String> keys = new ArrayList<String>(); private List<Object> values = new ArrayList<Object>(); @Override public void setDynamicAttribute(String uri, String localName, Object value) throws JspException { keys.add(localName); values.add(value); } @Override public void doTag() throws JspException, IOException { JspWriter out = getJspContext().getOut(); out.println("<ol>"); for (int i = 0; i < keys.size(); i ++) { String key = keys.get(i); Object value = values.get(i); out.println("<li>" + key + "=" + value + "</li>"); } out.println("</ol>"); }}
说明:支持动态属性的标签处理类,需要实现javax.servlet.jsp.tagext.DynamicAttributes
接口,实现setDynamicAttribute方法以存储动态属性。
demo.tld
<tag> <name>dynaAttr</name> <tag-class>cn.ljl.javaweb.demo.jsp2taglib.DynamicAttributesTag</tag-class> <body-content>empty</body-content> <dynamic-attributes>true</dynamic-attributes> </tag>
注意:这里配置了dynamic-attributes来支持动态属性。
dynaAttr.jsp
<%@ page language="java" contentType="text/html; charset=GBK" pageEncoding="GBK"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><%@ taglib uri="http://www.lijinlong.cn/demotaglib" prefix="demo"%><html><head><meta http-equiv="Content-Type" content="text/html; charset=GBK"><title>自定义标签 - dynaAttr</title></head><body><h2>指定了三个属性:</h2><demo:dynaAttr name="Adam" age="24" favorite="books" /></body></html>
说明:在使用标签时,属性的个数、名称都是自由的。
DynaAttrServlet.java
package cn.ljl.javaweb.demo.jsp2taglib.servlet;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;@WebServlet(name="dynaAttrServlet", urlPatterns={"/demo/jsp2taglib/dynaAttr"})public class DynaAttrServlet extends HttpServlet { private static final long serialVersionUID = -3146911293779890912L; private static final String VIEW_SHOW = "/demo/jsp2taglib/dynaAttr.jsp"; @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.getRequestDispatcher(VIEW_SHOW).forward(req, resp); } }
测试
开源标签库
- JSTL
Sun提供的一套标签库 - DisplayTag
Apache组织下的一套开源标签库
原标题:JSP2 自定义标签
关键词:JS