Jstl提供的功能已经非常强大,但是当它不能满足我们的需求时,我们还能够开发我们自己的标签。如何开发呢,首先看一下当我们在使用jstl的时候是如何引用标签的 使用<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>在页面中引入标签, 通过<c:out param1="">body</c>进行使用。 其中uri并不是一个链接,它代表唯一的一个标志,可以通过这个标志找到指定的TLD文件。prefix为前缀,引用这个tld文件中的标签时所使用的前缀。 c:out表示的就是已用的是c所对应的tld文件中的哪一个标签,param1为这个标签中所能够定义的属性,body是标签体。 所以当我们写自己的标签的时候需要做的内容就包括了写一个tld文件来定义标签,属性和body。 另外我们还需要编写tag handler类,即标签所对应的操作的类,这个类也会在tld文件中与标签关联。
开发自定义标签类有三种方式tag files ,tag handle(simple, classic),我们从最简单的开始说起。 tag files
主要的目的是动态的引入页面。 开发的时候我们可以先写好一个要引入的jsp页面,比如headers.jsp,然后重新命名为headers.tag放到WEB-INF目录下的tags目录下。内容如下: <h1>This is my JSP page.</h1> <br> ${subTitle }<br> 为了使他真正成为一个标签,我们还需要定义其中的属性,body等等,然后代码更改成如下的样子:
<%@ attribute name="subTitle" required="true" rtexprvalue="true" %>
<%@ tag body-content="tagdependent" %> <h1>This is my JSP page.</h1> <br> ${subTitle }<br> <jsp:doBody/>
其中<jsp:doBody/>表示输出体的内容。 然后就是在页面中的引用 <%@ taglib tagdir="/WEB-INF/tags" prefix="myTag"%> <myTag:headers subTitle="nihao"><%=123 * 123 %></myTag:headers>
这种方法并不常用,另外其中attribute和body-content的作用跟在tld中的作用是相同的,下面会统一的介绍。
SimpleTagSupport 最常用的就是继承SimpleTagSupport这个类来开发自定义的标签。他是JSP2.0为了简便开发新提供给我们的类,先来一个比较简单的,hello sombody。 先写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"> <tlib-version>1.2</tlib-version> <short-name>testTag</short-name> <uri>myTestTag</uri> <tag> <description>show hello</description> <name>simple1</name> <tag-class>com.prince.tag.Simple1Tag</tag-class> <body-content>scriptless</body-content> <attribute> <name>somebody</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib>
其中taglib中的属性不用在意只是制定了schema和版本等信息。uri中是这个文件的唯一标识,一般使用一个网站地址来表示,因为一般网站地址是不会有重复的可以有多个tag标签定义多个tag。name表示的是:后面的部分即标签名。tag-class指定了这个标签所关联的类。body-cotent表示标签body中可以包含的内容可以取值为;empty,scriptless,tagdependent,JSP因为2.0规范中不建议使用这一项,所以很多容器都没有实现它attribute表示标签可以包含的属性,required表示必须填写的项,rtexprvalue表示属性是否可以使用表达式来赋值为了告诉去什么地方寻找这个tld文件,在jsp2.0之前我们需要在DD文件中进行配置:
<jsp-config> <taglib> <taglib-uri>myTestTag</taglib-uri> <taglib-location>/WEB-INF/testTag.tld</taglib-location> </taglib> </jsp-config>
在JSP2.0之后容器会自动在四个目录查找tld文件,WEB-INF的根目录,子目录。WEB-INF/lib中的jar包中的META-INFO目录及其子目录。 tld文件编写完成,第二步,写类
public class Simple1Tag extends SimpleTagSupport { private String somebody = null; public void doTag() throws JspException, IOException { getJspContext().getOut().write("<h1>你好"+ somebody +"</h1>"); } public String getSomebody() { return somebody; } public void setSomebody(String somebody) { this.somebody = somebody; } }
需要继承SimpleTagSupport 类在tld文件中定义的attribute需要在这个类中作为实例变量,另外需要有seter和geter来供容器使用。最后在需要在页面上进行引用: <%@ taglib uri="myTestTag" prefix="myTag"%> <myTag:simple1 somebody="小红"/>
rtexprvalue
表示属性是否能够使用脚本进行赋值,如果设置为true的话页面上就能能够使用多种方式进行赋值:
<% String somebody = "小白"; %> <c:set var="somebody1" value="小黑" scope="session"></c:set> <myTag:simple1 somebody="小红"/> <myTag:simple1 somebody="${somebody1}"/> <myTag:simple1 somebody="<%=somebody %>"/>
<myTag:simple1 ><jsp:attribute name="somebody" value="ddd"></myTag:simple1>
如果body不为空
在tld文件中有body-content配置项
为empty的时候表示body必须为空,以/>结尾为scriptless的时候表示不能够使用脚本,但是能够使用action、自定义标签或者el表达式为tagdependent时,表示body中的内容会作为普通文本,用户可以在自己的类里进行处理。在类中对body进行处理时也比较的简单,可以直接加入getJspBody().invoke(null);的代码,来把body直接输出到页面上。
如果能够使用el表达式,我们还能够实现一些更复杂的功能,能够在类中更改或者是新建el表达式想要输出的变量。比比如加入:
getJspContext().setAttribute("somebody1", "小白");
如果body中有${somebody1 }就会被翻译成小白。
更甚至于,我们还能够循环输出body的内容,这非常有用,比如动态建立多行表格时等等。首先我们更改一下tld文件:
加入
<attribute> <name>tastes</name> <required>false</required> <rtexprvalue>true</rtexprvalue>
</attribute>
把类代码改为: private String[] tastes = null; public void doTag() throws JspException, IOException { if(tastes != null){ for (String taste : tastes) { getJspContext().setAttribute("taste", taste); getJspBody().invoke(null); } } } 页面上的代码为:
<% String[] tastes = new String[]{"化学","游泳"}; request.setAttribute("tastes",tastes);%> <table> <myTag:simple1 tastes="${tastes }"> <tr> <td>${taste }</td> </tr> </myTag:simple1> 执行结果会发现,我们打印出了一个表格。
TagSupport
在JSP2.0之前,是没有SimpleTagSupport来让我们使用的,下面就来看一下使用比较原始的方式该如何操作。
首先看一个比较简单的例子:
public class ClassicTwo extends TagSupport { private static final long serialVersionUID = 1L; JspWriter out; public int doStartTag() throws JspException { out = pageContext.getOut(); try { out.println("doStartTag"); } catch (IOException e) { e.printStackTrace(); } return SKIP_PAGE; } public int doEndTag() throws JspException { try { out.println("doEndTag"); } catch (IOException e) { e.printStackTrace(); } return EVAL_PAGE; } }
当然页面上还是要加入引用的内容的,看这个类我们会发现,可覆盖的方法变多了,另外是通过return的值来控制流转的.对于具体的流转过程,看下图:
从这个图中,我们还能够每个方法中都有哪些跳转变量
另外还能看出在什么地方能够处理body。下面就来看一个跟上面SimpleTagSupport中相同功能的打印循环的类应该如何写:
public class ClassicThree extends BodyTagSupport { private static final long serialVersionUID = 1L; private String[] tastes = null; int tasteCount = 0; public int doStartTag() throws JspException { tasteCount = 0; return EVAL_BODY_INCLUDE; } public int doAfterBody() throws JspException { if(tastes != null){ if(tasteCount < tastes.length){ pageContext.setAttribute("taste", tastes[tasteCount]); tasteCount ++; return EVAL_BODY_AGAIN; }else{ return SKIP_BODY; } }else { return SKIP_BODY; } } public int doEndTag() throws JspException { return EVAL_PAGE; } public String[] getTastes() { return tastes; } public void setTastes(String[] tastes) { this.tastes = tastes; } }
代码比较简单,主要是利用了return EVAL_BODY_AGIN来实现的循环