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

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

forEach标签


forEach标签最基本的功能:遍历集合、数组

  • 首先,我先写一个可以遍历List集合的标签,可能我们会这样设计
public class Demo2 extends SimpleTagSupport {
    //遍历的是List集合,于是标签的属性就为List
    private List items;
    //遍历出来的对象就用Object存着,因为我们不知道List集合保存的是什么元素
    private Object var;
    @Override
    public void doTag() throws JspException, IOException {
        //获取到迭代器
        Iterator iterator = items.iterator();
        //遍历集合
        while (iterator.hasNext()) {
            //获取到集合的元素
            var = iterator.next();
            //.....var属性代表的就是集合的元素,现在问题来了,好像在标签体内无法获取到这个对象....
            //做到这里完成不下去了....
        }
    }
    public void setItems(List items) {
        this.items = items;
    }
    public void setVar(Object var) {
        this.var = var;
    }
}

上面的思路是正常的,但是做不下去!我们换一个思路呗。上面的问题主要是在标签体获取不到被遍历出来的对象

我们这样做:把var定义成String类型的,如果遍历得到对象了,就设置PageContext的属性,var为关键字,对象为值。在标签体用EL表达式搜索以var为关键字的对象!每遍历出一个对象,就执行一次标签体!

  • 于是就有了以下的代码!
public class Demo1 extends SimpleTagSupport {
    //遍历的是List集合,定义List集合成员变量
    private List items;
    //以var为关键字存储到PageContext
    private String var;
    @Override
    public void doTag() throws JspException, IOException {
        //获取到集合的迭代器
        Iterator iterator = items.iterator();
        //获取到代表标签体内容的对象
        JspFragment jspFragment = this.getJspBody();
        //遍历集合
        while (iterator.hasNext()) {
            Object o = iterator.next();
            //把遍历出来的对象存储到page范围中,关键字为标签的属性var(在标签体中使用EL表达式${var},就能够获取到集合的对象了!)
            this.getJspContext().setAttribute(var, o);
            //每设置了一个属性,我就执行标签体
            jspFragment.invoke(null);
        }
    }
    public void setItems(List items) {
        this.items = items;
    }
    public void setVar(String var) {
        this.var = var;
    }
}
  • tld文件如下
<tag>
    <name>forEach</name>
    <tag-class>tag.Demo1</tag-class>
    <body-content>scriptless</body-content>
    <attribute>
        <name>var</name>
        <rtexprvalue>true</rtexprvalue>
        <!--字符串即可,不需要EL表达式的支持-->
        <required>false</required>
    </attribute>
    <attribute>
        <name>items</name>
        <required>true</required>
        <rtexprvalue>true</rtexprvalue>
    </attribute>
</tag>
  • 测试的jsp代码如下
<%
    List list = new ArrayList();
    list.add("zhongfucneng");
    list.add("1");
    list.add("2");
    list.add("3");
    request.setAttribute("list",list);
%>
<zhongfucheng:forEach items="${list}" var="str">
    ${str}
</zhongfucheng:forEach>
  • 效果:

12.png

上面写的仅仅能够遍历List集合,做一个通用的forEach标签麻烦的是在:不知道传进来的是什么类型的数组、什么类型集合!,需要逐一去判断

  • 我们的实现思路就是将所有的集合或数组都转成是Collection类型的!
  • 我们来尝试着写一下
//如果items是Collection类型的,就强转为Colletion
if (items instanceof Collection) {
    collection = (Collection) items;
}
//如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的!
if (items instanceof Map) {
    Map map = (Map) items;
    collection = (Collection) map.entrySet();
}
//对象数组
if (items instanceof Object[]) {
    Object[] objects = (Object[]) items;
    collection = Arrays.asList(objects);
}
//int[],Byte[],char[]等八大基本数据类型.....

还有int[],byte[],char[]等八大基本数据类型,这八大基本数据类型就不能用Arrays.asList()把引用传进去了。因为JDK5以后会把引用自动装箱成Interger[]、Byte[]等等,而不是获取到数组的元素数据。

  • 测试代码如下:
