15.自定义标签开发案例(我的JavaEE笔记)

主要内容:

  • 防盗链
  • 开发if标签
  • 开发if else标签
  • 开发foreach标签
  • 开发HTML转义的标签
  • 打包标签库

一、防盗链

防盗链的意思是,比如一个网站有一个资源很好,希望来访者先进入首页看完广告才能看到这个资源,如果不加防盗链,则别的网站可能加如此资源的超链接,那么就没有进入首页看广告。我们需要防止除从首页访问资源的其他方式。(工程tag_example
相关代码:
itcast.tld

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" 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 web-jsptaglibrary_2_1.xsd">
    <tlib-version>1.0</tlib-version>
    <short-name>itcast</short-name>
    <uri>/itcast</uri>
    <!-- 防盗链标签 -->
    <tag>
        <name>referer</name>
        <tag-class>cn.itcast.web.tag.RefererTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>site</name><!-- 第一个属性 -->
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <name>page</name><!-- 第二个属性 -->
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>

index.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>My JSP 'index.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    <a href="${pageContext.request.contextPath}/1.jsp">内容链接</a>
  </body>
</html>

1.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<itcast:referer site="http://localhost:8080/" page="/index.jsp"/>
<html>
  <head>
    <title>保护页面</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    This is my JSP page. <br>
  </body>
</html>

RefererTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.SkipPageException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class RefererTag extends SimpleTagSupport {
    private String site ;
    private String page ;
    
    @Override
    public void doTag() throws JspException, IOException {
        //拿到来访者代表page的实例对象
        PageContext pageContext = (PageContext) this.getJspContext();
        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
        String referer = request.getHeader("referer");
        
        //判断
        //如果地址为空或者地址不是我们给定的地址,就跳转到page页面
        if(referer == null || !referer.startsWith(site)){
            HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
            String webroot = request.getContextPath();//得到/day11_example
            //如果page="/day11_example/index.jsp"
            if(page.startsWith(webroot)){
                response.sendRedirect(page);
            }else {
                //如果page="/index.jsp"
                response.sendRedirect(webroot + page);
            }
            //重定向之后我们不能让保护页面显示,要让浏览器先进入index.jsp再进入保护页面
            throw new SkipPageException();
        }

    }

    public String getSite() {
        return site;
    }

    public void setSite(String site) {
        this.site = site;
    }

    public String getPage() {
        return page;
    }

    public void setPage(String page) {
        this.page = page;
    }
}

说明:

  • 1.我们在首页中给出资源页面(保护页面)的链接,在保护页面(1.jsp)中使用自定义标签进行检测:
    <itcast:referer site="http://localhost:8080/" page="/index.jsp"/>。如果资源不是从site指定的地址进入,则跳转到index.jsp页面。当然为了更好的匹配page,我们提供两种方式指定地址,一种是page="/index.jsp",一种是page="/tag_example/index.jsp"
  • 2.需要注意的是当我们处理外来地址访问之后需要抛出SkipPageException异常,不让后面的页面进行显示。

二、开发 if 标签

相关代码:
itcast.tld

<!-- if标签 -->
    <tag>
        <name>if</name>
        <tag-class>cn.itcast.web.tag.IfTag</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>test</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

2.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>if标签</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    <itcast:if test="${user != null }">
        xxxx
    </itcast:if>
  </body>
</html>

IfTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
//开发if标签
public class IfTag extends SimpleTagSupport {
    
    private boolean test;

    @Override
    public void doTag() throws JspException, IOException {
        //只有在满足test条件的时候才执行
        if(test){
            this.getJspBody().invoke(null);
        }
    }

    public boolean isTest() {
        return test;
    }

    public void setTest(boolean test) {
        this.test = test;
    }
}

说明:当满足test为ture时才执行相关页面内容。

三、开发if else 标签

相关代码:
itcast.tld

<!-- if else标签 -->
    <tag>
        <name>choose</name>
        <tag-class>cn.itcast.web.tag.ChooseTag</tag-class>
        <body-content>scriptless</body-content>
    </tag>
    <tag>
        <name>when</name>
        <tag-class>cn.itcast.web.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>cn.itcast.web.tag.OtherwiseTag</tag-class>
        <body-content>scriptless</body-content>
    </tag>

ChooseTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ChooseTag extends SimpleTagSupport {
    private boolean isOk;

    @Override
    public void doTag() throws JspException, IOException {
        this.getJspBody().invoke(null);
    }

    public boolean isOk() {
        return isOk;
    }

    public void setOk(boolean isOk) {
        this.isOk = isOk;
    }
}

WhenTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class WhenTag extends SimpleTagSupport {
    private boolean test;

    @Override
    public void doTag() throws JspException, IOException {
        ChooseTag parent = (ChooseTag) this.getParent();
        if(test == true && parent.isOk() == false){
            this.getJspBody().invoke(null);
            parent.setOk(true);
        }
    }

    public boolean isTest() {
        return test;
    }
    public void setTest(boolean test) {
        this.test = test;
    }
}

OtherwiseTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class OtherwiseTag extends SimpleTagSupport {

    @Override
    public void doTag() throws JspException, IOException {
        ChooseTag parent = (ChooseTag) this.getParent();
        if(parent.isOk() == false){
            this.getJspBody().invoke(null);
            parent.setOk(true);
        }
    }
}

3.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>if else标签</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    <itcast:choose>
        <itcast:when test="${user != null }">aaa</itcast:when>
        <itcast:otherwise>bbb</itcast:otherwise>
    </itcast:choose>
  </body>
</html>

说明:开发if else标签需要三个标签组合起来。同时我们在父标签<itcast:choose>中设置了一个标志位,如果标志位为true表示已经执行了。默认是false。

四、开发迭代标签ForEach

相关代码:
itcast.tld

<!-- foeach标签 -->
    <tag>
        <name>foreach</name>
        <tag-class>cn.itcast.web.tag.ForEachTag</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue><!-- 这里不能是表达式 -->
        </attribute>
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

ForEachTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class ForEachTag extends SimpleTagSupport {

    private List items;
    private String var;

    @Override
    public void doTag() throws JspException, IOException {
        
        PageContext pageContext = (PageContext) this.getJspContext();
        
        Iterator it = items.iterator();
        while(it.hasNext()){
          //因为我们不知道集合中的内容是什么类型,所以一定要用Object类
            Object obj = it.next();
            //后一次的值会覆盖前一次的值,将得到的值存到pageContext域中
            pageContext.setAttribute(var, obj);
            this.getJspBody().invoke(null);
        }
    }
    public void setItems(List items) {
        this.items = items;
    }

    public void setVar(String var) {
        this.var = var;
    }
}

4.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>ForEach标签</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    <%
        List list = new ArrayList();
        list.add("aa");
        list.add("bb");
        request.setAttribute("list", list);
     %>
     <itcast:foreach items="${list}" var="str">
        ${str }
     </itcast:foreach>
     
  </body>
</html>

说明:显然这个标签是不完善的,因为只能处理List集合,我们需要进行改进。
相关代码:(重点)
itcast.tld

<tag>
        <name>foreach2</name>
        <tag-class>cn.itcast.web.tag.ForEachTag2</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <name>var</name>
            <required>true</required>
            <rtexprvalue>false</rtexprvalue><!-- 这里不能是表达式 -->
        </attribute>
        <attribute>
            <name>items</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
</tag>

ForEachTag2.java

package cn.itcast.web.tag;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Array;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class ForEachTag2 extends SimpleTagSupport {

    //不知道要迭代的类型使用Object代替
    private Object items;
    private String var;
    private Collection collection;

    @Override
    public void doTag() throws JspException, IOException {
        PageContext pageContext = (PageContext) this.getJspContext();
        Iterator it = collection.iterator();
        while(it.hasNext()){
            Object obj = it.next();
            pageContext.setAttribute(var, obj);
            this.getJspBody().invoke(null);
        }
    }

    public void setItems(Object items) {
        this.items = items;
        
        //如果传递进来的是一个集合
        if(items instanceof Collection){
            collection = (Collection) items;
        }
        
        //如果传递进来的是一个对象数组
        /*if(items instanceof Object[]){
            Object arg[] = (Object[]) items;
            collection = Arrays.asList(arg);
        }*/
        
        //如果传递进来的是一个Map
        if(items instanceof Map){
            Map map = (Map) items;
            collection = map.entrySet();
        }
        
        //如果传递进来的是一个基本数据类型数组
        /*if(items instanceof int[]){
            int temp[] = (int[]) items;
            collection = new ArrayList();
            for(int num : temp){
                collection.add(num);
            }
        }*///对于基本数据类型这样写八次很麻烦,sun公司的源码也是这样判断的。。。
        
        //对于数组类型(对象数组和基本数据数组)我们使用此种方式改进
        if(items.getClass().isArray()){
            collection = new ArrayList();
            int len = Array.getLength(items);
            for(int i = 0; i < len; i++){
                Object obj = Array.get(items, i);
                collection.add(obj);
            }
        }
    }

    public void setVar(String var) {
        this.var = var;
    }

    public void setCollection(Collection collection) {
        this.collection = collection;
    }
}

5.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@taglib uri="/itcast" prefix="itcast" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>ForEach标签2</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
  </head>
  
  <body>
    <%
        List list = new ArrayList();
        list.add("aa");
        list.add("bb");
        request.setAttribute("list", list);
        
        Integer arr[] = new Integer[]{1,2,3};
        request.setAttribute("arr", arr);
        
        Map map = new HashMap();
        map.put("a", "aaa");
        map.put("b", "bbb");
        map.put("c", "ccc");
        request.setAttribute("map", map);
     %>
     <itcast:foreach items="str" var="${list }">
        ${str }
     </itcast:foreach><br>
     
     <itcast:foreach2 items="num" var="${var }">
        ${num }
     </itcast:foreach2><br>
     
     <!-- 注意基本数据迭代 -->
     <%
        int arr1[] = new int[]{1,2,3};
        request.setAttribute("arr1", arr1);
        
        byte[] arr2 = new byte[]{3,2,1};
        request.setAttribute("arr2", arr2);
      %>
      <itcast:foreach2 items="num" var="${arr1 }">
        ${num }
      </itcast:foreach2>
       <itcast:foreach2 items="num" var="${arr2 }">
        ${num }
      </itcast:foreach2>
  </body>
</html>

说明:

  • 1.首先我们分析一些可能会遇到的数据类型。数据类型有集合、Map、数组,数组中又分对象数组和基本数据类型数组。我们可以在execute方法中依次进行判断,但是会有很多重复代码,于是我们统一在setItems方法中进行判断。首先对于集合、Map、对象数组我们可以统统将其转换为Collection对象。单数对于基本数据类型数组的处理其实sun公司是进行了八次判断,显然较为麻烦。这里其实我们可以将数组统一处理,从代码中可以看到。
  • 2.判断过程。如果本来就是一个Collection对象,则直接转换。如果是对象数组,则先转换成Object对象,然后再转换成Collection对象。如果是Map对象,其实本身这个对象在迭代的时候也是通过Set集合进行迭代的,所以这里我们可以将其先转换成Collection对象。然后是八种基本数据类型的转换,但是之后我们对数组的处理进行了修改,我们这里统一处理。

五、开发HTML转义标签

相关代码:
itcast.tld

<!--html转义标签 -->
    <tag>
        <name>htmlFilter</name>
        <tag-class>cn.itcast.web.tag.HtmlFilterTag</tag-class>
        <body-content>scriptless</body-content>
    </tag>

HtmlFilterTag.java

package cn.itcast.web.tag;
import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;
//这个方法在源码中可以找到
public class HtmlFilterTag extends SimpleTagSupport {

    @Override
    public void doTag() throws JspException, IOException {
        StringWriter sw = new StringWriter();
        this.getJspBody().invoke(sw);
        
        //得到标签体
        String content = sw.getBuffer().toString();
        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);
        StringBuilder result = new StringBuilder(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                result.append("<");
                break;
            case '>':
                result.append(">");
                break;
            case '&':
                result.append("&");
                break;
            case '"':
                result.append(""");
                break;
            default:
                result.append(content[i]);
            }
        }
        return (result.toString());

    }
}

6.jsp

<body>
    <itcast:htmlFilter>
        <a href="#">链接</a>
    </itcast:htmlFilter>
  </body>

说明:这个标签中的方法filter其实我们可以在源码中找到,地址是apache-tomcat-8.0.26-src\webapps\examples\WEB-INF\classes\util\HTMLFilter.java。主要是为了在页面中显示HTML代码,不能让页面解析使用的。

六、打包标签库

我们在自定义了一些自己需要用到的标签之后,可能在别的地方也需要使用,这时我们最好像sun公司一样将其打包成一个jar文件。

先将cn.itcast.web.tag中所有的自定义标签类全部拷贝到一个普通java工程中的src目录下面,这时会发现有错误,这时因为我们没有导入相应的包,这里我们需要将tomcat中的E:\software\tomcat\lib中的jsp-api.jarservlet-api.jar两个包拷贝到新建的lib包中,然后导入即可。最后还需要将配置文件itcast.tld拷贝到新建目录META-INF目录中。这里我们可以看到需要建立一个lib包保存相关的jar包,而META-INF中一般保存相关的配置文件。然后选中工程右键->Export->Java->JAR file->Next->选中要打包的工程,同时将右边框中MyEclipse的配置文件取消选中,然后在JAR file选择要保存的路径和文件名,文件名后缀必须为.jar,然后点击finish即可。

最后:

这些标签其实sun公司已经帮我写好了,我们只需要拿过来用即可。这里只是让我们更好的明白其是怎样实现的,以后碰到特殊的标签我们可以自己开发。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容

  • 一、 自定义标签简介 自定义标签主要用于移除jsp页面中的java代码。工程(tag) 使用自定义标签移除jsp页...
    yjaal阅读 786评论 1 4
  • 一、EL表达式简介 1.EL全名为Expression Language。主要作用: 获取数据:EL表达式主要用于...
    yjaal阅读 3,781评论 2 28
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,562评论 18 399
  • EL表达式 EL作用 jsp的核心语法: jsp表达式 <%=%>和 jsp脚本<% %> 以后开发jsp的原则...
    奋斗的老王阅读 2,428评论 1 51
  • 1什么是JSTLJSTL是apache对EL表达式的扩展(也就是说JSTL依赖EL),JSTL是标签语言!JSTL...
    TY_阅读 370评论 0 1