javaweb回顾第八篇如何创建自定义标签
前言:在javaweb开发中自定义标签的用处还是挺多的。今天和大家一起看自定义标签是如何实现的。
1:什么是标签
标签是一种XML元素,通过标签可以使JSP页面变得简介易用,而且标签具有很好的复用性。
2:自定义标签的标签库主要的接口以及类的继承实现关系图

3:一步步实现自定义标签
3.1:Tag接口
我们先看一个标签<td></td>这个标签有开始标签和结束标签,而且还有<tr>这样的父标签,那么实现一个简单的标签需要什么呢
第一:开始标签 第二:结束标签第三:资源释放3个方法,而且还有父标签,如果我们要得到这个JSP上的内容我们还需要一个PageContext那么现在我们应该清晰了实现一个标签需要的元素。ok我们来看看Tag接口都有哪些内容
3.1.1:int doStartTag() throws JspException;这个是开始执行的起始方法
3.1.2:int doEndTag() throws JspException;这个是即将结束的结束方法
3.1.3:void release();释放对象的资源
3.1.4:void setPageContext(PageContext pc);设置当前页的上下文对象
3.1.5: void setParent(Tag t);设置父标签
3.1.6:Tag getParent();获取父标签
通过上面的介绍我们现在应该知道怎么去写一个标签了,我们小试牛刀一下
public class HelloTag implements Tag{
private PageContext pageContext;
private Tag parent;
public void setPageContext(PageContext pc) {
this.pageContext=pc;//这个方法由jsp页面的实现对象调用
}
public void setParent(Tag t) {
this.parent=t;
}
public Tag getParent() {
return parent;
}
public int doStartTag() throws JspException {
return SKIP_BODY;
}
public int doEndTag() throws JspException {
//利用pageContext来获取jspWriter对象
JspWriter out=pageContext.getOut();
try {
//利用JSPWriter向客户端输入信息
out.print("Hello Tag");
} catch (IOException e) {
e.printStackTrace();
}
return SKIP_PAGE;
}
public void release() {
}
其中SKIP_BODY表示忽略标签体内容,下面我们会说到。既然写完了一个标签体我们就开始配置了
首先在WEB-INFO创建一个tlds文件夹然后创建一个tld文件然后设置如下
<tag>
<name>hello</name>
<tag-class>com.lp.tags.HelloTag</tag-class>
<body-content>empty</body-content>//表示标签没有内容
</tag>
然后我们在创建一个jsp文件然后在jsp文件头部加上Taglib指令元素<%@ taglib uri="/WEB-INF/tlds/CustomTaglib.tld" prefix="hello"%>
在jsp页面就可以直接引用HelloTag标签了比喻我的是<hello:hello></hello:hello>
启动运行结果如下

有人又问了如果<td>这样的标签都有属性啊,如果有属性我怎么办呢,这个也简单没有属性我们就加入属性。我们来实现一个加法的自定义标签。这个我使用TagSupport类,从上面图中我们可以看出这个类实现了Tag接口,它会使我们写标签更加简单
public class AddTag extends TagSupport{
private int num1;
private int num2;
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
public int getNum1() {
return num1;
}
public void setNum1(int num1) {
this.num1 = num1;
}
public int doEndTag() throws JspException
{
JspWriter out=pageContext.getOut();
int num=num1+num2;
try {
out.print(num);
} catch (IOException e) {
e.printStackTrace();
}
return EVAL_PAGE;
}
有人又问你为什么没有写doStartTag方法啊,其实TagSupport类已经帮我们实现了,它默认情况是忽略标签中的内容的。现在我们在此配置tld文件
<tag>
<name>add</name>
<tag-class>com.lp.tags.AddTag</tag-class>
<attribute>//表示属性
<name>num1</name>属性命名
<required>true</required>是否必须输入
<rtexprvalue>true</rtexprvalue>是否是可运行的表达式
</attribute>
<attribute>
<name>num2</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
现在我们在jsp中加入以下代码
<%@ taglib uri="/WEB-INF/tlds/CustomTaglib.tld" prefix="addtaglib"%>
<body>
自定义的标签:
<%
int num1 = Integer.parseInt(request.getParameter("num1"));
int num2 = Integer.parseInt(request.getParameter("num2"));%>
算法:
<addtaglib:add num2="<%=num1 %>" num1="<%=num2 %>"></addtaglib:add>
</body>
再次运行我们看看结果

有人又说了,你这写的标签都没有标签内容,你能不能实现一个标签带有内容的呢,ok这个没问题,刚刚我们说了SKIP_BODY表示忽略标签内容那么有个相反的EVAL_BODY_INCLUDE表示带有标签中的内容,在这里我们一起实现一个Switch case default三个标签体联用的简单功能
我们先看SwitchTag标签
public class SwitchTag extends TagSupport{
private static final long serialVersionUID = 1L;
//用于判断子标签是否已执行
private boolean childTagExec;
public SwitchTag()
{
childTagExec=false;
}
public int doStartTag() throws JspException
{
//当遇到switch的起始标签的时候子标签还没执行
childTagExec=false;
return EVAL_BODY_INCLUDE;//此时开始执行Switch内部的Case标签了
}
/**
* 由子标签处理器对象调用,用于判断是否可以执行自身的标签体
* @return
*/
public synchronized boolean isExec()
{
return (!childTagExec);
}
/**
* 如果子标签任何一个满足条件就调用这个方法 通知父标签
* 这样其他子标签就忽略他们自身标签体,从而实现Switch case
*/
public synchronized void childTagSucceeded()
{
childTagExec=true;
}
public void release()
{
childTagExec=false;
}
CaseTag
public class CaseTag extends TagSupport{
private static final long serialVersionUID = 1L;
private boolean cond;//表示条件(比喻case:1此类)
public CaseTag()
{
cond=false;
}
public void setCond(boolean cond)
{
this.cond=cond;
}
public int doStartTag() throws JspException
{
Tag parent=getParent();//获取父标签
//判断是否可以执行自身标签
if(!((SwitchTag)parent).isExec())
{
return SKIP_BODY;
}
//如果条件为true,则通知父标签有一个子标签满足条件
//否则忽略标签体
if(cond)
{
((SwitchTag)parent).childTagSucceeded();
return EVAL_BODY_INCLUDE;
}
else {
return SKIP_BODY;
}
}
public void release()
{
cond=false;
}
DefaultTag
public class DefaultTag extends TagSupport{
private static final long serialVersionUID = 1L;
public int doStartTag() throws JspException
{
Tag parent=getParent();
if (!((SwitchTag)parent).isExec()) {
return SKIP_BODY;
}
((SwitchTag)parent).childTagSucceeded();//如果所有Case都不满足则执行Default标签
return EVAL_BODY_INCLUDE;
}
}
我们在次配置tld文件
<tag>
<name>switch</name>
<tag-class>com.lp.tags.SwitchTag</tag-class>
<body-content>jsp</body-content>
</tag>
<tag>
<name>case</name>
<tag-class>com.lp.tags.CaseTag</tag-class>
<body-content>jsp</body-content>
<attribute>
<name>cond</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>default</name>
<tag-class>com.lp.tags.DefaultTag</tag-class>
<body-content>jsp</body-content>
</tag>
其中<body-content>jsp</body-content>中jsp表示支持jsp具有的一切功能(比喻jsp9种内置对象)
<body>
<%
String userName = request.getParameter("userName");
%>
<mytag:switch>
<mytag:case cond='<%=userName.equals("zhangsan")%>'>
<%out.print("张三");%>
</mytag:case>
<mytag:case cond='<%=userName.equals("lisi")%>'>
<%out.print("李四");%>
</mytag:case>
<mytag:case cond='<%=userName.equals("wangwu")%>'>
<%out.print("王五");%>
</mytag:case>
<mytag:default>
<%out.print("无");%>
</mytag:default>
</mytag:switch>
</body>
现在开始执行效果如下

3.1:IterationTag接口
上面我们都一直说的标签内容都一次性完成,但是如果是循环标题体内容怎么办,那么就用到了IterationTag接口,此接口增加了一个方法
public int doAfterBody() throws JspException该方法表示每次对标签体处理之后被调用也就是说在doStartTag方法之后doEndTag方法之前被调用,如果没有的话就不执行。
新增加了一个常量EVAL_BODY_AGAIN表示再次执行标签体。现在我们实现一个获取多条用户信息展示的功能
public class UserBean implements Serializable{
private static final long serialVersionUID = 1L;
public UserBean(){}
public UserBean(String userName,int age,String email)
{
this.age=age;
this.email=email;
this.userName=userName;
}
private String userName;
private int age;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public class IterateTag extends TagSupport{
private static final long serialVersionUID = 1L;
private Iterator items;//获取集合
private String itemId;//对象的标识
private Object item;//迭代对象中的每一个对象
public IterateTag()
{
items=null;
}
public void release()
{
items=null;
}
/**
* 得到集合的迭代对象
*/
public void setItems(Collection cl)
{
if(cl.size()>0)
items=cl.iterator();
}
public void setVar(String var)
{
this.itemId=var;
}
public int doStartTag()throws JspException
{
if(items.hasNext())//首先被执行
{
item=items.next();
}
else{
return SKIP_BODY;
}
saveItems();//把迭代的对象保存在pageContext中
return EVAL_BODY_INCLUDE;
}
public int doAfterBody() throws JspException
{
if(items.hasNext())//直到把迭代对象中的每一项都放进pageContext中
{
item=items.next();
}
else{
return SKIP_BODY;
}
saveItems();
return EVAL_BODY_AGAIN;
}
public void saveItems()
{
if(item==null)
{
pageContext.removeAttribute(itemId,pageContext.PAGE_SCOPE);
}
else{
pageContext.setAttribute(itemId, item);//如果加入相同的id会进行覆盖
}
}
}
<tag>
<name>iterate</name>
<tag-class>com.lp.tags.IterateTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>items</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
<body>
<%
ArrayList al = new ArrayList();
UserBean user1 = new UserBean("zhangsan", 25, "7808@outlook.com");
UserBean user2 = new UserBean("lisi", 15, "16455@qq.com");
UserBean user3 = new UserBean("wangwu", 35, "7808@outlook.com");
al.add(user1);
al.add(user2);
al.add(user3);
%>
<table>
<tr>
<td>用户名</td>
<td>年龄</td>
<td>邮箱</td>
</tr>
<iterator:iterate items="<%=al%>" var="user">
<jsp:useBean id="user" class="com.lp.beans.UserBean"></jsp:useBean>
<tr>
<td><jsp:getProperty property="userName" name="user" /></td>
<td>${user.age}</td>
<td>${user["email"]}</td>
</tr>
</iterator:iterate>
</table>
</body>
效果如下

4:简单标签开发
为了简化自定义标签开发,JSP2.0加入了简单标签的开发实现的接口是SimpleTag,我们一起看下SimpleTag的主要方法
4.1: public void setJspContext( JspContext pc )该方法被容器调用,设置JspContext,JspContext 是PageContext的基类
4.2:public void setParent( JspTag parent );设置父标签
4.3:public JspTag getParent();获取父标签
4.4:public void setJspBody( JspFragment jspBody );该方法用于设置标签体标签体,标签体由JspFragment对象提供,可以把JspFragment看做是一个对象封装一段JSP代码,可以被多次执行。
4.5:public void doTag(),主要处理标签和标签体的业务逻辑
public class WelcomeSimpleTag extends SimpleTagSupport{
private JspFragment jspFragment;
private String name;
public void setJspBody(JspFragment jspBody)
{
this.jspFragment=jspBody;
}
public void setName(String name)
{
this.name=name;
}
public void doTag() throws JspException, IOException
{
JspContext jspContext=getJspContext();
JspWriter out=jspContext.getOut();
out.print(name);
jspFragment.invoke(null);
}
然后在jsp页面之间调用即可。关于简单标签的开发,大家可以自行实践。
javaweb回顾第八篇如何创建自定义标签的更多相关文章
- 在html中创建自定义标签
创建并使用自定义标签 Web Components 标准非常重要的一个特性是,它使开发者能够将HTML页面的功能封装为 custom elements(自定义标签),本篇介绍使用 CustomElem ...
- 使用原生js创建自定义标签
使用原生js创建自定义标签 效果图 代码 <!DOCTYPE html> <html lang="en"> <head> <meta ch ...
- javaweb回顾第十篇JSTL
前言:JSTL(JSP Standard Tag Library)JSP标准标签库.它的目的是为了简化JSP的开发,如何没有JSTL可能我们开发的时候就需要写大量的自定义标签,无疑会加大开发难度,有了 ...
- javaweb回顾第七篇jsp
1:为什么会有JSP jsp全名(java server pages)中文叫做java服务器页面.在Servlet那一篇我们发现用Servlet可以生成动态页面,但是我们却在Servlet中却写了大量 ...
- Spring源码学习-容器BeanFactory(四) BeanDefinition的创建-自定义标签的解析.md
写在前面 上文Spring源码学习-容器BeanFactory(三) BeanDefinition的创建-解析Spring的默认标签对Spring默认标签的解析做了详解,在xml元素的解析中,Spri ...
- javaweb回顾第六篇谈一谈Servlet线程安全问题
前言:前面说了很多关于Servlet的一些基础知识,这一篇主要说一下关于Servlet的线程安全问题. 1:多线程的Servlet模型 要想弄清Servlet线程安全我们必须先要明白Servlet实例 ...
- javaweb回顾第三篇数据库访问
前言:本篇主要针对数据库的操作,在这里不适用hibernate或者mybatis,用最原始的JDBC进行讲解,通过了解这些原理以后更容易理解和学习hibernate或mybatis. 1:jdbc的简 ...
- javaweb回顾第五篇浅谈会话
1:什么是会话 通俗来说就是客户和服务器的一次私密谈话,客户发送请求以后服务器能够识别请求是来自同一个客户,他们是1对1的关系. 了解会话以后我们就要去考虑如何去实现这些问题下面一一进行解析 2:会话 ...
- javaweb回顾第四篇Servlet异常处理
前言:很多网站为了给用户很好的用户体验性,都会提供比较友好的异常界面,现在我们在来回顾一下Servlet中如何进行异常处理的. 1:声明式异常处理 什么是声明式:就是在web.xml中声明对各种异常的 ...
随机推荐
- Mini projects #3 ---- Stopwatch: The Game
课程全名:An Introduction to Interactive Programming in Python,来自 Rice University 授课教授:Joe Warren, Scott ...
- VIM使用(二) 浏览内核源代码
为了实现类似SourceInsight功能,通过VIM+Ctags+Cscope+Taglist+Source Explore +NERD Tree实现. 一, 安装插件 1)安装Ctags 和Csc ...
- document.write 摘抄
页面载入过程中用实时脚本创建页面内容,以及用延时脚本创建本窗口或新窗口的内容.该方法需要一个字符串参数,它是写到窗口或框架中的HTML内容.这些字符串参数可以是变量或值为字符串的表达式,写入的内容常常 ...
- wrHDL编译中软核代码初始化及编译耗时长的问题
问题的提出整个WR的ISE工程比较大,编译时间很长,导致开发效率低.通过分析发现,ISE在综合的时候大量的时间都花在了初始化DPRAM上.调研发现Xilinx提供了BMM文件和DATA2MEM工具,可 ...
- cocoapods的时候出现的问题 _OBJC_CLASS_$_XXX
最新的cocoapod导入xmpp的时候,会出现循环依赖,所以撸主选择了手动导入. 一开始还用的挺开心的,后来,使用cocoapods导入其他的框架,发现调用的时候总是报错. Undefined sy ...
- 在Ubuntu13.04中配置Jexus+Mono3.2运行Asp.Net Mvc 4站点 (二)
开始写这篇前看了看日期,写下这个序列的前一半竟是两个月前的事情了,无比惭愧.这段时间尝试重新组织Mvc4的项目引用,创建了两个项目模板,一个是简单模式,即仅包含Mvc基本功能.另一个是包含了Mvc4 ...
- MVC 伪静态
1.config <system.webServer> <validation validateIntegratedModeConfiguration="false&quo ...
- 深入理解openstack网络架构(4)-----连接到public network
原文地址: https://blogs.oracle.com/ronen/entry/diving_into_openstack_network_architecture3 译文转自:http://b ...
- [Asp.net 开发系列之SignalR篇]专题一:Asp.net SignalR快速入门
一.前言 之前半年时间感觉自己有点浮躁,导致停顿了半年多的时间没有更新博客,今天重新开始记录博文,希望自己可以找回初心,继续沉淀.由于最近做的项目中用到SignalR技术,所以打算总结下Asp.net ...
- Intellij修改archetype Plugin配置
Maven archetype plugin为我们提供了方便的创建 project功能,Archtype指我们项目的骨架,作为项目的脚手架. 如fornt end的yo之类.我们能够通过简单的一行控制 ...