JSTL:JSP自定义标签库学习总结

自定义标签可以极大的简化jsp页面,在JSP页面中调用标签其实就是调用某个标签处理类中的方法,因此,想要了解自定义标签,核心还是需要掌握标签处理类。这里常用的几个标签处理类有:

一、自定义标签的概念

  • JSP标签分为标准JSP 环境自带的标签(即前面章节中学习过的JSP 动作标签)和JSP 自定义标签。JSP 自定义标签是用户定义的标记,它遵循XML 语法。当servlet 容器处理自定义标记时,会自动调用一个Java 类文件完成相对应的功能。
  • Java 开发人员编写标记处理程序类以处理标记并处理所有需要的Java 代码和数据操作。对于Web页面设计者来说,自定义标记与标准HTML 标记使用起来没什么区别,但HTML 标记只能完成前台显示的功能,而自定义标记可以在后台完成某些操作。
  • 正确编写自定义标记可以让 Web 设计者创建、查询和操作数据而无需编写一行Java 代码。正确使用自定义标记使 Java 开发人员不必再在编码过程中考虑表示层。这样应用程序开发小组的每一位成员都可以关注于他或者她最擅长的事物。
  • 所以说,JSP 自定义标记为在动态Web 页中将表示与业务逻辑分离提供了一种标准化的机制,使页面设计者可以将注意力放到表示上,而应用程序开发人员编写后端的代码。

二、自定义标签常用的需继承的类、需实现的接口

  • SimpleTag: SimpleTag接口是JSP2.0中新给出的接口,用来简化自定义标签,所以现在我们基本上都是使用SimpleTag。他是一个简单标签机制
  • SimpleTagSupport: 要比实现SimpleTag接口方便太多了,现在你只需要重写doTag()方法,其他方法都已经被SimpleTagSuppport完成了。他不是一个接口,所以只能继承这个SimpleTagSupport类。
  • TagSupport:自定义标签default Adapter模式(缺省适配模式)
  • BodyTagSupport:自定义标签标签体交互模式

三、SimpleTag:简单标签机制

JSP 2.0 中加入了新的创建自定义标记的API:javax.servlet.jsp.tagext.SimpleTag,该API 定义了用来实现简单标记的接口。和JSP 1.2 中的已有接口不同的是,SimpleTag 接口不使用doStartTag()和doEndTag()方法,而提供了一个简单的doTag()方法。这个方法在调用该标记时只被使用一次。一个自定义标记中实现的所有逻辑都在这个方法中实现。相对JSP1.2 中自定义标记机制,SimpleTag 的方法和处理周期要简单得多。

(一)SimpleTag接口内容

  1. void doTag():标签执行方法;
  2. JspTag getParent():获取父标签;
  3. void setParent(JspTag parent):设置父标签
  4. void setJspContext(JspContext context):设置PageContext
  5. void setJspBody(JspFragment jspBody):设置标签体对象;
    请记住,万物皆对象!在JSP页面中的标签也是对象!你可以通过查看JSP的源码,清楚的知道,所有标签都会变成对象的方法调用。标签对应的类我们称之为“标签处理类”!

(二)SimpleTag标签的生命周期

  1. 当容器(Tomcat)第一次执行到某个标签时,会创建标签处理类的实例;

  2. 然后调用setJspContext(JspContext)方法,把当前JSP页面的pageContext对象传递给这个方法;

  3. 如果当前标签有父标签,那么使用父标签的标签处理类对象调用setParent(JspTag)方法;

  4. 如果标签有标签体,那么把标签体转换成JspFragment对象,然后调用setJspBody()方法;

  5. 每次执行标签时,都调用doTag()方法,它是标签处理方法。

(三)SimpleTag接口具体实现类

public class userTldClassOne implements SimpleTag {
    
    private JspFragment jspFragment;
    private PageContext pageContext;
    private JspTag jspTag;

    @Override
    public void doTag() throws JspException, IOException {
        // TODO: 2021/6/26 将标签
        jspFragment.invoke(null);
    }

    @Override
    // TODO: 2021/6/26 如果标签是嵌套在父标签中,则会所父标签传递进来
    public void setParent(JspTag jspTag) {
        this.jspTag = jspTag;
    }

    @Override
    // TODO: 2021/6/26  获取父标签
    public JspTag getParent() {
        return this.jspTag;
    }

    @Override
    // TODO: 2021/6/26 这个方法是在窗口执行标签后,生产标签处理类实例,这个方法为标签处理类实例的一个方法
    //TODO:主要用途是将tomcat容器中的PageContext传递给该标签处理类实例
    public void setJspContext(JspContext jspContext) {
        this.pageContext = (PageContext) jspContext;
    }