public static void main(String[] args) {
    int[] ints = new int[]{1, 2, 3};
    Object[] objects = new Object[]{"1", "2", "3"};
    if (objects instanceof Object[]) {
        Collection collection = Arrays.asList(objects);
        System.out.println(collection);
    }
    if (ints instanceof int[]) {
        Collection collection1 = Arrays.asList(ints);
        System.out.println(collection1);
    }
}
  • 效果:

13.png

  • 对于八大基本类型数据我们就可以这样干
if (items instanceof int[]) {
    int[] ints = (int[]) items;
    collection = new ArrayList();
    for (int anInt : ints) {
        collection.add(anInt);
    }
}
//......这里还要写7个
  • JSTL的forEach标签类似就是这样干的

14.jpg

由于JDK5的新特性,我们又有另外的解决方案,Class对象能够判断是否为数组类,reflect反射包下Array类

  • 其实,无论Map集合、还是任何类型的数组、都可以使用Colletion进行遍历!。
  • 如果是八大基本数据类型的数组,我们就使用反射来进行构建出Collection集合。
  • 标签处理器的代码
public class Demo1 extends SimpleTagSupport {
    //遍历的是未知的集合或数组,定义成Object
    private Object items;
    //每次被遍历的对象存储关键字
    private String var;
    //Colletion
    private Collection collection;
    //在WEB容器设置标签的属性的时候,判断是什么类型的数组和集合
    public void setItems(Object items) {
        this.items = items;
        //如果items是Collection类型的,就强转为Colletion
        if (items instanceof Collection) {
            collection = (Collection) items;
        }
        //如果itmes是Map类型的,那么就强转为Map,再获取到<Map.Entry<K, V>,这个是Set集合的!
        if (items instanceof Map) {
            Map map = (Map) items;
            collection = (Collection) map.entrySet();
        }
        //可以这样解决,Class对象判断是否是一个数组类
        if (items.getClass().isArray()) {
            //创建Collection集合添加数组的元素!
            collection = new ArrayList();
            //再利用reflect包下的Array类获取到该数组类的长度
            int len = Array.getLength(items);
            //遍历并添加到集合中
            for (int i = 0; i < len; i++) {
                collection.add(Array.get(items, i));
            }
        }
    }
    public void setVar(String var) {
        this.var = var;
    }
   @Override
    public void doTag() throws JspException, IOException {
        //获取到代表标签体内容的对象
        JspFragment jspFragment = this.getJspBody();
        Iterator iterator = collection.iterator();
        //遍历集合
        while (iterator.hasNext()) {
            Object o = iterator.next();
            //把遍历出来的对象存储到page范围中(在标签体中使用EL表达式${var},就能够获取到集合的对象了!)
            this.getJspContext().setAttribute(var, o);
            jspFragment.invoke(null);
        }
    }
}
  • tld文件和上面是一样的,下面是测试代码
<%
    /*list集合*/
    List list = new ArrayList();
    list.add("zhongfucneng");
    list.add("1");
    list.add("2");
    list.add("3");
    request.setAttribute("list",list);
    /*基本数据类型数组*/
    int[] ints = new int[]{1, 2, 3, 4, 5};
    request.setAttribute("ints", ints);
    /*对象数组*/
    Object[] objects = new Object[]{2, 3, 4, 5, 6};
    request.setAttribute("objects", objects);
    /*map集合*/
    Map map = new HashMap();
    map.put("aa", "aa");
    map.put("bb", "bb");
    map.put("cc", "cc");
    request.setAttribute("map",map);
%>
    List集合:
    <zhongfucheng:forEach items="${list}" var="str">
        ${str}
    </zhongfucheng:forEach>
    <hr>
    <hr>
    基本数据类型数组:
    <zhongfucheng:forEach items="${ints}" var="i">
        ${i}
    </zhongfucheng:forEach>
    <hr>
    <hr>
    对象数组:
    <zhongfucheng:forEach items="${objects}" var="o">
        ${o}
    </zhongfucheng:forEach>
    <hr>
    <hr>
    map集合:
    <zhongfucheng:forEach items="${map}" var="me">
        ${me.key} = ${me.value}
    </zhongfucheng:forEach>
  • 效果:

