JavaWeb之EL表达式

 

时间:2016-11-25 23:06

 

 

——JavaBean规范

    JavaBean出现的目的是在一些可视化工具中完成一些组件的显示。

一、规范
    1、必须为成员提供set/get方法(两者只提供一个也可以)
        如果只有get方法,那么这个属性是只读属性。
    2、必须有默认构造器(无参构造器)
    3、一般对具有set/get方法的成员变量称之为属性。
    4、就算一个属性没有对应的成员变量,只有set/get方法也是可以的,例如在get方法中直接retunr "aaa"。
    5、属性的名称就是set/get方法去掉set/get后,再把首字母小写。
    6、属性名称由set/get方法来决定,而不是成员名称,与声明变量名称无关
    7、boolean类型的属性,它的get方法可以是is开头,也可以是get开头。

二、JavaBean属性
    JavaBean属性分为四种:
    1、单值属性
        name、age
    2、索引属性
        数组
    3、关联属性
        类似于触发器(仅限于消息传递,无权操作其他属性)
    4、限制属性
        类似于触发器(可以操作属性)

三、内省
    内省就是通过反射来操作JavaBean,但是内省比反射要方便。
    首先需要提供JavaBean类。

    内省类 —— Bean信息 —— 属性描述符 —— 属性的get、set方法对应的Method —— 进行反射操作。

    BeanInfo  Introspector.getBeanInfo(*.class)
        得到Bean的信息,然后通过BeanInfo可以得到所有属性描述符对象。
        属性描述符:可以通过PropertyDiscriptor得到一个属性的读、写方法。

    PropertyDiscriptor[ ]  getPropertyDiscriptor()
        获得属性描述符。

    getReadMethod()、getWriteMethod()
        获取读写方法。

    可以通过读、写方法来操作JavaBean的属性。

四、Commons-BeanUtils.jar
    它依赖内省完成操作。
    导包:
        commons-beanutils.jar
        commons-logging.jar

public class Demo {
    @Test
    public void function() throws Exception
    {
        String className = "com.wyc.domain.Person";
        Class c = Class.forName(className);
        Object bean = c.newInstance();
        //为name属性设置zhangsan
        BeanUtils.setProperty(bean, "name", "zhangsan");
        //为age属性设置20,会自动进行类型转换
        BeanUtils.setProperty(bean, "age", "20");
        //为gender属性设置男
        BeanUtils.setProperty(bean, "gender", "男");
        //输出bean对象
        System.out.println(bean);
 
        //获取属性
        String age = BeanUtils.getProperty(bean, "name");
        System.out.println(age);
    }
    /**
     * 将Map中的属性直接封装到Bean中
     * Map:{"username":"zhangsan","password":"123"}
     * 键必须和属性名相同,否则无法封装
     * @throws Exception 
     * @throws IllegalAccessException 
     */
    @Test
    public void function2() throws IllegalAccessException, Exception
    {
        Map<String, String> map = new HashMap<String, String>();
        map.put("username", "zhangsan");
        map.put("password", "123");
 
        User user = new User();
        //将Map中的数据封装到User对象中,前提是Map中的键必须和User的属性名相同
        //后期可以通过该方法直接封装表单。
        BeanUtils.populate(user, map);
        System.out.println(user);
    }
}
 