    @Override
    // TODO: 2021/6/26 这个地方将标签体中的内容传递进来
    public void setJspBody(JspFragment jspFragment) {
        this.jspFragment = jspFragment;
    }
}

(四)TLD(标签库描述文件)

标签库描述文件是用来描述当前标签库中的标签的!标签库描述文件的扩展名为tld,你可以把它放到WEB-INF下,这样就不会被客户端直接访问到了。

<?xml version="1.0" encoding="ISO-8859-1"?>

<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.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>http://mycompany.com</uri>

    <!-- Invoke 'Generate' action to add tags or functions -->
    <tag>
        <name>userout</name>
        <tag-class>tldone.userTldClassOne</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>username</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

在这里插入图片描述

(五)在web.xml中部署标签库文件

一个标签对应一个标签处理类,在TLD文件中对标签进行定义和描述后,想要在jsp页面中使用标签,则必须在web.xml中对标签库进行加载和描述,具体实现代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
     xmlns="http://java.sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

    <jsp-config>
        <taglib>
            <taglib-uri>http://localhost:8081/newtaglib</taglib-uri>
            <taglib-location>/WEB-TLD/usertld.tld</taglib-location>
        </taglib>
    </jsp-config>

</web-app>

在这里插入图片描述

(六)在JSP中使用自定义的标签

在JSP中使用自定义标签,主要有两个步骤

  • 使用taglib导入标签库;
  • 使用标签;
<%@ page pageEncoding="UTF-8" contentType="text/html; charset=utf-8"  %>
<%@ taglib uri="http://localhost:8081/newtaglib" prefix="lo" %>
<html>
<body>
<h2>Hello World!</h2>
<lo:userout username="hello java"/>
</body>
</html>

在这里插入图片描述

四、SimpleTagSupport

继承SimpleTagSuppport:要比实现SimpleTag接口方便许多了,只需要重写doTag()方法,其他方法都已经被SimpleTagSuppport完成。

(一)定义继承于SimpleTagSupport的标签处理类

public class UserTldClasss extends SimpleTagSupport {
    private String username;
    private PageContext pageContext;

    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public void doTag() throws JspException, IOException {
        // TODO: 2021/6/26 getJspContext()方法是将tomcat容器中PageContext的实例传递进来,PageContext包含jsp页面运行的基本信息
        pageContext = (PageContext)getJspContext();
        JspWriter out = pageContext.getOut();
        out.write("hello java");
    }
}

(二)JSP中标签体几个选项

标签体有内容,首先得确定标签体的可选值,可选值的具体位置在tld文件中,如图所示:
在这里插入图片描述
具体可选内容如下:

  • empty:无标签体。
  • JSP:传统标签支持它,SimpleTag已经不再支持使用JSP。标签体内容可以是任何东西:EL、JSTL、<%=%>、<%%>,以及html;
  • scriptless:标签体内容不能是Java脚本,但可以是EL、JSTL等。在SimpleTag中,如果需要有标签体,那么就使用该选项;
  • tagdependent:标签体内容不做运算,由标签处理类自行处理,无论标签体内容是EL、JSP、JSTL,都不会做运算。这个选项几乎没有人会使用!

(三)如何获得JSP页面中标签体的内容

获得标签体的内容,在SimpleTagSupport中,主要是通过getJspBody()方法,具体步骤如下:

  • 获取标签体对象:JspFragment jspBody = getJspBody();
  • 把标签体内容输出到页面:jspBody.invoke(null);
  • tld中指定标签内容类型:scriptless。

如果想把标签体中的内容输出到字符串,可以使用StringWriter对象实例,即:jsjspBody.invoke(stringWriter)

public class UserTldClasss extends SimpleTagSupport {
    private String username;
    private PageContext pageContext;

    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public void doTag() throws JspException, IOException {
        JspFragment jspBody = getJspBody();
        // TODO: 2021/6/26 想将标签体中的内容提取出来,可以使用StringWriter类,将内容存放到StringWriter类实例中
        StringWriter stringWriter = new StringWriter();
        jspBody.invoke(stringWriter);
        // TODO: 2021/6/26 下面的方法则是直接将标签体的内容输出到页面 
        jspBody.invoke(null);
        String s = stringWriter.toString();
    }
}

(四)在TLD文件中设置标签体选项

在前面已经讲到,标签体的稳定项有四种类型,如果标签体需要有内容,一般都会使用scriptless选项
在这里插入图片描述

  <tag>
        <name>userout</name>
        <tag-class>tldone.userTldClassOne</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>username</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

(五)不执行标签后面JSP页面内容

