• 如何自定义标签


    自定义标签的步骤:

    1.创建一个自定义标签处理器类,实现SimpleTag接口

    public class HelloSimpleMyTag implements SimpleTag {

    2.在WEB-INF文件夹下新建一个.tld(标签库描述文件)为扩展名的xml文件.并拷入固定的部分:并对

    description,display-name,tlib-version,short-name,uri做出修改

    <?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">
      
      <!-- 描述TLD文件 -->
      <description>MyTag 1.0 core library</description>
      <display-name>MyTag core</display-name>
      <tlib-version>1.0</tlib-version>
      
      <!-- 建议在jsp页面上使用的前缀 -->
      <short-name>test</short-name>
      <!-- 作为tld文件的id,用来标示当前的tld文件,多个tld文件的uri不能重复,在
      JSP页面 通过 taglib 的属性 uri来引入tld文件     -->
      <uri>http://www.test.com/mytag/core</uri>
    </taglib>

    3.在tld文件中描述自定义的标签

    <tag>
          <!-- 标签的名字,在jsp页面上使用标签的名字 -->
          <name>hello</name>
          <!-- 标签所在的全类名 -->
          <tag-class>com.java.tag.HelloSimpleMyTag</tag-class>
          <!-- 标签体类型 -->
         <body-content>empty</body-content>
      </tag>

    4.在jsp页面使用自定义标签

    <%@ taglib uri="http://www.atguigu.com/mytag/core" prefix="test" %>
    <test:hello/>

    使用自定义标签时,prefix可以自己定义,通常会推荐tld文件中的<short-name>

    -------------------------------------------------------------------------------------

    通过使用自定义标签,会发现,标签处理器类的 setJspContext(),doTag()会被调用

    public class HelloSimpleMyTag implements SimpleTag {
    
        @Override
        public void doTag() throws JspException, IOException {
        }
    
        @Override
        public JspTag getParent() {
            return null;
        }
    
        @Override
        public void setJspBody(JspFragment arg0) {
        }
      //JSP引擎调用
        @Override
        public void setJspContext(JspContext arg0) {
        }
    
        @Override
        public void setParent(JspTag arg0) {
        }
    
    }    

    在调用setJspContext(JspContext arg0) 时传入JspContext 对象,

    通过J2EE API 发现

    javax.servlet.jsp
    Class JspContext

    java.lang.Object
    javax.servlet.jsp.JspContext
    
    Direct Known Subclasses:
    PageContext

    PageContext 是 JspContext的一个子类,

    在setJspContext(JspContext arg0) 是传入一个PageContext对象,

        private PageContext pageContext;
      //JSP引擎调用,把实际代表PageContext对象传入 @Override
    public void setJspContext(JspContext arg0) { this.pageContext = (PageContext)arg0; }

    其中PageContext为JSP的一个隐含对象,通过PageContext可以获取JSP页面的其他8个隐含对象

    即可以在doTag()中对页面进行输出

       //执行标签体逻辑实际应该编写到该方法中 
      @Override
    public void doTag() throws JspException, IOException { pageContext.getOut().print("HelloWorld!");
    HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
    pageContext.getOut().println("Hello: "+request.getParameter("name"));
        }

    标签的属性:

    <atguigu:hello value="aaa" count="10"/>

    在标签上使用属性以下几个步骤

    1.需要在tld文件中添加标签属性的描述

    <tag>
          <!-- 标签的名字,在jsp页面上使用标签的名字 -->
          <name>hello</name>
          <!-- 标签所在的全类名 -->
          <tag-class>com.java.tag.HelloSimpleMyTag</tag-class>
          <!-- 标签体类型 -->
         <body-content>empty</body-content>
         <attribute>
             <!--该属性的名字,需要和标签处理器类settter 方法定义的属性相同  -->
             <name>value</name>
             <!-- 该属性是否必须 -->
             <required>true</required>
             <!--rtexprvalue :runtime expression value 
             当前属性是否可以接受运行时表达式的动态值  -->
             <rtexprvalue>true</rtexprvalue>
         </attribute>
         <attribute>
             <name>count</name>
             <required>false</required>
             <rtexprvalue>false</rtexprvalue>
         </attribute>
      </tag>
    <!--rtexprvalue :runtime expression value 
             当前属性是否可以接受运行时表达式的动态值  -->
             <rtexprvalue>true</rtexprvalue>
    可以动态赋值
    <atguigu:hello value="${param.name }" count="10"/>

    2.在标签处理器类中需要出现与上述属性名相同的成员变量

    public class HelloSimpleMyTag implements SimpleTag {
        
    //建议把所有类型都设置成String类型,因为从页面传过来是String类型
    private String value; private String count; public void setValue(String value) { this.value = value; } public void setCount(String count) { this.count = count; }

    当标签属性存在时会默认调用setXxx()

    次需求为将value的值打印count次

    <atguigu:hello value="${param.name }" count="10"/>
    @Override
        public void doTag() throws JspException, IOException {
    //        pageContext.getOut().println("HelloWorld!<br>");
    //        HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();
    //        pageContext.getOut().println("Hello: "+request.getParameter("name"));
            JspWriter out  = pageContext.getOut();
            int c = Integer.parseInt(count);
            for(int i = 0;i<c;i++){
                out.print(value);
                out.print("<br>");
            }
        }

    SimpleTag 有一个实现类 SimpleTagSupport

    javax.servlet.jsp.tagext 
    Interface SimpleTag

    All Superinterfaces:
    JspTag
    All Known Implementing Classes:
    SimpleTagSupport

    通过继承SimpleTagSupport可以简化实现

    public class HelloSimpleMyTag extends SimpleTagSupport {

    有此获取PageContext

    PageContext pageContext = (PageContext)getJspContext();

    带标签体的自定义标签

    1.若一个标签带有标签体

    <test:bodyContent>HelloWorld</atguigu:test>

    在自定义标签的标签处理器中使用JspFragment 对象封装标签体信息

    2.若配置了标签含有标签体,则JSP引擎会调用SimpleTagSupport中的setJspBody(JspFragment jspBody)

    把JspFragment对象传给标签处理器类,SimpleTagSupport中还定义了一个 protected JspFragment getJspBody()

    用于返回JspFragment对象.

    3.JspFragment 的 invoke(Writer):把标签体内容从Writer中输出,若为null,则等同于invoke(getJspContext().getOut()),即直接把标签体内容输出到页面.

    @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            JspFragment bodyContent = getJspBody();
            StringWriter sw = new StringWriter();
         //将标签体的内容输出到sw中
         bodyContent.invoke(sw); bodyContent.invoke(
    null);

    4.在tld文件中,使用body-content 节点来描述标签体的类型:

    <body-content>:指定标签体的类型,大部分情况下,取值为scriptless.可能有3中取值:

    empty:没有标签体

    scriptless:标签体可以包含 el 表达式和 JSP 动作元素,但不能包含JSP的脚本元素

    tagdependent:表示标签体交由标签本身去解析处理.

    若指定tagdependent 在标签体中的所有代码都会原封不动的交给标签处理器,而不是将执行结果传递给标签处理器

    如果指定 <body-content>scriptless</body-content>,在页面上使用

    <atguigu:bodyContent><%=request.getParameter("name") %></atguigu:bodyContent>

    会报错

    使用<body-content>tagdependent</body-content>

    <atguigu:bodyContent>${param.name }</atguigu:bodyContent>

    页面上显示结果为 ${param.name }

    example1: 写一个自定义标签将标签体内容变大写,重复time次 <test:printStr time="10">HelloWorld</test:printStr>

    public class PrintTimesStr extends SimpleTagSupport{
        
        private String time;
        
        public void setTime(String time) {
            this.time = time;
        }
        
        @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            JspFragment bodyContent = getJspBody();
            StringWriter sw = new StringWriter();
            bodyContent.invoke(sw);
            String content = sw.toString().toUpperCase();
            int count = 1;
            try {
                count = Integer.parseInt(time);
            } catch (Exception e) {
            }
            for(int i = 0;i<count;i++){
                getJspContext().getOut().write(content+"<br>");
            }
        }
    
    }
    <tag>
          <name>printStr</name>
          <tag-class>com.java.tag.PrintTimesStr</tag-class>
          <body-content>scriptless</body-content>
          <attribute>
              <name>time</name>
              <required>true</required>
              <rtexprvalue>true</rtexprvalue>
          </attribute>
      </tag>
    <test:printStr time="10">HelloWorld</test:printStr>

    example2:模拟<c:forEach>

    public class TestForEach extends SimpleTagSupport{
        
        private Collection<?> item;
        private String var;
        
        public void setItem(Collection<?> item) {
            this.item = item;
        }
        public void setVar(String var) {
            this.var = var;
        }
        
        @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            //遍历item对应的集合
            for(Object obj:item){
                //把正在遍历的集合放到pageContext中,键:var 值:正在遍历的对象
                getJspContext().setAttribute(var,obj);
                //把标签体内容直接输出到页面上
                getJspBody().invoke(null);
            }
        }
    
    }
    <tag>
          <name>forEach</name>
          <tag-class>com.java.tag.TestForEach</tag-class>
          <body-content>scriptless</body-content>
          <attribute>
              <name>item</name>
              <required>true</required>
              <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
              <name>var</name>
              <required>true</required>
              <rtexprvalue>true</rtexprvalue>
          </attribute>
      </tag>
    <%
            Customer customer1 = new Customer(1,"aa","aa@java.com");
            Customer customer2 = new Customer(2,"bb","aa@java.com");
            Customer customer3 = new Customer(3,"cc","aa@java.com");
            List<Customer> customers = new ArrayList<Customer>();
            customers.add(customer1);
            customers.add(customer2);
            customers.add(customer3);
            request.setAttribute("customers", customers);
        %>
    
        <test:forEach var="customer" item="${requestScope.customers }">
            ${customer.id }--${customer.name }--${customer.email }<br>
        </test:forEach>

    1--aa--aa@java.com
    2--bb--aa@java.com
    3--cc--aa@java.com

     带父标签的自定义标签

    1.父标签无法获取子标签的引用,父标签仅把子标签作为标签体来使用.

    2.子标签可以通过 getParent() 方法来获取父标签的引用(需继承SimpleTagSupport或自实现SimpleTag接口的该方法):

    若子标签的确有父标签,JSP引擎会把代表父标签的引用通过setParent(JspTag parent)赋给标签处理器

    3.注意:父标签的类型是JspTag类型.该接口是一个空接口,但是来统一SimpleTag和Tag的.因为没法确定父标签是传统标签还是简单标签.实际使用需要进行类型转换.

    4.在tld配置文件中,无需为父标签有额外的配置.但,子标签是以标签体的形式存在的,所以父标签的<body-content></body-content>

    需设置为scriptless ,子标签的<body-content></body-content>需要设置为empty

    5.实现:

    <c:choose>

      <c:when test="${param.age>24}">大学毕业</c:when>

      <c:when test="${param.age>20}">高总毕业</c:when>

      <c:otherwise>高中以下...</c:otherwise>

    </c:choose>   

      开发3个标签:choose,when,otherwise

      其中when标签有一个boolean类型的属性:test

      choose是when和otherwise的父标签

      when在otherwise之前使用

      在父标签choose中定义一个"全局"的boolean类型的flag:用于判断子标签在满足条件的情况下是否执行.

        若when的test为true他,且when的父标签也为true,则执行when的标签体(正常输出标签体的内容),同时把flag设置为false

        若when的test为true,且when的父标签的flag为false,则不执行标签体.

        若flag为true,执行otherwise的标签体.

    贴出代码

    public class ChooseTag extends SimpleTagSupport {
        
        private boolean flag = true;
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
        public boolean isFlag() {
            return flag;
        }
        
        @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            getJspBody().invoke(null);
        }
    }
    public class WhenTag extends SimpleTagSupport{
        
        private boolean test;
        public void setTest(boolean test) {
            this.test = test;
        }
        @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            if(test){
                ChooseTag chooseTag = (ChooseTag)getParent();
                boolean flag = chooseTag.isFlag();
                if(flag){
                    getJspBody().invoke(null);
                    chooseTag.setFlag(false);
                }
            }
        }
    }
    public class OtherWiseTag extends SimpleTagSupport {
        
        @Override
        public void doTag() throws JspException, IOException {
            super.doTag();
            ChooseTag chooseTag = (ChooseTag)getParent();
            boolean flag = chooseTag.isFlag();
            if(flag){
                getJspBody().invoke(null);
            }
        }
    
    }
    <tag>
          <name>choose</name>
          <tag-class>com.java.tag.ChooseTag</tag-class>
          <body-content>scriptless</body-content>
      </tag>
      <tag>
          <name>when</name>
          <tag-class>com.java.tag.WhenTag</tag-class>
          <body-content>scriptless</body-content>
          <attribute>
              <name>test</name>
              <required>true</required>
              <rtexprvalue>true</rtexprvalue>
          </attribute>
      </tag>
      <tag>
          <name>otherWise</name>
          <tag-class>com.java.tag.OtherWiseTag</tag-class>
          <body-content>scriptless</body-content>
      </tag>

    使用标签:

    <test:choose>
            <test:when test="${param.age>24}">大学以上</test:when>
            <test:when test="${param.age>20}">高中以上</test:when>
            <test:otherWise>高中以下</test:otherWise>
        </test:choose>

    EL自定义函数   

    在EL表达式中调用的某个Java类的静态方法,这个静态方法需在web应用程序中进行配置

    才能被EL表达式调用.

    EL自定义函数可以扩展EL表达式的功能,让EL表达式完成普通Java程序所能完成的功能 

    public class MyElFunction {
    
        public static String concat(String str1,String str2){
            return str1+str2;
        }
    }
    <!--描述EL的自定义函数--> 
    <function>
       <name>concat</name> <function-class>com.java.tag.MyElFunction</function-class> <function-signature>java.lang.String concat(java.lang.String,java.lang.String)</function-signature> </function>

    在页面上使用:

    ${atguigu:concat(param.str1,param.str2)}

    为了简化在JSP页面操作字符串,JSTL中提供了一套EL自定义函数,这些自定义函数包含了

    JSP页面经常要用到的字符串操作

    在JSTL的表达式中要使用一个函数,其格式为

    ${fn:methodNames(args...)}

    使用这些函数前必须引入

    <%@ taglib prefix="fn" uri="http//java.sun.com/jsp/jstl/functions"%>

  • 相关阅读:
    今天才知道的JavaScript的真实历史~[转]
    JQuery实现可编辑的表格
    详细记录ASP.NET中的图象处理
    使用javascript比较任意两个日期相差天数(代码)
    你所不知的 CSS ::before 和 ::after 伪元素用法
    javascript模拟post提交
    jQuery/javascript实现IP/Mask自动联想功能
    CSS 中的强制换行和禁止换行
    17.C++-string字符串类(详解)
    16.C++-初探标准库
  • 原文地址:https://www.cnblogs.com/wq3435/p/5174237.html
Copyright © 2020-2023  润新知