15.png



HTML转义标签


要开发这个标签就很简单了,只要获取到标签体的内容,再通过经过方法转义下标签体内容,输出给浏览器即可

  • 标签处理器代码:
public class Demo1 extends SimpleTagSupport {
   @Override
    public void doTag() throws JspException, IOException {
       //获取到标签体的内容再修改
       StringWriter stringWriter = new StringWriter();
       JspFragment jspFragment = this.getJspBody();
       jspFragment.invoke(stringWriter);
       String content = stringWriter.toString();
       //经过filter()转义,该方法在Tomcat可以找到
       content = filter(content);
       //再把转义后的内容输出给浏览器
       this.getJspContext().getOut().write(content);
    }
    private String filter(String message) {
        if (message == null)
            return (null);
        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuffer result = new StringBuffer(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
                case '<':
                    result.append("&lt;");
                    break;
                case '>':
                    result.append("&gt;");
                    break;
                case '&':
                    result.append("&amp;");
                    break;
                case '"':
                    result.append("&quot;");
                    break;
                default:
                    result.append(content[i]);
            }
        }
        return (result.toString());
    }
}
  • 测试代码
<zhongfucheng:filter><a href="2.jsp">你好啊</a> </zhongfucheng:filter>
<br>
<a href="2.jsp">你好啊
  • 效果:

16.png



if else标签


在JSTL中并没有if else的标签,JSTL给予我们的是choose,when,otherwise标签,现在我们模仿choose,when,otherwise开发标签

思路:when标签有个test属性,但otherwise怎么判断标签体是执行还是不执行呢?这时就需要choose标签的支持了!choose标签默认定义一个Boolean值为false,。当when标签体被执行了,就把Boolean值变成true,只要Boolean值为false就执行otherwise标签体的内容。

看程序就容易理解上面那句话了:

  • choose标签处理器
