(十三)自定义JSTL标签
前面的博客,我们讲过了 自定义
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属性方法,为标签设置属性;
(十三)自定义JSTL标签的更多相关文章
- 自定义JSTL标签和函数库
一.自定义JSTL标签 1.编写标签处理类: (1)实现 SimpleTag 接口,通过 setJspContext()方法可以获取到 jspContext 对象,实际上也是 pageContext ...
- 如何自定义JSTL标签与SpringMVC 标签的属性中套JSTL标签报错的解决方法
如何自定义JSTL标签 1.创建一个类,从SimpleTagSupport继承 A) 通过继承可以获得当前JSP页面上的对象,如JspContext I) 实际上可以强转为PageContext II ...
- 自定义EL函数、自定义JSTL标签
自定义EL函数 1.做一个类(静态) package com.maya.el; public class ELBiaoDaoShi { public static String TiHuan(Stri ...
- 自定义jstl标签库
开发环境:Spring+SpringMVC +Maven +Mybatis JSTL 标签库的配置: 导入对应的 jstl.jar 和 standard.jar ,我使用的配置如下: <prop ...
- 自定义jstl标签*
原文链接:https://www.it610.com/article/442039.htm 步骤如下: 1.写tld文档:用来指定标签的名字,标签库等. 2.写标签处理器类. 3.配置到web.xml ...
- 自定义jstl标签开发
从jsp1.1开始就可以在jsp页面中使用自定义标签了,使用自定义标签不但可以实现代码重用,而且可以使jsp代码更简洁.Jsp2.0的标签扩展API中又增加了SimpleTag接口和其实现类Simpl ...
- EL表达式、JSTL标签库
一.EL(Expression Language)表达式 语法结构:${var} 若要停用对EL表达式的评估的话,需要使用page指令将isELIgnored属性值设为true: <%@ pag ...
- JSP学习笔记(3)——JSTL 标签库
JSP Standard Tag Lib,名为JSP标准标签库,设计的目的主要用来方便我们将数据输出,而不是使用JSP中的语法<% %> <%= %> <%! %> ...
- 12 自定义标签/JSTL标签库/web国际化/java web之设计模式和案例
EL应用 自定义一个标签,实现两个字符串的相加 1回顾 1.1servlet生命周期 init(ServletConfig) service ...
随机推荐
- [TJOI2019]甲苯先生的字符串——矩阵乘法+递推
题目链接: [TJOI2019]甲苯先生的字符串 我们用一个$26*26$的$01$矩阵记录任意两个字符是否能相邻. 设$f[i][j]$表示处理完前$i$个字符,第$i$个字符为$j$的方案数. 可 ...
- BaggingClassifier
写在前面 Ensemble methods 组合模型的方式大致为四个:/bagging / boosting / voting / stacking ,此文主要简单叙述 bagging算法. 算法 ...
- 解决用root用户及密码可以直接登陆某LINUX系统,但是用ssh登陆,系统却总是提示密码不对
引用 vi /etc/ssh/sshd_config 将PermitRootLogin项改为yes service sshd restart 重启sshd服务即可
- 唯品会HDFS性能挑战和优化实践
唯品会HDFS性能挑战和优化实践 原创: 大数据平台 唯技术 4月1日 https://mp.weixin.qq.com/s/LMa99ubgACI4eaDV3G-6gw
- angular 中父子路由
1. 创建组件引入组件 import { NewsaddComponent } from './components/newsadd/newsadd.component'; import { News ...
- Angular 中的 dom 操作(ViewChild)以及父子组件中通过 ViewChild 调用子组件的方法
<app-header #header></app-header> <div #myBox> 我是一个dom节点 </div> <button ( ...
- SQL 引号中的问号在PrepareStatement 中不被看作是占位符
SQL 引号中的问号在PrepareStatement 中不被看作是占位符. 如:SELECT P.NAME, S.YEAR, S.QUANTITY FROM SALES S LEFT JOIN PR ...
- java最简单复制文件方法,不依赖任何框架
java最简单复制文件方法 把java2.txt内容复制到java.txt中 import java.io.File; import java.io.IOException; import jav ...
- Python与C/C++相互调用(python2 调c++那个试了ok)
一.问题 Python模块和C/C++的动态库间相互调用在实际的应用中会有所涉及,在此作一总结. 二.Python调用C/C++ 1.Python调用C动态链接库 Python调用C库比较简单,不经过 ...
- Kafka——副本(Replica)机制
副本定义 Kafka 是有主题概念的,而每个主题又进一步划分成若干个分区.副本的概念实际上是在分区层级下定义的,每个分区配置有若干个副本. 所谓副本(Replica),本质就是一个只能追加写消息的提交 ...