在一定的情况下,当我们执行完成标签中的标签内容后,不再想执行标签体后面的Jsp页面内容,那么就就需要在doTag()方法中抛出一个SkipPageException。
在这里插入图片描述

    @Override
    public void doTag() throws JspException, IOException {
        JspFragment jspBody = getJspBody();
        // TODO: 2021/6/26 想将标签体中的内容提取出来,可以使用StringWriter类,将内容存放到StringWriter类实例中
        StringWriter stringWriter = new StringWriter();
        jspBody.invoke(stringWriter);
        // TODO: 2021/6/26 下面的方法则是直接将标签体的内容输出到页面
        jspBody.invoke(null);
        String s = stringWriter.toString();
        System.out.println(s);
        // TODO: 2021/6/26 不再想执行JSP页面后面的内容 
        throw new SkipPageException();
    }

(六)标签处理类属性值的传递

标签处理类带有属性时,想要在JSP页面中通过标签将值传递到标签处理类,需有以下几个条件:

  1. 标签处理类的属性必须设置set方法,只有设置set方法,标签处理类才能接收到JSP页面中,通过标签传递进来的值;
  2. 在TLD标签库描述文件中,须设置相关的选项,主要有两个选项:
  • required选项: 明确是否是必须传递值的属性;
  • rtexprvalue选项:是否可以接受EL表达式;
    在这里插入图片描述
  1. 在web.xml中对TLD文件进行加载描述
  2. 在JSP页面中导入自定义的标签库(TLD),并使用.

五、TagSupport与BodyTagSupport

(一)TagSupport简介

  1. 这是一种不带标记体的标签,所以该类标签的处理类直接继承javax.servlet.jsp.tagext.TagSupport即可。TagSupport 的主要方法如下:
  • public int doStartTag() throws JspException.在WEB 容器遇到标签开始时,该方法会运行。
  • public int doEndTag() throws JspException
    在WEB 容器遇到标签结束时,该方法会运行。

这个地方需要理解的是:TagSupport是默认不带标签体的回传设置,如果需要与标签体进行交互,则需要分别设置doStartTag(),doEndTag()和doAfterBody()的回传参数设置.

  1. TagSupport 类中有一个重要的成员:pageContext,该成员的功能与JSP 的内置对象pageContex完全相同。通过该对象可以得到其他几个JSP 对象的引用。这样,我们就可以在JAVA 类中与JSP 进行交互了。如: JspWriter out=pageContext.getOut();这一语句可以得到JSP 内置对象out 的引用,通过out 我们就可以向客户端浏览器中输出内容了。要使用其他几个JSP 对象原理与此相同。

(二)TagSupport中回传参数的设置

  1. 如果在自订标签库时,并不需要对标签本体作处理,则您可以继承TagSupport类别.因为TagSupport默认的回传上参数是设置为不对标签本体进行处理的.
  2. TagSupport默认方法是:doStartTag()和doEndTag(),这是不执行标签体中的内容所需要用到的两个方法.其中doStartTag()方法是遇到标签开始时会呼叫的方法,其合法的回传值是EVAL_BODY_INCLUDE与SKIP_BODY,前者表示将显示标签间的本体文字,后者表示不显示标签间的本体文字;doEndTag()方法是在遇到标签结束时呼叫的方法,其合法的回传值是EVAL_PAGE与SKIP_PAGE,传回前者表示处理完标签后继承执行以下JSP网页,传回后者则表示不处理接下来的JSP网页。
  3. 如果TagSupport需要处理标签体,则会用到doAfterBody()文方法,则需要修改标签处理类中doStartTag(),doEndTag() 和doAfterBody()各个的回传参数.doAfterBody(),这个方法是在显示完标签间文字本体之后呼叫的,其合法的回传值有EVAL_BODY_AGAIN与SKIP_BODY,如果传回前者,则会再显示一次标签间的文字本体,传回后者则继承执行标签处理的下一步。

(三)TagSupport中预设的回传值

预设的回传值是:doStartTag()回传 SKIP_BODY、doAfterBodyTag()回传SKIP_BODY、doEndTag()回传EVAL_PAGE。所以在继承 TagSupport之后,如果没有改写任何的方法,则在标签处理上执行的顺序是:

  • doStartTag() -> 不显示本体文字 -> doEndTag() -> 执行接下来的JSP网页 
    

如果您改写了doStartTag(),则您必须指定回传值,如果指定了EVAL_BODY_INCLUDE,则预设会照以下的顺序执行:

  • doStartTag() -> 显示本体文字 -> doAfterBodyTag()->doEndTag()->执行接下来的JSP网页
    

其总体流程图如下:
在这里插入图片描述

(四)TagSupport开发示例

1. 定义标签处理类