public class Choose extends SimpleTagSupport {
    private boolean flag;
    @Override
    public void doTag() throws JspException, IOException {
        this.getJspBody().invoke(null);
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
  • When标签处理器
public class When extends SimpleTagSupport {
    private boolean test ;
    @Override
    public void doTag() throws JspException, IOException {
        Choose choose = (Choose) this.getParent();
        //如果test为true和flag为false,那么执行该标签体
        if (test == true && choose.isFlag() == false) {
            this.getJspBody().invoke(null);
            //修改父标签的flag
            choose.setFlag(true);
        }
    }
    public void setTest(boolean test) {
        this.test = test;
    }
}
  • OtherWise标签处理器
public class OtherWise extends SimpleTagSupport {
    @Override
    public void doTag() throws JspException, IOException {
        Choose choose = (Choose) this.getParent();
        //如果父标签的flag为false,就执行标签体(如果when标签没执行,flag值就不会被修改!when标签没执行,就应该执行otherwise标签!)
        if (choose.isFlag() == false) {
            getJspBody().invoke(null);
            //改父标签的flag为false
            choose.setFlag(true);
        }
    }
}
  • 测试代码:
<zhongfucheng:choose>
    <zhongfucheng:when test="${user!=null}">
        user为空
    </zhongfucheng:when>
    <zhongfucheng:otherwise>
        user不为空
    </zhongfucheng:otherwise>
</zhongfucheng:choose>
  • 效果:

17.png



DynamicAttribute接口


此接口的主要功能是用于完成动态属性的设置!前面我们讲解属性标签的时候,属性都是写多少个,用多少个的。现在如果我希望属性可以动态的增加,只需要在标签处理器类中实现DynamicAttribute接口即可!

现在我要开发一个动态加法的标签

  • 标签处理器
public class Demo1 extends SimpleTagSupport implements DynamicAttributes {
    //既然有动态属性和动态的值,那么我们就用一个Map集合存储(1-1对应的关系),做的加法运算,值为Double类型的。
    Map<String, Double> map = new HashMap<>();
    @Override
    public void doTag() throws JspException, IOException {
        //定义一个sum变量用于计算总值
        double sum = 0.0;
        //获取到Map集合的数据
        Iterator iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Double> entry = (Map.Entry<String, Double>) iterator.next();
            sum += entry.getValue();
        }
        //向浏览器输出总和是多少
        this.getJspContext().getOut().write(String.valueOf(sum));
    }
    //对于这个要实现的方法,我们只要关注第2个参数和第3个参数即可
    //第二个参数表示的是动态属性的名称,第三个参数表示的是动态属性的值
    @Override
    public void setDynamicAttribute(String s, String localName, Object value) throws JspException {
        //将动态属性的名字和值加到Map集合中
        map.put(localName, Double.valueOf(Float.valueOf(value.toString())));
    }
}
  • tld文件,注意要把dynamic-attribute设置为true
<tag>
    <name>dynamicAttribute</name>
    <tag-class> tag.Demo1</tag-class>
    <body-content> empty</body-content>
    <!--这个必须要设置为true-->
    <dynamic-attributes>true</dynamic-attributes>
</tag>
  • 测试代码
<zhongfucheng:dynamicAttribute num="1.1" num2="2.2" num3="1"/>
  • 效果,double在运算的时候会丢失精度的,现在只是测验下动态属性,这里就不详细说了!

18.png


开发自定义函数


至于怎么开发自定义函数,在EL表达式的博客中有

目录
相关文章
|
Java
JSP自定义标签【下】
JSP自定义标签【下】
66 0
|
Java
jsp自定义标签
jsp自定义标签
77 1
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 3
JSP自定义标签允许开发人员创建可重用的组件,简化页面逻辑。在JSP 2.0及以上版本,可通过继承`SimpleTagSupport`并覆写`doTag()`方法来创建自定义标签,如`HelloTag`,它有一个`message`属性。属性值通过setter方法`setMessage()`设置。在TLD文件中定义该属性后,可在JSP页面使用`&lt;ex:Hello message=&quot;This is custom tag&quot;/&gt;`来调用,输出定制的文本。
26 0
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 4
**JSP自定义标签允许创建用户定义的语言元素。它们转换为Servlet中的tag handler,在执行时由Web容器调用。使用SimpleTagSupport继承并重写doTag()方法可创建简单标签。标签可设置属性,如message,通过setter方法访问。TLD文件定义属性元数据,JSP页面则通过prefix和uri引用。例如,&lt;ex:Hello message=&quot;...&quot;/&gt; 显示定制消息。属性可配置为必需、类型、描述及是否为JspFragment。**
29 0
|
5月前
|
搜索推荐 Java 容器
JSP 教程 之 JSP 自定义标签 2
**JSP自定义标签允许用户创建个性化标签,简化页面逻辑。在JSP 2.0中,通过继承`SimpleTagSupport`并重写`doTag()`可创建简单标签处理器。示例展示了一个名为`Hello`的自定义标签,它接收并显示标签体内容。TLD文件配置了标签元数据,JSP页面引用该标签并展示“这是消息主体”。**
24 0
|
5月前
|
Java 容器
JSP 教程 之 JSP 自定义标签 1
**JSP自定义标签简介**:扩展JSP功能,创建用户定义标签,通过Servlet容器调用Tag Handler。在JSP 2.0中,使用SimpleTagHandlers简化定制。以&quot;Hello&quot;标签为例,创建`HelloTag`类继承`SimpleTagSupport`,重写`doTag()`打印消息。编译后,在`custom.tld`定义标签库,JSP页面引用后即可使用。例如 `&lt;ex:Hello/&gt;` 显示 &quot;Hello Custom Tag!&quot;。
28 0
|
前端开发 Java
通用分页进阶之jsp之自定义标签
通用分页进阶之jsp之自定义标签
35 1
|
6月前
|
Java 数据安全/隐私保护
Jsp自定义标签(foreach,dept,select)
Jsp自定义标签(foreach,dept,select)
48 0
|
6月前
|
XML Java 数据格式
JSP自定义标签
JSP自定义标签
70 0
|
存储 Java 开发者
JSP自定义标签(下)
JSP自定义标签(下)
57 0