五、JSP中与JavaBean相关的标签
    1、<jsp:useBean>
        该标签的作用是创建JavaBean对象:在当前JSP页面创建JavaBean对象,然后把创建的JavaBean对象保存到域对象中。
        查询或创建Bean:如果有,则执行查询,如果没有,则创建新的对象,相当于new。

        在当前JSP页面中创建User类型的对象,并且把它保存到page域中:
                <jsp:useBean id="user1" class="com.wyc.domain.User" />
            如果翻译成Java代码:
                <%
                    com.wyc.domain.User user1 = new com.wyc.domain.User();
                    pageContext.setAttribute("user1", user1);
                %>
        <jsp:useBean>标签默认把JavaBean对象保存到page域中,可以通过scope属性来指定保存的范围:
            <jsp:useBean id="user1" class="com.wyc.domain.User" scope="page" />
            <jsp:useBean id="user1" class="com.wyc.domain.User" scope="request" />
            <jsp:useBean id="user1" class="com.wyc.domain.User" scope="session" />
            <jsp:useBean id="user1" class="com.wyc.domain.User" scope="application" />

        <jsp:useBean>标签不一定会创建对象,它会先在指定范围中查找这个对象,如果对象不存在才会执行创建操作。
            <jsp:useBean id="user1" class="com.wyc.domain.User" scope="application" />
        相当于Java:
            <%
                com.wyc.domain.User user1 = (com.wyc.domain.User)application.getAttribute("user1");
                if(user1 == null){
                    user1 = new com.wyc.domain.User("user1", user1);
                }
            %>
    2、<jsp:setProperty>
        该标签的作用是给JavaBean设置属性值。

    3、<jsp:getProperty>
        该标签的作用是获取JavaBean的属性值。

        <jsp:useBean id="user" class="com.wyc.domain.User" />
        //将名为user的JavaBean的username属性设置为“admin”
        <jsp:setProperty name="user" property="username" value="admin" />
        //将名为user的JavaBean的password属性设置为“123456”
        <jsp:setProperty name="user" property="password" value="123456" />


        用户名:<jsp:getProperty name="user" property="username" />
        密   码:<jsp:getProperty name="user" property="password" />


——EL表达式(Expression Language)


1、EL概述
    EL表达式是JSP内置的表达式语言。
    EL起源于JSTL1.0,与JSTL联合使用。
    JSP2.0开始,不再使用Java脚本,而是使用EL表达式和动态标签来替代Java脚本。
    EL表达式替代的是<%= %>,也就是说,EL表达式只能用作输出。
    JSP2.0要把HTML和CSS分离、要把HTML和JavaScript分离、要把Java脚本替换成标签,标签的好处是非Java人员也可以使用。
    JSP2.0是一个纯标签页面,即不包含<% %>  <%!  %>  <%= %>
    JSTL:JSP Standard Tag Library
    目前,减少JSP页面中的Java代码,代替了表达式:<%= %>

2、EL语法
    格式:
        ${表达式}
        例如:
            ${10+10}            等价于<%=10+10 %>
            ${user.name}     等价于<%=user.getName() %>

2、EL表达式来读取四大域
    1)${xxx},全域查找名为xxx的属性,如果不存在,输出空字符串,而不是null。
        相当于pageContext.findAttribute("xxx");
    2)获取指定域属性:
        ${pageScope.xxx }、${requestScope.xxx }、${sessionScope.xxx }、${applicationScope.xxx }

    注意:
        1)EL表达式可以直接获取到四大作用域中的值。
        2)直接写表达式中的值时,如果多个内置对象都包含该值,就直接从作用域小的范围开始找对应键名的值。
            ${xxx },全域查找名为xxx的属性,如果不存在,输出空字符串,而不是null。
        3)如果想获取指定作用域里面的值,可以使用EL内置的作用域对象:
            requestScope
            sessionScope
            applicationScope
            pageScope(是pageContext的对应对象,一般不使用,因为pageContext的作用域本来就是最小)
        4)只有当setAttribute之后才可以在其他页面中使用“name”值来获取value,如果没有给作用域设置setAttribute,并且想直接把参数转发给JSP文件,那么JSP中可以使用${param.xxx}来获取值。xxx就是name,param.xxx返回的值就是value。


3、JavaBean导航
    一个满足JavaBean规范的Java类,可以通过如下语句进行操作:
        获取User类的user对象的username属性:
            ${requestScope.user.username }
    因为满足了JavaBean规范,所以可以直接使用属性名称来获取值,相当于:
        request.getAttribute("user").getUsername();

