JSP第七篇【自定义标签就是如此简单】(修订版)(一)

简介: 为了简化标签开发的复杂度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。。一般来说,实现了SimpeTag接口的标签称之为简单标签


SimpleTag接口


  • 首先我们来看一下它的源码吧
public interface SimpleTag extends JspTag {
    void doTag() throws JspException, IOException;
    void setParent(JspTag var1);
    JspTag getParent();
    void setJspContext(JspContext var1);
    void setJspBody(JspFragment var1);
}
  • setParent()和getParent()方法就不多说了,我们来看一下剩下的3个方法
void doTag() throws JspException, IOException;
void setJspContext(JspContext var1);
void setJspBody(JspFragment var1);
  • 明显地:
  • doTag()就是我们要写代码处理逻辑地方
  • setJspContext(JspContext var1)是将PageContext对象传递给标签处理器类(PageContext是JspContext的子类)
  • setJspBody(JspFragment var1)把代表标签体的JspFragment对象传递给标签处理器对象


快速入门


  • 一般地,我们做开发都是继承SimpleTagSupport类(该类实现了SimpleTag)来编写自定义标签
  • 下面我们就来个快速入门吧
  • 目标:传入字符串格式就可以显示想要的格式日期,对比之前传统标签的,看有什么不同之处
  • 标签处理器类:
public class Demo1 extends SimpleTagSupport {
    String format = null;
    @Override
    public void doTag() throws JspException, IOException {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
        this.getJspContext().getOut().write(simpleDateFormat.format(new Date()));
    }
    public String getFormat() {
        return format;
    }
    public void setFormat(String format) {
        this.format = format;
    }
}
  • tld文件:
<tag>
    <name>formatDate</name>
    <tag-class>tag.Demo1</tag-class>
    <body-content>tagdependent</body-content>
    <attribute>
        <name>format</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
  • 效果:

1.png2.png

  • 简单标签的好处就是不用去理会doStartTag、doEndTag、SKIP_BODY以及一系列的方法和属性!
  • 简单标签一个doTag()方法走天下!


SimpleTagSupport类的执行顺序:


  • ①WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象
  • ②WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。【注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法】
  • ③如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
  • ④如果简单标签有标签体,容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来
  • ⑤执行标签时:容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

3.png


深入简单标签


在我们讲解传统标签的时候,配合着SKIP_BODY、SKIP_PAGE等变量可以实现如下的功能:

  1. 控制jsp页面某一部分内容(标签体)是否执行
  2. 控制整个jsp页面是否执行
  3. 控制jsp页面内容重复执行
  4. 修改jsp页面内容输出

  • 简单标签可没有这些变量呀,那它怎么才能实现上面那些功能呢?
  • 在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况,我们来测试一下,在上面例子的代码中添加
throw new SkipPageException();
  • 效果:

4.png

  • 至于其他的功能下面会讲到


带标签体的简单标签


  • SimpleTagSupport也可以带标签体,但是处理方法和传统标签完全不同
  • 传统标签是这样子的:将标签体的内容通过setBodyContent()注入到BodyContent对象中。
  • 简单标签是这样子的:通过JspFragment对象实现!
  • 我们来看一下JspFragment对象的源码吧
public abstract class JspFragment {
    public JspFragment() {
    }
    public abstract void invoke(Writer var1) throws JspException, IOException;
    public abstract JspContext getJspContext();
}

JspFragment对象十分简单,重要的只有invoke(Writer var1)方法(获取JspContext对象并不重要,在标签描述器上就可以获取到了)

public abstract void invoke(java.io.Writer out) :

  • 用于执行JspFragment对象所代表的JSP代码片段
  • 参数out用于指定将JspFragment对象的执行结果写入到哪个输出流对象中,如果传递给参数out的值为null,则将执行结果写入到JspContext.getOut()方法返回的输出流对象中。(简而言之,可以理解为写给浏览器)

  • 下面是标签处理器类的代码
public class Demo1 extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
        //得到代表标签体的对象
        JspFragment jspFragment = getJspBody();
        //invoke方法接收的是一个Writer,如果为null,就代表着JspWriter(),将标签体的数据写给浏览器!
        jspFragment.invoke(null);
    }
}
  • 效果:

5.png

  • 既然标签体的内容是通过JspFragment对象的invoke()方法写给浏览器的,那么那么那么,我只要控制好invoke()方法,我想干什么就干什么!
  • 也就是说:
  • 不调用invoke()方法,标签体内容就不会输出到浏览器上
  • 重复调用invoke()方法,标签体内容就会被重复执行
  • 若想在标签处理器中修改标签体内容,只需在调用invoke方法时指定一个可取出结果数据的输出流对象(例如StringWriter),让标签体的执行结果输出到该输出流对象中,然后从该输出流对象中取出数据进行修改后再输出到目标设备,即可达到修改标签体的目的
  • 来来来,我们来试验一下:
  • 不调用invoke()方法
public void doTag() throws JspException, IOException {
    //得到代表标签体的对象
    JspFragment jspFragment = getJspBody();
    //jspFragment.invoke(null);
}
  • 标签体的内容没有输出
    6.png
  • 调用两次invoke()方法