此开发示例不设计为不含标签体的,所以各方法的主要回传参数设置如下:

  1. 在自定义的类中,重写了父类TagSupport 的两个方法:doStartTag()、doEndTag(),在容器遇到标记开始时会运行doStartTag(),遇到标记结束时运行doEndTag()方法;
  2. doStartTag()方法的返回值:通常可以取两个值:
  •  EVAL_BODY_INCLUDE——包含标记体,本例中要编写自结束标记所以不使用该值;
    
  •  SKIP_BODY——跳过标记体,即不处理标记体,开发自结束标记应该使用该值。
    
  1. doEndTag()方法的返回值:通常可以取两个值:
  •  SKIP_PAGE——返回这个值,则终止页面执行;
    
  •  EVAL_PAGE——返回该值则处理完当前标记后,JSP 页面中止运行。
    

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import java.io.IOException;

public class UserTagSupport extends TagSupport {

    // TODO: 2021/6/26
    public static final long serialVersionUID = 1L;

    // TODO: 2021/6/26 要这个地方定义属性 
    private int salary;

    // TODO: 2021/6/26 必须设置set方法才从能jsp标签中得到传递进来的值 
    public void setSalary(int salary) {
        this.salary = salary;
    }

    @Override
    // TODO: 2021/6/26  oStartTag()方法是遇到标签开始时会呼叫的方法,其合法的回传值是EVAL_BODY_INCLUDE与SKIP_BODY,
    //  前者表示将显示标签间的本体文字,后者表示不显示标签间的本体文字;
    public int doStartTag() throws JspException {
        // TODO: 2021/6/26 这个地方向jsp页面输出5根水平线
        JspWriter out = pageContext.getOut();
        try {
            out.println("<h4>开始执行doStartTag()</h4>");
            for (int i = 0; i < 5; i++) {
                out.println("<hr>");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // TODO: 2021/6/26 跳远标签体的执行,直接跳转到doEndTag()方法
        return TagSupport.SKIP_BODY;
    }

    @Override
    // TODO: 2021/6/26  doEndTag()方法是在遇到标签结束时呼叫的方法,其合法的回传值是EVAL_PAGE与SKIP_PAGE,
    //  传回前者表示处理完标签后继承执行以下JSP网页,传回后者则表示不处理接下来的JSP网页。
    public int doEndTag() throws JspException {
        JspWriter out = pageContext.getOut();
        try {
            out.println("开始执行doEndTag()方法");
            for (int i = 0; i < 10; i++) {
                out.println("现在的输出结果为:" + i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        // TODO: 2021/6/26 执行完doEndTag()后,这个回传值会继续执行JSP页面的其余内容
        return EVAL_PAGE;
    }

    @Override
    // TODO: 2021/6/26  doAfterBody(),这个方法是在显示完标签间文字本体之后呼叫的,
    //  其合法的回传值有EVAL_BODY_AGAIN与SKIP_BODY,如果传回前者,则会再显示一次标签间的文字本体,
    //  传回后者则继承执行标签处理的下一步。
    public int doAfterBody() throws JspException {
        return super.doAfterBody();
    }
}

2. 创建标签库描述符文件(TLD)

在这里插入图片描述

    <tag>
        <name>usertag</name>
        <tag-class>tldone.UserTagSupport</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>salary</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

3. 在web.xml文件中部署TLD文件
   <jsp-config>
        <taglib>
            <taglib-uri>http://localhost:8081/newtaglib</taglib-uri>
            <taglib-location>/WEB-TLD/usertld.tld</taglib-location>
        </taglib>
    </jsp-config>
4. 在JSP中加载TLD文件并使用

在这里插入图片描述

(五)TagSupport和BodyTagSupport的区别

TagSupport和BodyTagSupport的区别,主要还是因为doStartTag(),doEndTag()和doAfterBodyTag()的回传参数不同而不同.

  • TagSupport不执行标签体内容,在doStartTag()中,回传参数为:SKIP_BODY,在doEndTag()中,回传参数为:EVAL_PAGE;
  • BodyTagSupport,需要执行标签体中的内容,并且进行交互,因此,在doStartTag()中,回传参数为EVAL_BODY_INCLUDE;在doAfterBodyTag()中的回传参数根据交互判断结果,如果没有执行完此,则回传参数为:EVAL_BODY_AGAIN,如果执行完毕,则回传参数为:SKIP_BODY; 在doEndTag()中,如查要执行jsp页面,则回传参数为:EVAL_PAGE,如果不执行其余的jsp页面,则回传参数为:SKIP_PAGE

其具体的区别还是前面所学,从流程图可以看出:

  1. TagSupport流程图:
  • doStartTag() -> 不显示本体文字 -> doEndTag() -> 执行接下来的JSP网页
    

2.BodyTagSupport流程图:

  • doStartTag() -> 显示本体文字 -> doAfterBodyTag()->doEndTag()->执行接下来的JSP网页
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值