4、EL内置对象
    EL一共11个内置对象,无需创建即可使用,这11个内置对象中有10个是Map类型的,最后一个是pageContext对象。
    *   pageScope
    *   requestScope
    *   sessionScope
    *   applicationScope

    *   param 
            对应参数,它是一个Map,其中key是参数名,value是参数,适用于单值参数。
            ${param.username }

    *   paramValues
            对应参数,它是一个Map,其中key是参数名,value是多个参数,适用于多值参数。
            ${paramValues.hobby[0] }

            ${paramValues.hobby[1] }
            因为得到的是一个数组,所以需要使用下标来获取值。

    *   header
            对应请求头,它是一个Map,其中key表示头名称,value表示单个请求头值,适用于单值请求头。
            ${header.host }
            如果请求头中包含“-”,例如User-Agent,需要使用引号:
            ${header['User-Agent'] }

    *   headerValues
                        对应请求头,它是一个Map,其中key表示头名称,value表示多个请求头值,适用于多值请求头。

    *   initParam
            获取<context-param>中的初始化参数。

    *   cookie
            cookie是Map<String, Cookie>类型,其中key是Cookie的名称,而值是Cookie对象。
            ${cookie.JSESSIONID.value },相当于调用cookie.getValue()方法。

    *   pageContext
            pageContext是PageContext类型,可以使用pageContext对象调用getXXX()方法,例如:pageContext.getRequest(),可以写成${pageContext.request },也就是获取JavaBean属性。
                EL表达式                                                                        说明
            ${pageContext.request.queryString }                            pageContext.getRequest().getQueryString();
            ${pageContext.request.requestURL}                            pageContext.getRequest().getRequestURL();
            ${pageContext.request.contextPath }                           pageContext.getRequest().getContextPath();
            ${pageContext.request.method }                                  pageContext.getRequest().getMethod();
            ${pageContext.request.protocol }                                  pageContext.getRequest().getProtocol();
            ${pageContext.request.remoteUser }                            pageContext.getRequest().getRemoteUser();
            ${pageContext.request.remoteAddr }                            pageContext.getRequest().getRemoteAddr();
            ${pageContext.session.new }                                        pageContext.getSession().isNew();
            ${pageContext.request.session.id }                               pageContext.getSession().getId();
            ${pageContext.request.servletContext.serverInfo }       pageContext.getServletContext().getServerInfo();

5、EL应用
    可以直接在JSP页面中使用。
    可以作为元素属性的值。
    可以做超链接请求时的附加参数。
    可以在自定义或者标准动作元素的内容中使用。
    不能在脚本元素中使用 -- 因为EL表达式本来就是取代脚本(Java代码)的表达式。


6、EL表达式的分类
    普通表达式:${100}
    单值属性读取:${属性名}
    对象属性读取:
        假设user是对象,设置属性pageContext.setAttribute("user",user);
        读取user对象的属性name:
            ${user.name}
            ${user['name']}
            ${user["name"]}
        user.name等价于user.getName();,返回的是user的name属性值。

7、EL应用案例:登录系统
    1)View层设计
    2)Model层设计
    3)Controller层设计
    4)完善View层(数据显示)


8、EL运算符


——EL函数库

1、什么是EL函数库
    EL函数库是由第三方对EL的扩展。
    EL函数库就是定义一些有返回值的静态方法,然后通过EL语言来调用它们,我们也可以自定义EL函数库。
    EL函数库中包含了很多对字符串的操作方法,以及对集合对象的操作,例如:${fn:length("abc") },会输出字符串的长度。

2、导入函数库
    因为是第三方的东西,所以需要导入,需要使用tablib指令。
    <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>

3、EL函数库介绍
    String toUpperCase(String input)
    String toLowerCase(String input)
    int indexOf(String input, String substring)
    boolean contains(String input, String substring)
    boolean containsIgnoreCase(String input, String substring)
    boolean startsWith(String input, String substring)
    boolean endsWith(String input, String substring)
    String substring(String input, int beginIndex, int endIndex)    //下标-1表示倒数第一个
    String substringAfter(String input, String substring)
    String substringBefore(String input, String substring)
    String escapeXml(String input)    //将标签转义成普通字符串输出:<  >  &  '  "
    String trim(String input)
    String replace(String input, String substringBefore, String subsringAfter)
    String[] split(String input, String delimiters)
    int length(Object obj)    //可以获取字符串、数字、各种集合的长度
    String join(String array[], String separator)    //使用连接符合并字符串 separator:连接符

4、自定义EL函数库
    1)写一个类,类中可以定义0 - N个方法,但必须是static,并且必须有返回值。
