一、JSP自定义标签
JSP自定义标签,可以通过实现Tag接口、继承TagSupport类来设置标签功能。
后续通过配置文件将标签和具体的实现类关联。
二、自定义第一个标签(实现Tag接口)
自定义标签需要先创建一个Java类,然后实现Tge接口或者继承S.....类
我们先来看实现接口这种方法自定义标签:
2.1创建Java类
HelloTag.java
import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.Tag; public class HelloTag implements Tag { private PageContext pageContext; @Override public int doEndTag() throws JspException { // TODO Auto-generated method stub System.out.println("endTag"); return EVAL_PAGE;//继续显示该标签后续内容 } @Override public int doStartTag() throws JspException { // TODO Auto-generated method stub String msg = "MyTag"; JspWriter out = pageContext.getOut(); try { out.println(msg); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("startTag"); return EVAL_BODY_INCLUDE;//显示标签体中的内容 } @Override public Tag getParent() { // TODO Auto-generated method stub return null; } @Override public void release() { // TODO Auto-generated method stub } @Override public void setPageContext(PageContext pageContext) { // TODO Auto-generated method stub //获取JSP页面的pageContext this.pageContext = pageContext; System.out.println("pageContext"); } @Override public void setParent(Tag arg0) { // TODO Auto-generated method stub } }
2.2创建好java类后,需要创建配置文件
2.2.1创建my_tag_config.tld文件
在WEB-INF文件夹下新建XML文件。
将.xml改为.tld(结果如下图所示)
点击next,进入如下页面。(结果如下图所示)
点击next(结果如下图所示)
点击next,(效果如下图所示)
点击finish,创建结束。
打开创建好的文件,效果如下图所示。
接着我们就要对其进行配置,将标签和我们创建的类关联。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd" > <taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>short-name</short-name> <!-- 此处的uri只是一个标识,可任意指定,后续JSP页面中引用时保持一致即可 --> <uri>http://www.myTag.com</uri> <tag> <name>hello</name> <!-- 设置标签名 --> <tag-class>com.myTag.HelloTag</tag-class> <!-- 设置标签名关联类 --> <body-content>JSP</body-content> <!-- 指定标签体显示格式 --> </tag> </taglib>
其中<body-content>中的属性有四种:
tagdependent,empty,JSP,
各个属性的含义可参阅:https://www.cnblogs.com/keyi/p/7127685.html
以上都配置好了后,我们就来使用标签。
tag.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!-- 指定需要使用标签的URi和前缀 --> <%@ taglib uri="http://www.myTag.com" prefix = "m" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <m:hello>include body</m:hello> </body> </html>
自定义标签的功能是输出“MyTag”,结果正常输出。
我们来看下控制台的输出:
最后我们分析下标签的执行流程
我们看下tag.jsp生存的servlet中的代码:
Servlet中调用了一个方法,我们来看下这个方法
查看代码可以很清楚的看到自定义标签的执行流程。
三、自定义out标签(继承TagSupport类实现)
java类
import java.io.IOException; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.TagSupport; public class HelloTagEx extends TagSupport { private String value; private JspWriter out; public void setValue(String value) {//设置值 this.value = value; } @Override public int doStartTag() throws JspException { // TODO Auto-generated method stub
//不重写setPageContext方法,也可直接使用pageContext
// 例如: this.out = pageContext.getOut(); try { out.println(value); //将设置的value输出 } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return SKIP_BODY;//不输出标签体中内容 } @Override public int doEndTag() throws JspException { // TODO Auto-generated method stub return SKIP_PAGE;//不输出界面后续内容 } @Override public int doAfterBody() throws JspException { // TODO Auto-generated method stub return super.doAfterBody(); } @Override //此方法也可以不重写,在doStartTag、doEndTag中可以直接使用pageContext
public void setPageContext(PageContext pageContext) { this.pageContext = pageContext; this.out = pageContext.getOut(); } }
标签配置:
<tag> <name>helloEx</name> <tag-class>com.myTag.HelloTagEx</tag-class> <body-content>JSP</body-content> <attribute> <name>value</name> <!-- 属性名要和类中属性名对应 --> <required>true</required> <!-- 该属性是否为必填项 true 代表为必填,不填会报错 --> <rtexprvalue>true</rtexprvalue> <!-- 能否使用表达式赋值 true表示可以用表达式--> </attribute> </tag>
tag.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!-- 指定需要使用标签的URi和前缀 --> <%@ taglib uri="http://www.myTag.com" prefix = "m" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <m:helloEx value="123">ss</m:helloEx> endTag方法返回值是SKIP_PAGE,不输出后续界面,所以这句话不会输出。 </body> </html>
四、自定义if标签
有了前面两个作为基础,后面的就很简单了。
标签类
import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class IF extends TagSupport{ private boolean flag; public void setFlag(boolean flag) { this.flag = flag; } @Override public int doStartTag() throws JspException { // TODO Auto-generated method stub if(flag == true) {//如果为true,标签体中内容执行,反正不执行。 return EVAL_BODY_INCLUDE; }else { return SKIP_BODY; } } @Override public int doEndTag() throws JspException { // TODO Auto-generated method stub return EVAL_PAGE;//IF标签结束后,继续执行页面内容 } }
配置:
<tag> <name>IF</name> <tag-class>com.myTag.IF</tag-class> <body-content>JSP</body-content> <attribute> <name>flag</name> <!-- 属性名要和类中属性名对应 --> <required>true</required> <!-- 该属性是否为必填项 true 代表为必填,不填会报错 --> <rtexprvalue>true</rtexprvalue> <!-- 能否使用表达式赋值 true表示可以用表达式--> </attribute> </tag>
tag.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!-- 指定需要使用标签的URi和前缀 --> <%@ taglib uri="http://www.myTag.com" prefix = "m" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <m:IF flag="${3>2}"> true </m:IF> </body> </html>
五、自定义foreach标签
标签类:ForEach.java
import java.util.Iterator; import java.util.List; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class ForEach extends TagSupport { private List<String> list; private String var; public void setList(List<String> list) { this.list = list; } public void setVar(String var) { this.var = var; } @Override public int doStartTag() throws JspException { // TODO Auto-generated method stub if(list == null) { return SKIP_BODY; }else { Iterator<String> items = list.iterator(); pageContext.setAttribute("ite", items); pageContext.setAttribute("list", list); //由于doStartTage方法结束后是输出标签体, //而标签体中有${var}表达式用于输出迭代对象 //所以此处最好先拿出一个对象。 if(items.hasNext()) {//如果迭代器中有元素,设置值,并输出标签体 pageContext.setAttribute(var, items.next()); return EVAL_BODY_INCLUDE; }else {//反之则不输出标签体 return SKIP_BODY; } } } @Override public int doAfterBody() throws JspException { // TODO Auto-generated method stub //获取迭代器 Iterator<String> items = (Iterator<String>)pageContext.getAttribute("ite"); if(items.hasNext()) {//将其中元素取出,并设置。 String value = (String) items.next(); pageContext.setAttribute(var, value); return EVAL_BODY_AGAIN; //再一次执行标签体 }else { return SKIP_BODY; //迭代器为空,则停止输出标签体 } } @Override public int doEndTag() throws JspException {//结束标签后的内容继续输出 // TODO Auto-generated method stub return EVAL_PAGE; } }
配置:
<tag> <name>FOREACH</name> <tag-class>com.myTag.ForEach</tag-class> <body-content>JSP</body-content> <attribute> <name>var</name> <!-- 属性名要和类中属性名对应 --> <required>true</required> <!-- 该属性是否为必填项 true 代表为必填,不填会报错 --> <rtexprvalue>false</rtexprvalue> <!-- 能否使用表达式赋值 false表示不可以用表达式--> </attribute> <attribute> <name>list</name> <!-- 属性名要和类中属性名对应 --> <required>true</required> <!-- 该属性是否为必填项 true 代表为必填,不填会报错 --> <rtexprvalue>true</rtexprvalue> <!-- 能否使用表达式赋值 true表示可以用表达式--> </attribute> </tag>
tag.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import = "java.util.*" %> <!-- 指定需要使用标签的URi和前缀 --> <%@ taglib uri="http://www.myTag.com" prefix = "m" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Insert title here</title> </head> <body> <% List<String> list = new ArrayList<String>(); for(int i = 0; i < 5 ; i++){ list.add("str" + i); } pageContext.setAttribute("list", list); %> <m:FOREACH list="${list}" var="s"> ${s} </m:FOREACH> </body> </html>
foreach主要通过表达式将list对象传递到ForEach.java中,
在ForEach.java中主要通过doAfterBody()方法,将迭代器中元素
通过pageSetAttribute()设置到page中,然后输出${var},输出的表达式又会自动获取设置的值,
然后往复输出执行doAfterBody()方法,并且输出标签体(表达式${var}),来实现foreach。
直到迭代器元素全部输出,方法才终止。
那个doAfterBody()和return EVAL_BODY_AGAIN配合的流程可能不太好理解,
我们来看下tag.jsp生成的Servlet中的代码就很好理解了。
执行顺序为(不考虑后续输出终止情况):
doStartTag->标签体内容->doAfterBody->doEndTag
<xx> 标签开始(doStartTag)
xxxxxx 标签体
doAfterBody判断是否继续输出标签体(doAfterBody方法也可看作标签体的一部分)
</xx> 标签结束(doEndTag)
参考资料: