一、简介
自定义标签的作用主要是用来替换JSP中的Java脚本。属于JSP技术。
二、自定义标签的开发步骤:
1.编写一个实现Tag接口的Java类(标签处理器类)。
2.编写标签库描述符(tld)文件,在tld文件中把标签处理器类进行描述在tld文件放入应用的web-inf文件中。如输入客户端ip地址的例子具体步骤示例如下:
1>定义一个类,实现javax.servlet.jsp.tagext.Tag接口(通过继承Tag接口的实现类也可)。
public class ShowRemoteIpTag extends TagSupport { public int doStartTag() throws JspException { String remoteIp = pageContext.getRequest().getRemoteAddr(); try { pageContext.getOut().print("您的IP是是:"+remoteIp); } catch (IOException e) { e.printStackTrace(); } return super.doStartTag(); } }
2、在WEB-INF目录下建立一个扩展名为tld的XML文件(XML的内容参考Tomcat中的示例)fk.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>my taglib study.</description> <tlib-version>1.0</tlib-version> <short-name>fk</short-name> <uri>http://www.test.com/jsp/fk</uri> <tag> <!-- 可定义标签如<fk: showRemoteIp /> --> <description>Show Remote Ip</description> <name>showRemoteIp</name> <tag-class>com.fk.tag.ShowRemoteIpTag</tag-class> <body-content>empty</body-content> </tag> <tag> <name>forEach</name> <tag-class>com.fk.tag.example.ForEachSimpleTag</tag-class> <body-content>scriptless</body-content> <attribute> <!-- 定义标签属性 --> <name>items</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>var</name> <required>true</required> <rtexprvalue>false</rtexprvalue><!-- var属性是否支持el--> </attribute> </tag> <function> <!-- 也可定义在el中使用的函数如:${fk:contains(users,user)} --> <description>某一List对象是否包含某一Object对象</description> <name>contains</name> <function-class>com.fk.util.ListContainsLabel</function-class> <function-signature>java.lang.Boolean contains(java.util.List,java.lang.Object)</function-signature> </function> </taglib>
3、在web.xml中建立tld文件的存放位置和uri的对应关系。
注:此步骤是可选的,前提是tld文件放在了WEB-INF目录下(建议使用此方式)。
<jsp-config> <taglib> <taglib-uri>http://www.test.com/jsp/fk</taglib-uri> <taglib-location>/WEB-INF/fk.tld</taglib-location> </taglib> </jsp-config>
三、Tag接口详解(接口中的方法大部分都是容器调用的,继承了JspTag接口)
int doStartTag():容器调用。遇到开始标签时调用。
返回值可为:
Tag.SKIP_BODY:指示标签主体内容不执行
Tag.EVAL_BODY_INCLUDE:指示标签主体内容执行
int doEndTag():容器调用。遇到结束标签时调用。
返回值可为:
Tag.SKIP_PAGE:忽略结束标签之后的JSP内容
Tag.EVAL_PAGE:结束标签后的JSP内容还要继续执行
Tag getParent():用户调用。得到父标签。没有父标签返回null
void release():容器调用。释放标签类占用的资源。
void setPageContext(PageContext pc):容器调用。传入当前页面的上下文对象
void setParent(Tag t):容器调用。传入当前标签的父标签对象。如果有的话。
四、IterationTag接口:增加了重复执行主体内容的方法。(IterationTag接口继承了Tag接口,TagSupport类实现了IterationTag接口)
int doAfterBody():主体执行后,结束标签执行前,容器调用
返回值:
Tag.SKIP_BODY:指示标签主体内容不执行
IterationTag.EVAL_BODY_AGAIN:再次调用doAfterBody方法
注:成员变量的问题。
五、BodyTag:增加了获取主体内容的方法
void doInitBody() :容器调用
void setBodyContent(BodyContent b):容器调用,传递标签的主体内容
注意:以上2个方法都是由容器调用。要求doStartTag()方法必须返回BodyTag.EVAL_BODY_BUFFERED
六、传统标签返回值图、标签类继承及传统标签的执行流程
七、简单标签
SimpleTag接口详解
void setJspContext(JspContext pc) :设置当前JSP的上下文件。容器调用
void setParent(JspTag parent) :设置父标签对象。如果有的话。容器调用
void setJspBody(JspFragment jspBody) :设置主体内容。容器调用
若实现此接口类,有相关的setter属性,容器会自动调用自定义的setter方法。
void doTag():遇到标签就执行。容器调用。---------处理自定义标签功能的方法
用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以 抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中 位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回 Tag.SKIP_PAGE常量的情况。
JspTag getParent():自己调用。得到父标签。没有返回null
注意:每次调用都会重新实例化,简单标签不会驻留内存,而传统标签会驻留内存(像Servlet一样,第一次访问时容器创建实例化后就驻留了)。
//url地址过滤,若不是本站过来的url将被传到toPage值的文件中去。 public void doTag() throws JspException, IOException { PageContext pc = (PageContext)getJspContext(); HttpServletRequest request = (HttpServletRequest)pc.getRequest(); String referer = request.getHeader("Referer");//Referer HttpServletResponse response = (HttpServletResponse)pc.getResponse(); if(null == referer ||!referer.startsWith(siteUrl)){ //若不是指定的url开头地址则转过去,否则继续浏览呢,siteUrl为标签另一属性本站的基地址。 if(toPage.startsWith("/")){ response.sendRedirect(toPage); }else{ String path = request.getRequestURL().toString(); String prePath = path.substring(0, path.lastIndexOf("/")); response.sendRedirect(prePath+"/"+toPage); } } }
显示类如下,模拟foreach功能:
public class ForEachSimpleTag extends SimpleTagSupport { private Object items; private String var; private Collection collection ; public void setItems(Object items) { if(items instanceof List){ collection = (List)items; } if(items instanceof Set){ collection = (Set)items; } if(items instanceof Map){ collection = ((Map)items).entrySet(); } if(items.getClass().isArray()){ int len = Array.getLength(items);//获取数组的长度 for(int i=0;i<len;i++) collection.add(Array.get(items, i)); } } public void setVar(String var) {//setter方法容器自动将属性设置进去。 this.var = var; } public void doTag() throws JspException, IOException { PageContext pc = (PageContext)getJspContext(); if(null==collection) return; for(Object o:collection){ pc.setAttribute(var, o); getJspBody().invoke(null); } } }
八、tld文件的主要元素
taglib:根元素
tlib-version:版本号
short-name:短名称
uri:名称空间
function:可定义函数
name:标签的名称如contains
function-class:标签的处理类如com.fk.util.ListContainsLabel
function-signature:标签的静态处理方法
如:java.lang.Boolean contains(java.util.List,java.lang.Object)
tag:定义一个标签
name:标签的名称
tag-class:标签的处理类。全名称
body-content:标签的主体内容类型。
可选值:
JSP:主体内容可以是任意内容。服务传统标签
scriptless:主体内容一般是非java脚本内容。服务简单标签
empty:没有主体内容
tagdepentend:指示主体内容当做普通字符串对待
attribute:定义标签的属性
name:属性名称
required:是否是必须属性
rtexprvalue:是否支持表达式(EL表达式或Java表达式)
(runtime expression value)