public class MyFunction {
    public static String fun(){
        return "这是一个字符串";
    }
}
    2)编写tld文件(放到WEB-INF文件夹下)
<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
 
    <description>wyc</description>
    <display-name>wyc-function</display-name>
    <tlib-version>1.0</tlib-version>
    <short-name>w</short-name>

    //每写一个函数,就要写一个function标签
    <function>
        <name>fun</name>    //调用的方法名
        <function-class>com.wyc.fn.MyFunction</function-class>    //完整类名
        <function-signature>java.lang.String fun()</function-signature>    //完整方法
    </function>
</taglib>

    3)在JSP页面中导入标签库
        <%@taglib prefix="w" uri="/EL/wyc.tld"%>    //可以是本地路径

    4)在JSP页面中使用自定义函数
        ${w: fun() }

<%
    String[] strs = {"a", "b", "c"};
    List list = new ArrayList();
    list.add("a");
    pageContext.setAttribute("arr", strs);
    pageContext.setAttribute("list", list);
%>
    数组的长度:${fn: length(arr) }<br />
    集合的长度:${fn: length(list) }<br />
    转换成小写:${fn: toLowerCase("Hello") }<br />
    转换成大写:${fn: toUpperCase("Hello") }<br />
    abc是否包含a:${fn: contains("abc", "a") }<br />
    abc是否包含a,忽略大小写:${fn: containsIgnoreCase("abc", "Ab") }<br />
    数组是否包含a:${fn: contains(arr, "a") }<br />
    集合是否包含A,忽略大小写:${fn: containsIgnoreCase(list, "A") }<br />
    Hello.java是否以.java结尾:${fn: endsWith("Hello.java",".java") }<br />
    Hello.java是否以Hello开头:${fn: startsWith("Hello.java", "Hell") }<br />
    返回Hello-World中-的下标:${fn: indexOf("Hello-World","-") }<br />
    合并arr数组和;字符串:${fn: join(arr, ";") }<br />
    将Hello-World中的-替换为+:${fn: replace("Hello-World","-", "+") }<br />
    将字符数组a;b;c;拆分后再使用-连接:${fn: join(fn: split("a;b;v;", ";"), "-") }<br />
 






——JSTL(JSP Standard Tag Library)

1、概述
    全称:JSP Standard Tag Library
    JSTL是Apache对EL表达式的扩展(也就是说JSTL依赖EL),JSTL是标签语言,JSTL标签使用起来非常方便,它与JSP动作标签一样,只不过它不是JSP内置的标签,需要我们自己导包、以及指定标签库而已。
    如果使用MyEclipse开发JavaWeb,那么在把项目发布到Tomcat时,MyEclipse会在lib目录下存放JSTL的jar包,如果没有使用MyEclipse开发,那么需要自己导入JSTL的jar包:jstl-1.2.jar。

    JavaEE5的标准技术
    应用在视图层,目的是为了尽可能减少JSP中的Java代码。
    通常情况下,JSTL联合EL表达式一起使用。

2、JSTL标签库
    JSTL一共包含四大标签库:
        *   core:核心标签库,是学习的重点。
        *   fmt:格式化标签库,只需要学习两个标签即可,日期、数字。
        *   sql:数据库标签库,不需要学习了,它已经过时了。
        *   xml:XML标签库,不需要学习了,它已经过时了。

3、使用taglib指令导入标签库
    除了JSP动作标签外,使用其他第三方的标签库都需要:
        *   导包。
        *   在使用标签的JSP页面中使用taglib指令导入标签库。
    下面水导入JSTL的core标签库:
        <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

        prefix="c":指定标签库的前缀,这个前缀可以随便给值,但大家都会在使用core标签库时指定前缀为c。
        uri="http://java.sun.com/jsp/jstl/core":指定标签库的uri,它不一定是真实存在的网址,但它可以让JSP找到标签库的描述文件,是对应jar包中的uri。

