前面的博客,我们讲过了 自定义
el函数
;
讲一个 自定义标签技术 ;
目录
自定义标签
自定义标签
主要用于移除JSP
页面中的java
代码 ;
要使用自定义标签移除JSP页面中的java代码,只需要完成以下两个步骤:
- 编写一个实现
Tag
接口的Java类
,把页面java
代码移到这个java类
中(标签处理器类) - 编写标签库描述符
(tld)
文件,在tld文件
中把标签处理器描述成一个标签 ;(tld文件,一般放在WEB-INF下面)
快速入门:使用标签输出客户机IP
分析自定义标签的执行流程 :
Tag
接口的方法都是由服务器调用的,但是在调用dostartTag
的时候,会先调用接口的setPageContext()
方法,因此,我们可以得到pageContext
对象;
自定义标签运行原理 :
当浏览器,访问web
服务器的jsp
页面时,遇到自定义标签;
- 首先实例化出一个自定义标签类的对象
- 利用
Tag
接口中的setPageContext
方法,将页面的pageContext
对象传递给处理器类的对象; - 再调用
setParent
方法,如果标签有父标签,则将父标签对象作为参数,传递进去;如果没有父标签,则参数为null
; - 上述工作完成以后,自定义标签类对象,相当于完成初始化操作了,这时候服务器开始执行标签,遇到开始标签,就执行
doStartTag
方法 ; - 如果标签有标签体,服务器一般会执行标签体 ;
- 执行到标签结束符,就会调用
doEndTag
方法; - 整个标签执行完毕以后,服务器一般会调用
release
方法,释放标签工作时锁占用的资源 ;
工作原理的源码:
从源码,我们可以看出,release
方法并没有得到执行,服务器把自定义标签对象,放到了缓存中,便于下次访问;
就像servlet
对象一样,一直驻留在服务器内存中,直到服务器关闭 ;
其中tld
文件,服务器会把WEB-INF
下面的tld
文件,全部加载到内存中,保存到一个Map
集合中,其中键就是jsp
文件中的uri
;
案列:使用标签输出客户机IP
我们新建一个java
类,让它继承 TagSupport
类;重写我们需要的方法 ,这里我们仅仅需要重写 doStartTag
方法;
public class ViewTag extends TagSupport {
// @Override
public int doStartTag() throws JspException {
// 利用pageContext对象,获取request
HttpServletRequest request = (HttpServletRequest) this.pageContext.getRequest();
String ip = request.getRemoteAddr() ;
JspWriter out = this.pageContext.getOut() ;
try {
out.write(ip);
} catch (IOException e) {
throw new RuntimeException(e);
}
return super.doStartTag();
}
}
然后我们需要写一个 TLD
文件 ;
如何编写TLD
文件?!
笔者也不会写,但是笔者会抄啊! (具体怎么抄,看笔者博客 怎么抄tld
文件(可以点击))
下面是我打开的 c.tld
的部分代码
我们怎么写 TLD
文件,就这些代码,稍微改吧改吧,就是我们自己的 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>JSTL 1.1 core library</description>
<display-name>JSTL core</display-name>
<tlib-version>1.1</tlib-version>
<!-- 这里我们要改下,改成我们自己的简化名,比如笔者,改成了自己的名字缩写 -->
<!-- <short-name>c</short-name> -->
<short-name>yaz</short-name>
<!-- 这里我们也要改下,改成我们自己的uri,比如笔者,改成了自己的网址,随便写一个也行 -->
<!-- <uri>http://ijava.xin./jsp/jstl/core</uri> -->
<uri>http://ijava.xin</uri>
<!-- 这是一大堆描述信息,直接删掉就好了
<validator>
<description>
Provides core validation features for JSTL tags.
</description>
<validator-class>
org.apache.taglibs.standard.tlv.JstlCoreTLV
</validator-class>
</validator>
-->
<!-- 下面是一个例子,如何描述EL函数 -->
<tag>
<!-- 描述这个标签是干嘛的;我们直接删掉
<description>
Catches any Throwable that occurs in its body and optionally
exposes it.
</description>
-->
<!-- 标签名字,如果你定义的标签有属性,就写;没属性就不需要写下面的那些 -->
<name>catch</name>
<!-- 标签方法来源的类,要写完整类名,留着 -->
<tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
<!-- 是否有标签体,没有的话就写empty,有的话,就写上标签体是啥,留着 -->
<body-content>JSP</body-content>
<!-- 标签属性,留着 -->
<attribute>
<!-- 还是描述信息,删掉删掉
<description>
Name of the exported scoped variable for the
exception thrown from a nested action. The type of the
scoped variable is the type of the exception thrown.
</description>
-->
<!-- 属性名字,留着 -->
<name>var</name>
<!-- 是否是必备属性,必备属性就写true,写了true的属性,就必须写上值了,留着 -->
<required>false</required>
<!-- 全称是 run time expression alue 翻译为 '运行时表达式',表示是否支持运行时赋值 ,留着 -->
<!-- 如果是 true 就表示属性支持动态赋值 -->
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
</taglib>
在编写完毕 tld
文件,然后在JSP
页面中引入我们的标签库,就可以用我们自定义的标签
了 ;
关于标签处理器类的方法
这个类里面的方法。并不是向我们上面举的例子那么简单的;上面只是简单的重写了 doStartTag
方法;
下面,我讲下,什么时候该重写什么方法 ;
获取标签体
为了获取到标签体,我们需要覆写
doStarTag()
方法,改变其返回值为EVAL_BODY_BUFFERED
;这样标签体就会被当做对象,保存在该标签器类对象中 ; 然后在doEndTag()
里面用java
代码完成逻辑 ;实例:
将标签体转成大写字母
@Override public int doStartTag() throws JspException { return BodyTagSupport.EVAL_BODY_BUFFERED ; } @Override public int doEndTag() throws JspException { // 获取标签体 BodyContent bodyContent = this.getBodyContent() ; String content = bodyContent.getString() ; content = content.toUpperCase() ; try { this.pageContext.getOut().write(content); } catch (IOException e) { throw new RuntimeException(e) ; } return super.doEndTag(); }
doAfterBody()
为了控制显示次数,需要利用到
doAfterBody()
这个方法在执行完标签体以后
以及执行doEndTag()
方法之前
得到调用 ;为了确保执行标签体,我们也还需要覆写
doStarTag()
方法,改变其返回值;实例:
控制标签体显示次数
int i = 5 ; @Override public int doStartTag() throws JspException { return Tag.EVAL_BODY_INCLUDE; } @Override public int doAfterBody() throws JspException { if (i-- > 0 ){ return TagSupport.EVAL_BODY_AGAIN; }else { i = 5 ; return TagSupport.SKIP_BODY ; } }
控制标签体是否显示
取决于d*
oStarTag()
*方法的方法值
EVAL_BODY_INCLUDE
包含标签体的意思,会显示标签体 ;而SKIP_BODY
跳过标签体
@Override
public int doStartTag() throws JspException {
return Tag.SKIP_BODY;
}
是否显示页面
主要是利用
doEndTag()
方法的返回值 ;
SKIP_PAGE
代表后续的jsp
页面的内容,不再进行处理,也就是不会显示;
@Override
public int doEndTag() throws JspException {
return Tag.SKIP_PAGE;
}
自定义标签功能扩展
控制
JSP
页面中某一部分的内容是否显示;利用是否显示标签体的技术,即可完成 ;
控制整个页面是否显示;
做法:在整个页面的外部套个自定义标签,和上面一样 ;
控制重复输出页面内容
重写
doAfterBody()
即可;修改
jsp
页面内容输出 ;获取代表标签体的对象,对其进行改写,再利用
out
对象输出 ;
传统标签
上面我们学的自定义标签技术,属于 传统标签
;需要实现的方法比较多,需要我们自己去记忆;
看下面的图, 左边的是JSP2.0的技术,实现不同的功能需要实现不同的接口;同一个接口中还要选择重写哪些方法
可以明显的看出来,传统标签
很烦 ,需要我们记住的东西太多了;说实话,笔者在这篇博客的时候,早就忘记了上面的那些知识,也是根据当年的笔记 ,写的这个博客 ;
简单标签
现在我们学习 简单标签
,让标签处理器类 继承 SimpleTagSupport
接口;
简单标签
中最重要的两个方法:dotag()
、setJSPBody()
;
setJSPBody()
方法,在服务器执行标签的时候调用,将标签体作为对象传给自定义标签类对象;
doTag()
方法,服务器执行标签,都会调用这个方法;我们对标签的处理逻辑,都写在这个方法里面 ;
是否显示标签体
比如,我们想不显示标签体,那么,在这个方法中,就不做任何操作;反之,想显示标签体,就在方法中,获取标签体输出;
@Override public void doTag() throws JspException, IOException { //只有doTag方法里面,什么都不做,就不会显示标签体 }
控制显示样式
我们需要获取到标签体的字符串形式;
获取方法
:将标签体输送到StringWriter
流的缓冲区中,从缓冲区中获取标签体的内容,从而修改样式 ;
@Override
public void doTag() throws JspException, IOException {
// 获取标签体
JspFragment jspFragment = this.getJspBody();
// 将标签体输送到流的缓冲区中
StringWriter stringWriter = new StringWriter();
// 这个方法很重要,负责将标签体输送到特定的流里面;
jspFragment.invoke(stringWriter);
// 获取缓冲区中的内容
String s = stringWriter.toString();
s = s.toUpperCase();
this.getJspContext().getOut().write(s);
}
上面代码中最重要的一个方法:jspFragment.invoke(stringWriter);
参数用于指定将JspFragment
对象的执行结果写入到哪个输出流对象中,如果传递给参数的值为 null
,则将执行结果写入到 JspContext.getOut()
方法返回的输出流 out
对象中。(简而言之,可以理解为写给浏览器)
是否显示页面
不显示页面:在
doTage
方法中,抛出SkipPageException()
异常,方法抛出这个异常,服务器,就不会再计算剩下
的JSP页面了;@Override public void doTag() throws JspException, IOException { throw new SkipPageException() ; }
配置简单标签
配置简单标签的时候,配置标签体的时候,需要注意,不能写JSP
了,改写为scriptless
;Sun公司的意思是:以后 脚本表达式
也不要出现在JSP
里面了,统统用标签代替 ;
<tag>
<name>....</name>
<tag-class>xxxx.....</tag-class>
<!-- 传统标签是写JSP,简单标签写 scriptless-->
<body-content>scriptless</body-content>
</tag>
与传统标签不同的是
,简单标签执行结束以后,创建的对象,并不会像传统标签那样驻留在服务器内存中,而是被垃圾回收器回收掉 ;
开发带属性的标签
要想让一个自定义标签具有属性,通常需要完成两个任务:
- 在标签处理器中编写
每个属性
对应的setter
方法 - 在
tld
文件中描述标签的属性
为自定义标签定义属性的时候,每个属性都必须按照JavaBean
的属性命名方式,在标签处理器中定义属性名对应的setter
方法,用来接收JSP
页面调用自定义标签时传递进来的属性值。
例如,属性uri
,在标签处理器类中就要定义相应的 setUri(String uri)
方法。
在标签处理器中定义响应的set
方法后,JSP引擎
在解析执行开始标签前
,也就是doStarTag()
方法前,会调用set属性方法,为标签设置属性;