public void doTag() throws JspException, IOException {
    //得到代表标签体的对象
    JspFragment jspFragment = getJspBody();
    jspFragment.invoke(null);
    jspFragment.invoke(null);
}
  • 标签体的内容被输出了两次
    7.png
  • invoke()方法指定别的输出流(StringWriter),将标签体的内容写到流对象中,再通过流对象把数据取出来,达到修改的目的。
//得到代表标签体的对象
JspFragment jspFragment = getJspBody();
//创建可以存储字符串的Writer对象
StringWriter stringWriter = new StringWriter();
//invoke()方法把标签体的数据都写给流对象中
jspFragment.invoke(stringWriter);
//把流对象的数据取出来,流对象的数据就是标签体的内容
String value = stringWriter.toString();
//将数据改成是大写的,写到浏览器中
getJspContext().getOut().write(value.toUpperCase());
  • 标签体的内容被改成了大写!

8.png

我们可以发现,传统标签能完成的功能,简单标签都可以完成,并且更为简单!



自定义标签的应用


既然我们学了简单标签,我们就用简单标签来做开发吧!

防盗链

在讲解request对象的时候,我们讲解过怎么实现防盗链的功能。现在我们使用标签来进行防盗链

模拟下场景:1.jsp页面是海贼王资源,2.jsp页面提示非法盗链,index1.jsp是我的首页。别人想要看我的海贼王资源,就必须通过我的首页点进去看,否则就是非法盗链!

  • 标签处理器的代码
@Override
public void doTag() throws JspException, IOException {
    //如果想要做成更加灵活的,就把站点设置和资源设置成标签属性传递进来!
    //等会我们要获取得到request对象,需要使用到JspContext的子类PageContext
    PageContext pageContext = (PageContext) this.getJspContext();
    //获取request对象
    HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();
    //获取到referer
    String referer = httpServletRequest.getHeader("Referer");
    //获取到response对象,等会如果是非法盗链,就重定向别的页面上
    HttpServletResponse httpServletResponse = (HttpServletResponse) pageContext.getResponse();
    //非法盗链!
    if (referer == null || !referer.startsWith("http://localhost:8080/zhongfucheng")) {
        //2.jsp提示了非法盗链!
        httpServletResponse.sendRedirect("/zhongfucheng/2.jsp");
        //不执行页面下面的内容了,保护页面
        throw new SkipPageException();
    }
}
  • 1.jsp代码:
<zhongfucheng:Referer/>
<html>
<head>
    <title></title>
</head>
<body>
海贼王最新资源
</body>
</html>
  • index1.jsp代码
<h1>这是首页!</h1>
<a href="${pageContext.request.contextPath}/1.jsp"> 海贼王最新资源</a>
  • 2.jsp代码
<body>
你是非法盗链的!!!!!!
</body>
  • 第一次我是直接访问1.jsp,Referer是为空的,所以是非法盗链。第二次我是通过从首页点进去看的,所以可以访问1.jsp。效果图:

9.gif


if标签


在JSTL中,我们已经使用过了<c:if/>标签了,现在我们学习了自定义标签,可以开发类似于JSTL的if标签了!

既然是if标签,那么就需要编写带属性和带标签体的标签(需要判断是true还是false呀!,通过判断是否为真值来决定是否执行标签体的内容)

  • 标签处理器代码
public class Demo1 extends SimpleTagSupport {
    //定义一个Boolean类型的变量
    boolean test ;
    @Override
    public void doTag() throws JspException, IOException {
        //获取到代表标签体内容的对象
        JspFragment jspFragment = this.getJspBody();
        //如果为真值才执行标签体的内容
        if (test == true) {
            jspFragment.invoke(null);
        }
    }
    public boolean isTest() {
        return test;
    }
    public void setTest(boolean test) {
        this.test = test;
    }
}
  • tld文件的代码
<tag>
    <name>if</name>
    <tag-class> tag.Demo1</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
        <name>test</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
  • 本来就没有user这个域对象属性,所以user就是为null

10.png

  • 将user改成不为null,浏览器就没有输出了

11.png

目录
相关文章
|
5月前
|
Java
JSP自定义标签【下】
JSP自定义标签【下】
49 0
|
6月前
|
Java
jsp自定义标签
jsp自定义标签
43 1
|
17天前
|
Java 数据安全/隐私保护
Jsp自定义标签(foreach,dept,select)
Jsp自定义标签(foreach,dept,select)
28 0
|
4月前
|
XML Java 数据格式
JSP自定义标签
JSP自定义标签
53 0
|
5月前
|
存储 Java 开发者
JSP自定义标签(下)
JSP自定义标签(下)
39 0
|
5月前
|
XML Java 数据格式
JSP自定义标签
JSP自定义标签
32 0
|
5月前
|
XML Java 数据格式
JSP自定义标签
JSP自定义标签
42 0
|
5月前
|
Java API
JSP自定义标签【上】
JSP自定义标签【上】
36 0
|
6月前
|
存储 安全 Java
JSP自定义标签(进阶)
JSP自定义标签(进阶)
17 0
|
6月前
|
XML Java 数据格式
JSP自定义标签(基础)
JSP自定义标签(基础)
22 0