4、core标签库常用标签(c标签)

    1)out:(输出)
        *   value:可以使字符串常量,也可以是EL表达式。
        *   default:当要输出的内容为null时,会输出default指定的值。
        *   escapeXml:默认值为true,会自动转义特殊字符。

            <c:out value="aaa" />
                输出aaa字符串常量。

            <c:out value="${aaa }" />
                与${aaa }相同,输出一个变量。

            <c:out value="${aaa }" default="xxx" />
                当${aaa }不存在时,输出xxx字符串。

            <%request.setAttribute("a", "<script>alert('hello');</script>");%>
            <c:out value="${a }" default="xxx" escapeXml="false" />
                当escapeXml为false时,不会转换"<" ">"等字符,这可能会受到JavaScript攻击,因为域中存放的数据可能是用户在表单中填入的数据,如果填入了JavaScript脚本,会遭到攻击。
                escapeXml的默认值为true。

    2)set:(设置域属性)
        默认的域是pageContext。
        *   var:变量名(key)
        *   value:变量值(可以使EL表达式)
        *   scope:域,默认为page域,可选值:page、request、session、application。

            <c:set var="a" value="hello" />
                在pageContext域中添加name为a、value为hello的数据。

            <c:set var="a" value="hello" scope="session" />
                在session域中添加name为a、value为hello的数据。

    3)remove
        无默认域,默认删除所有域。
        *   var:变量名
        *   scope:域,如果不给出scope,表示删除所有域中该名称的变量,如果指定了域,那么只删除该域的变量。

            <%
                pageContext.setAttribute("a", "pageContext");
                request.setAttribute("a", "request");
                session.setAttribute("a", "session");
                application.setAttribute("a", "application");
            %>
            <c:remove var="a" />
            <c:out value="${a }" default="none"
                删除所有域中name为a的数据。

            <c:remove var="a" scope="page" />
                删除pageContext中name为a的数据。

    4)url
        url标签会在需要进行URL重写时添加SESSIONID。
        *   value:指定一个路径, 它会在路径前面自动添加项目名,指定的路径必须以“/”开头。
        *   子标签:
            <c:param>:用来给url后面添加参数,可以自动对参数进行URL编码。
        *   var:指定变量名,一旦添加该属性,那么url标签就不会再输出到页面,而是把生成的URL保存到域中,var表示key。
        *   scope:与var一起使用,用于保存URL到指定域,如果不写,则默认保存到pageContext。

            <c:url value="/" />
                输出当前上下文路径。

            <c:url value="/" var="a" scope="request" />
                把文本输出的结果赋值给变量a,范围为request域。

            <c:url value="/AServlet" />
                输出:/ServletDemo/AServlet

            <c:url value="/AServlet" >
                <c:param name="username" value="admin" />
                <c:param name="password" value="123456" />
            </c:url>
                输出:/ServletDemo/AServlet?username=admin&password=123456
                如果参数中包含中文,那么会自动使用URL编码。

    5)if
        只能表示if语句,不能表示else语句。
        if标签的test属性必须是一个boolean类型的值,如果test值为true,那么执行if标签中的内容,否则不执行。

            <c:set var="a" value="hello" />
            <c:if test="${not empty a }" >
                <c:out value="${a }" />
            </c:if>

    6)choose
        它表示if / else if / else
        when标签的test为true时,会执行这个when的内容,当所有when标签的test都为false时,才会执行otherwise标签的内容。

            <c:set var="score" value="${param.score }" />
            <c:choose>
                <c:when test="${score > 100 || score < 0 }" >分数错误</c:when>
                <c:when test="${score >= 90 }" > A </c:when>
                <c:when test="${score >= 80 }" > B </c:when>
                <c:when test="${score >= 70 }" > C </c:when>
                <c:when test="${score >= 60 }" > D </c:when>
                <c:otherwise>不及格</c:otherwise>
            </c:choose>

    7)forEach
        forEach标签有两种使用方式:
        >   使用循环变量:指定开始和结束值,类似for(int i = 1; i < 10; i++) { ... }
            *   var:循环变量。
            *   begin:设置循环变量从几开始。
            *   end:设置循环变量到几结束。
            *   step:设置循环步长,等同于Java中的i++或者i+=2,step默认值为1。
            <c:forEach var="i" begin="0" end="10" step="2">    //end相当于“<=”,包含等于
                ${i }<br/>
            </c:forEach>
        >   循环遍历集合,类似for(Object obj : 集合) { }
            <%
                int arr[] = {1,2,3,4,5,6,7,8};
                pageContext.setAttribute("arr", arr);
            %>
            <c:forEach items="${pageScope.arr }" var="val">//items中第二个大括号和分号之间不能加空格
                ${val } <br/>
            </c:forEach>

    8)forEach循环状态变量
        forEach标签还有一个属性:varStatus,这个属性用来指定接收“循环状态”的变量名,例如:<forEach varstatus="vs" ... />,这时候可以使用vs这个变量来获取循环的状态了。
        *   count:int类型,当前已遍历元素的个数。
        *   index:int类型,当前元素的下标。
        *   first:boolean类型,是否是第一个元素。
        *   last:boolean类型,是否是最后一个元素。
        *   current:Object类型,表示当前元素,与item相同。

            <%
                int arr[] = {1,2,3,4,5,6,7,8};
                pageContext.setAttribute("arr", arr);
            %>
            <c:forEach items="${arr }" var="item" varStatus="vs">
                <c:if test="${vs.first }">第一个元素</c:if><br/>
                这是第${vs.count }个元素,下标是${vs.index },元素是${vs.current }"<br/>
                <c:if test="${vs.last }">最后一个元素</c:if><br/>
            </c:forEach>

5、fmt标签库常用标签
    fmt标签库是用来格式化输出的,通常需要格式化的只有日期和数字。
    1)导入标签库
        <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
    2)格式化时间
        <%
            Date date = new Date();
            pageContext.setAttribute("date", date);
        %>
        <fmt:formatDate value="${date }" pattern="yyyy-MM-dd HH:mm:ss" />

    2)格式化数字
        <%
            double d1 = 3.5;
            double d2 = 4.4;
            pageContext.setAttribute("d1", d1);
            pageContext.setAttribute("d2", d2);
        %>
        <fmt:formatNumber value="${d1 }" pattern="0.00" /><br/>
            //必须且仅保留两位小数,如果大于两位,那么将四舍五入,如果小于两位,则补零。
            //保留2位小数,四舍五入,补零。
        <fmt:formatNumber value="${d2 }" pattern="#.##" /><br/> 
            //最多显示两位小数,如果小于两位,那么有几位保留几位,不会补零,大于两位小数则四舍五入,只保留两位小数。
             //保留2位小数,四舍五入,不补零。

6、核心库
    操作属性变量       条件操作               循环操作             URL操作
    out                         if                           forEach              import
    set                         choose                 forTokens           url
    remove                  when                                              redirect    
    catch                     otherwise                                        param



——自定义标签

一、自定义标签概述
    1、自定义标签的步骤
        其实我们在JSP页面中使用标签就等同于调用某个对象的某个方法一样,例如:<c:if test=" ">,这就是在调用一个方法,自定义标签其实就是自定义一个类。

        标签也是一个对象,要想使用对象,就必须先有类。

        1)定义标签处理类:必须是Tag或SimpleTag的实现类。
        2)编写标签库描述符文件(TLD文件)

        SimpleTag接口是JSP2.0中新给出的接口,用来简化自定义标签,所以现在我们基本上都是使用SimpleTag。
        Tag是旧接口,传统的自定义标签时使用的接口,现在不建议使用了。

    2、SimpleTag接口介绍
        是一个接口,无构造方法。
        方法概要:
            void  doTag()
                标签执行方法。
                每次执行标签,都会调用该方法。
                该方法会在其他三个生命周期方法之后被Tomcat调用。

            JspTag  getParent()
                获取父标签。
                非生命周期方法,需要程序员手动执行。

            void  setJspBody(JspFragment jspBody)
                设置标签体。
                JspFragment表示标签体的内容,因为标签体有可能是EL表达式等,所以需要使用JspFragment进行设置。

            void  setJspContext(JspContext pc)
                设置JSP上下文对象,JspContext的实现类是PageContext。
                也就是设置PageContext。

            void setParent(JspTag parent)
                设置父标签。
        万物皆对象,在JSP页面中的标签也是对象,可以通过查看JSP编译后的源文件,清楚到知道,所有的标签都会变成对象的方法调用,标签对应的类称之为标签处理类。


    3、创建一个实现SimpleTag接口的类
/**
 * 自定义标签1
 * @author 31067
 *
 */
 
public class MyTag1 implements SimpleTag {
    //创建局部变量,用于保存对象 
    private PageContext pageContext = null;
    private JspFragment body = null;
 
    /**
     * 所有的setXxx()方法都会在doTag()方法之前被Tomcat调用。
     * 所以在doTag()中就可以使用Tomcat传递的对象。
     */
    public void doTag() throws JspException, IOException {
        pageContext.getOut().print("Hello World");
    }
 
    public JspTag getParent() {
        return null;
    }
 
    public void setJspBody(JspFragment body) {
        this.body = body;
    }
 
    public void setJspContext(JspContext context) {
        this.pageContext = (PageContext)context;
    }
 
    public void setParent(JspTag arg0) {}
}


    4、配置tld文件。
<?xml version="1.0" encoding="UTF-8"?>
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
 
    <tlib-version>1.0</tlib-version>
    <short-name>wyc</short-name>
 
    <!-- 配置标签 -->
    <tag>
        <!-- 标签名 -->
        <name>myTag2</name>
        <!-- 标签处理类 -->
        <tag-class>com.wyc.tag.MyTag1</tag-class>
        <!-- 标签体,这里使用空标签 -->
        <body-content>empty</body-content>
        <!-- 在HTML中,br和hr就是空标签 --> 
    </tag>
</taglib>

    5、在页面中指定tld文件位置
        通过taglib指令导入标签库,就是指定tld文件的位置。
        <%@ taglib prefix="w" uri="/WEB-INF/tlds/wyc-tag.tld" %>

    5、步骤总结
        1)先创建一个类,并且实现SimpleTag接口,服务器在调用方法时,会传递pageContext,所以需要创建局部变量进行保存。
        2)在doTag()方法内编写方法体,该方法会在每次执行标签时被调用。

    6、使用SimpleTag实现类SimpleTagSupport创建标签
/**
 * SimpleTagSupport实现了SimpleTag接口
 * 它已经把所有的Tomcat传递的数据都保存起来了,并且提供了get方法供子类调用
 * @author 31067
 *
 */
 
public class MyTag2 extends SimpleTagSupport {
    public void doTag() throws IOException {
        this.getJspContext();//该对象没有强转,需要手动进行强制类型转换
        this.getJspBody();
        this.getJspContext().getOut().print("haha");
    }
}

    <!-- 配置标签 -->
    <tag>
        <!-- 标签名 -->
        <name>myTag2</name>
        <!-- 标签处理类 -->
        <tag-class>com.wyc.tag.MyTag2</tag-class>
        <!-- 标签体 -->
        <body-content>empty</body-content>
    </tag> 


二、继承SimpleTagSupport
    继承SimpleTagSupport要比实现SimpleTag接口方便太多,现在只需要重写doTag()方法即可,因为其他方法都已经被SimpleTagSupport完成了。

    public class MyTag extends SimpleTagSupport {
        public void doTag() throws JspException, IOException{
            this.getJspContext().getOut().write("content");
        }
    }
    需要注意的是,不能向页面输出Java脚本。


    1、有标签体的标签
        <body-content>元素的可选值有:
            *   empty:无标签体,例如<br/><hr/>
            *   JSP:传统标签支持它,SimpleTag已经不再支持<body-content>JSP</body-content>,标签体内容可以是:EL、JSTL、<%= %>、<% %>以及HTML。
            *   scriptless:标签体内容不能是Java脚本,但可以是EL、JSTL、普通字符串等数据,在SimpleTag中,如果需要有标签体,那么就使用该项。一旦选择该属性,则传到后台的数据就是一个字符串。
            *   tagdependent:标签体内容不做运算,由标签处理类自行处理,无论标签体内容是EL、JSP、JSTL,都不会做运算,这个属性几乎没有人使用。

    自定义有标签体的标签需要:
        1)获取标签体对象:JspFragment jspBody = getJspBody();
        2)把标签体内容输出到页面:jspBody.invoke(null);    //参数为null相当于使用JSP页面中的out对象
        3)tld中指定标签内容类型:scriptless

    范例:
        public class MyTag3 extends SimpleTagSupport {
            public void doTag() throws  IOException, JspException{
                //获取当前JSP页面的输出流
                Writer out = this.getJspContext().getOut();
                out.write("*********************<br/>");
                //执行标签体内容,把结果输出到指定的流中,即页面。
                this.getJspBody().invoke(out);
                out.write("<br/>*********************");
            }
        }

        <%@ taglib prefix="w3" uri="/WEB-INF/tlds/wyc-tag.tld" %>
        <%
            request.setAttribute("xxx", "zhangsan");
        %>
        //这个标签体会被标签处理类获取,然后执行对应EL表达式。
        <w3:myTag3>${xxx }</w3:myTag3>

        <!-- 配置标签 -->
        <tag>
            <!-- 标签名 -->
            <name>myTag3</name>
            <!-- 标签处理类 -->
            <tag-class>com.wyc.tag.MyTag3</tag-class>
            <!-- 标签体 -->
            <body-content>scriptless</body-content>
        </tag>


    2、不执行标签下面的页面内容
        如果希望在执行了自定义标签后,不再执行JSP页面下面的内容,那么就需要在doTag()方法中使用SkipPageException。
        Tomcat会调用标签处理类的doGet()方法,然后Tomcat会通过抛出的异常(throw SkipPageException)得到异常对象,然后跳过本页面中其他内容。

        public class SkipTag extends SimpleTagSupport {
            public void doTag() throws IOException, SkipPageException{
                this.getJspContext().getOut().print("<h1>只能看到我</h1>");
                throw new SkipPageException();
            }
        }

        <%@ taglib prefix="skip" uri="/WEB-INF/tlds/wyc-tag.tld" %>
        <skip:skipTag></skip:skipTag>
        <h1>你看不见我</h1>

        <!-- 配置标签 -->
        <tag>
            <!-- 标签名 -->
            <name>skipTag</name>
            <!-- 标签处理类 -->
            <tag-class>com.wyc.tag.SkipTag</tag-class>
            <!-- 标签体 -->
            <body-content></body-content>
        </tag> 

    SkipPageException原理:

图片

图片

图片



    3、有属性的标签
        步骤:
            1)给标签处理类添加属性,属性至少要有一个set方法,这个set方法会在doTag()方法之前会被Tomcat调用,所以在doTag()方法中可以直接使用该属性。
                public class MyTag5 extends SimpleTagSupport {
                    private boolean test;
                    //这个方法会由Tomcat调用,并且在doTag()之前调用
                    public void setTest(boolean test) {
                        this.test = test;
                    }

                    //当执行完set方法之后,开始执行doTag()方法
                    public void doTag() throws  IOException, JspException{
                        if(test)
                        {
                            //执行标签体
                            //如果传递的输出流为null,则表示使用当前JSP页面的out对象
                            //相当于this.getJspBody().invoke(this.getJspContext().getOut());
                            this.getJspBody().invoke(null);
                        }
                    }
                }

            2)在tld文件中对属性进行配置。
                <!-- 配置标签 -->
                <tag>
                    <!-- 标签名 -->
                    <name>myTag5</name>
                    <!-- 标签处理类 -->
                    <tag-class>com.wyc.tag.MyTag5</tag-class>
                    <!-- 标签体 -->
                    <body-content>scriptless</body-content>
                    <!-- 配置属性 -->
                    <attribute>
                        <!-- 属性名 -->
                        <name>test</name>
                        <!-- 配置是否必须给出该属性,true表示必须,false表示非必须 -->
                        <required>true</required>
                        <!-- Runtime Expretion Value -->
                        <!-- 表示标签的值是否可以是EL表达式(变量) -->
                        <rtexprvalue>true</rtexprvalue>
                    </attribute>
                </tag>

        3)在JSP页面中使用自定义标签
            <%@ taglib prefix="w" uri="/WEB-INF/tlds/wyc-tag.tld" %>
            <w:myTag5 test="true">
                这是true

            </w:myTag5>

posted @ 2017-02-07 17:53  WWWYC  阅读(552)  评论(0编辑  收藏  举报