zoukankan      html  css  js  c++  java
  • JavaWeb(七):EL表达式、自定义标签和JSTL

     一、EL表达式

    语法

    el.jsp
    <%@page import="java.util.Date"%>
    <%@page import="com.atguigu.javaweb.Customer"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        
        <form action="el.jsp" method="post">
            
            username: <input type="text" name="username" 
                value="<%= request.getParameter("username") == null ? "" : request.getParameter("username")%>"/>
            
            <!--  
                EL 表达式的有点: 简洁!
            -->    
            username: <input type="text" name="username" 
                value="${param.username }"/>
            <input type="submit" value="Submit"/>
            
        </form>
        
        username: <%= request.getParameter("username") %>
        
        <br><br>
        
        <jsp:useBean id="customer" class="com.atguigu.javaweb.Customer" scope="session"></jsp:useBean>
        <jsp:setProperty property="age" value="12" name="customer"/>
        
        age: 
        <% 
            Customer customer2 = (Customer)session.getAttribute("customer");
            out.print(customer2.getAge());
        %>
        <br>
        age: <jsp:getProperty property="age" name="customer"/>
    
        <br>
        <br>
        
        <% 
            application.setAttribute("time", new Date());
        %>
        
        <a href="el2.jsp?score=89&name=A&name=B&name=C">To El2 Page</a>
        
    </body>
    </html>
    下面都在el2.jsp中

    可以使用点和中括号运算符

        <!-- 1. EL 的 . 或 [] 运算符 -->
        age: ${sessionScope.customer["age"] }
        
        <%-- 
            Customer customer = (Customer)session.getAttribute("customer");
            out.print(customer.getAge());
        --%>
        
        <% 
            Customer customer = new Customer();
            customer.setName("ATGUIGU");    
        
            session.setAttribute("com.atguigu.customer", customer);
        %>
        
        <br>
        <!--  
            如果域对象中的属性名带有特殊字符, 则使用 [] 运算符会很方便. 
        -->
        name: ${sessionScope["com.atguigu.customer"].name }
        

    EL中的隐含对象,EL可以大大简化代码

        <!-- 2. EL 中的隐含对象 -->
        <% 
            Customer cust2 = new Customer();
            cust2.setAge(28);
            request.setAttribute("customer", cust2);
        %>
        
        age: ${customer.age } 

    可以自动类型转换

    上面的结果是加了11的数

    下面的结果是字符串

        <!-- 3. EL 可以进行自动的类型转换 -->
        score: ${param.score + 11}
        <br>
        score: <%= request.getParameter("score") + 11 %>
        <br>
        <!-- 4. 隐含对象之与范围相关的: pageScope, requestScope, sessionScope, applicationScope -->
        time: ${applicationScope.time.time }
        <%-- 
        <%= application.getAttribute("time") %>
        --%>
        <!-- 5. 与输入有关的隐含对象: param, paramValues -->
        score: ${param.score }
        <%-- 
        <%= request.getParameter("score") %>
        --%>
        <br>
        names: ${paramValues.name[0].class.name }
        <%-- 
        <%= 
            request.getParameterValues("name")[0].getClass().getName()
        %>
        --%>
        <!-- 6. 其他隐含对象: pageContext 等(cookie, header, initParam 只需了解.) -->
        pageContext: pageContext 即为 PageContext 类型, 但只能读取属性就可以一直的 . 下去。 
        <br>
        contextPath: ${pageContext.request.contextPath }
        
        <br>
        sessionId: ${pageContext.session.id }
        
        <br>
        sessionAttributeNames: ${pageContext.session.attributeNames }
        
        <br>
        
        
        initParam: ${initParam.initName }
        <br>
        
        Accept-Language: ${header["Accept-Language"] }
        <br>
    
        JSESSIONID: ${cookie.JSESSIONID.name } -- ${cookie.JSESSIONID.value }
        <br>
        <!-- 7. EL 的运算符 -->
        ${param.score > 60 ? "及格" : "不及格" }
        <br>
        
        <% 
            List<String> names = new ArrayList<String>();
            names.add("abc");
            request.setAttribute("names", names);
        %>
        <!-- empty 可以作用于一个集合, 若该集合不存在或集合中没有元素, 其结果都为 true -->
        names is empty: ${empty requestScope.names }

    页面中不出现任何Java代码

    使用标签

    index.jsp

    <%@page import="com.atguigu.javaweb.Customer"%>
    <%@page import="java.util.ArrayList"%>
    <%@page import="java.util.List"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        
        <% 
            //模拟 Servlet 中的操作.
            List<Customer> customers = new ArrayList<Customer>();
            customers.add(new Customer(1, "AA", 12));
            customers.add(new Customer(2, "BB", 13));
            customers.add(new Customer(3, "CC", 14));
            customers.add(new Customer(4, "DD", 15));
            customers.add(new Customer(5, "EE", 16));
            
            request.setAttribute("customers", customers);
        %>
        
        <jsp:forward page="testtag.jsp"></jsp:forward>
        
    </body>
    </html>

    testtag.jsp

    <%@page import="com.atguigu.javaweb.Customer"%>
    <%@page import="java.util.List"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
        
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
        
        
        <!-- 在页面上对 request 中的 customers 属性进行遍历, 打印 id, name, age -->
        <c:forEach items="${requestScope.customers }" var="customer">
            --${customer.id }, ${customer.name }, ${customer.age }<br>
        </c:forEach>
        
        
        <%-- 
            List<Customer> customers = (List<Customer>)request.getAttribute("customers");
        
            if(customers != null){
                for(Customer customer: customers){
        %>
                    <%= customer.getId() %>, <%= customer.getName() %>, <%= customer.getAge() %><br>
        <%            
                }
            }
        --%>
        
    </body>
    </html>

    二、自定义标签

    2.1 HelloWorld

    ①. 创建一个标签处理器类: 实现 SimpleTag 接口.

    package com.atguigu.javaweb.tag;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspContext;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.PageContext;
    import javax.servlet.jsp.tagext.JspFragment;
    import javax.servlet.jsp.tagext.JspTag;
    import javax.servlet.jsp.tagext.SimpleTag;
    
    public class HelloSimpleTag implements SimpleTag {
    
        @Override
        public void doTag() throws JspException, IOException {
            System.out.println("doTag");
        }
    
        @Override
        public JspTag getParent() {
            System.out.println("getParent");
            return null;
        }
    
        @Override
        public void setJspBody(JspFragment arg0) {
            System.out.println("setJspBody");
        }
        @Override
        public void setJspContext(JspContext arg0) {
            System.out.println("setJspContext");  
        }
    
        @Override
        public void setParent(JspTag arg0) {
            System.out.println("setParent");
        }
    
    }

    ②. 在 WEB-INF 文件夹下新建一个 .tld(标签库描述文件) 为扩展名的 xml 文件. 并拷入固定的部分: 并对
    description, display-name, tlib-version, short-name, uri 做出修改

    <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">
        
      <description>JSTL 1.1 core library</description>
      <display-name>JSTL core</display-name>
      <tlib-version>1.1</tlib-version>
      <short-name>c</short-name>
      <uri>http://java.sun.com/jsp/jstl/core</uri>
      
    </taglib>

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

    <!-- 描述自定义的 HelloSimpleTag 标签 -->
      <tag>
          <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
          <name>hello</name>
          
          <!-- 标签所在的全类名 -->
          <tag-class>com.atguigu.javaweb.tag.HelloSimpleTag</tag-class>
          <!-- 标签体的类型 -->
          <body-content>empty</body-content>
      </tag>


    ④. 在 JSP 页面上使用自定义标签:

    > 使用 taglib 指令导入标签库描述文件: <%@taglib uri="http://www.atguigu.com/mytag/core" prefix="atguigu" %>

    > 使用自定义的标签: <atguigu:hello/>

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    <!-- 导入标签库(描述文件) -->    
    <%@taglib uri="http://www.atguigu.com/mytag/core" prefix="atguigu" %>
        
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        
        <atguigu:hello />
        
    </body>
    </html>

    使用自定义标签的JSP和tld 文件的对应:

    2.2 带属性的自定义标签

    上面的例子太简单了,下面使用带属性的自定义标签

    标签有value和count两个属性,我们想实现打印value值count次,即打印我们传入的参数10次

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    
    <!-- 导入标签库(描述文件) -->    
    <%@taglib uri="http://www.atguigu.com/mytag/core" prefix="atguigu" %>
        
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
        
        <atguigu:hello value="${param.name }" count="10"/>
        
    </body>
    </html>

    属性需要在tld文件中进行设置

    value是必需的属性,count不能用表达式

    <?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>atguigu</short-name>
        <!-- 作为 tld 文件的 id, 用来唯一标识当前的 TLD 文件, 多个 tld 文件的 URI 不能重复. 通过 JSP 页面的 taglib 
            标签的 uri 属性来引用. -->
        <uri>http://www.atguigu.com/mytag/core</uri>
    
        <!-- 描述自定义的 HelloSimpleTag 标签 -->
        <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>hello</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.atguigu.javaweb.tag.HelloSimpleTag</tag-class>
            <!-- 标签体的类型 -->
            <body-content>empty</body-content>
    
            <!-- 描述当前标签的属性 -->
            <attribute>
                <!-- 属性名 -->
                <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>
    
    </taglib>  

    标签处理器类

    package com.atguigu.javaweb.tag;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspContext;
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.JspWriter;
    import javax.servlet.jsp.PageContext;
    import javax.servlet.jsp.tagext.JspFragment;
    import javax.servlet.jsp.tagext.JspTag;
    import javax.servlet.jsp.tagext.SimpleTag;
    
    public class HelloSimpleTag implements SimpleTag {
    
        private String value;
        private String count;
        
        public void setValue(String value) {
            this.value = value;
        }
        
        public void setCount(String count) {
            this.count = count;
        }
        
        //标签体逻辑实际应该编写到该方法中. 
        @Override
        public void doTag() throws JspException, IOException {
            
            JspWriter out = pageContext.getOut();
            int c = 0;
            
            c = Integer.parseInt(count);
            for(int i = 0; i < c; i++){
                out.print((i + 1) + ": " + value);
                out.print("<br>");
            }
        }
    
        @Override
        public JspTag getParent() {
            System.out.println("getParent");
            return null;
        }
    
        @Override
        public void setJspBody(JspFragment arg0) {
            System.out.println("setJspBody");
        }
    
        private PageContext pageContext;
        
        //JSP 引擎调用, 把代表 JSP 页面的 PageContext 对象传入
        //PageContext 可以获取 JSP 页面的其他 8 个隐含对象. 
        //所以凡是 JSP 页面可以做的标签处理器都可以完成. 
        @Override
        public void setJspContext(JspContext arg0) {
            System.out.println(arg0 instanceof PageContext);  
            this.pageContext = (PageContext) arg0;
        }
    
        @Override
        public void setParent(JspTag arg0) {
            System.out.println("setParent");
        }
    
    }

    小结:

     setJspContext: 一定会被 JSP 引擎所调用, 先于 doTag, 把代表 JSP 引擎的 pageContext 传给标签处理器类.

    @Override
    public void setJspContext(JspContext arg0) {
        System.out.println(arg0 instanceof PageContext);  
        this.pageContext = (PageContext) arg0;
    }

    小结

    ①. 先在标签处理器类中定义 setter 方法. 建议把所有的属性类型都设置为 String 类型.

    private String value;
    private String count;
    
    public void setValue(String value) {
        this.value = value;
    }
    
    public void setCount(String count) {
        this.count = count;
    }

    ②. 在 tld 描述文件中来描述属性:

    <!-- 描述当前标签的属性 -->
    <attribute>
        <!-- 属性名, 需和标签处理器类的 setter 方法定义的属性相同 -->
        <name>value</name>
        <!-- 该属性是否被必须 -->
        <required>true</required>
        <!-- rtexprvalue: runtime expression value 
            当前属性是否可以接受运行时表达式的动态值 -->
        <rtexprvalue>true</rtexprvalue>
    </attribute>

    ③. 在页面中使用属性, 属性名同 tld 文件中定义的名字.

    <atguigu:hello value="${param.name }" count="10"/>

    练习

    定制一个带有两个属性的标签<max>, 用于计算并输出两个数的最大值

    实现SimpleTag接口会有大量的空方法,通常情况下开发简单标签直接继承 SimpleTagSupport 就可以了。可以直接调用其对应的 getter 方法得到对应的 API

    public class SimpleTagSupport implements SimpleTag{
        
        public void doTag() 
            throws JspException, IOException{}
        
        private JspTag parentTag;
        
        public void setParent( JspTag parent ) {
            this.parentTag = parent;
        }
        
        public JspTag getParent() {
            return this.parentTag;
        }
        
        private JspContext jspContext;
        
        public void setJspContext( JspContext pc ) {
            this.jspContext = pc;
        }
        
        protected JspContext getJspContext() {
            return this.jspContext;
        }
        
        private JspFragment jspBody;
                    
        public void setJspBody( JspFragment jspBody ) {
            this.jspBody = jspBody;
        }
        
        protected JspFragment getJspBody() {
            return this.jspBody;
        }   
    }

    2.3 带标签体的自定义标签

    JspFragment 
    该类的实例对象代表 JSP 页面中的一段符合 JSP 语法规范的 JSP 片段,这段 JSP 片段不能包含 JSP 脚本元素(<% … %>)
    JSP 引擎在处理简单标签的标签体时,会把标签体内容用一个 JspFragment 对象表示,并调用标签处理器对象的 setJspBody 方法把 JspFragment 对象传递给标签处理器对象。得到代表标签体的 JspFragment 对象后,标签开发者和就可以在标签处理器中根据需要调用 JspFragment 对象的方法,进而决定如何处理标签体。

    getJspContext 方法:该方法用于返回代表调用页面的 JspContext 对象
    invoke 方法(java.io.Writer out):该方法用于执行 JspFragment 对象所代表的 JSP 代码片段。在 doTag() 方法中可以根据需要调用该方法。

    • 该方法的参数 out 用于指定将 JspFragment 对象的执行结果写入到哪个输出流对象中。若传递参数 out 的值为 null,则将执行结果写入到 JspContext.geOut() 方法返回的输出流对象中。
    • 若想在标签处理器中修改标签体内容:需在调用 invoke 方法时指定一个可取出结果数据的输出流对象(如:StringWriter),让标签体的执行结果输出到该输出流中,然后从该输出流对象中取出数据进行修改后再输出到目标设备

    示例

    1)有标签体的标签

    <atguigu:testJspFragment>abcdefg</atguigu:testJspFragment>

    test.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="aidata" uri="http://www.aidata.com/mytag/core" %> 
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <aidata:testJspFragment>HelloWorld</aidata:testJspFragment>
    
    </body>
    </html>

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

    TestJspFragment

    package com.aidata.web;
    
    import java.io.IOException;
    import java.io.StringWriter;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.JspFragment;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class TestJspFragment extends SimpleTagSupport {
    
        @Override
        public void doTag() throws JspException, IOException {
            JspFragment bodyContent = getJspBody();
            // JspFragment.invoke(Writer);
            // Writer即位标签体内输出的字符流,若为null
            // 则输出getJspContext().getOut(),即输出到页面
            // bodyContent.invoke(null);
    
            // 为了打印的到控制台,用stringwriter
            // 1.用StringWriter得到标签体的内容
            StringWriter sw = new StringWriter();
            bodyContent.invoke(sw);
            // 2.把标签体的内容都变为大写
            String content = sw.toString().toUpperCase();
            // 3.获取JSP页面的out隐含对象,输出到页面上
            getJspContext().getOut().print(content);
            System.out.println(sw.toString());
        }
    }

    若配置了标签含有标签体, 则 JSP 引擎会调用 setJspBody() 方法把 JspFragment 传递给标签处理器类

    在 SimpleTagSupport 中还定义了一个 getJspBody() 方法, 用于返回 JspFragment 对象.JspFragment 的 invoke(Writer) 方法: 把标签体内容从 Writer 中输出, 若为 null,则等同于 invoke(getJspContext().getOut()), 即直接把标签体内容输出到页面上

    有时, 可以 借助于 StringWriter, 可以在标签处理器类中先得到标签体的内容,即上面代码中的:

    //1. 利用 StringWriter 得到标签体的内容.
    StringWriter sw = new StringWriter();
    bodyContent.invoke(sw);
    
    //2. 把标签体的内容都变为大写
    String content = sw.toString().toUpperCase();

    3)配置tld文件

    mytag.tld

    <?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.2 core library</description>
        <display-name>MyTag core</display-name>
        <tlib-version>1.0</tlib-version>
    
        <!-- 建议在 JSP 页面上使用的标签的前缀 -->
        <short-name>aidata</short-name>
        <!-- 作为 tld 文件的 id, 用来唯一标识当前的 TLD 文件, 多个 tld 文件的 URI 不能重复. 通过 JSP 页面的 taglib 
            标签的 uri 属性来引用. -->
        <uri>http://www.aidata.com/mytag/core</uri>
    
        <!-- 描述自定义的 HelloSimpleTag 标签 -->
        <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>testJspFragment</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.aidata.web.TestJspFragment</tag-class>
            <!-- 标签体的类型 -->
            <body-content>scriptless</body-content>
        </tag>
    
    </taglib>

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

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

    • empty: 没有标签体
    • scriptless: 标签体可以包含 el 表达式和 JSP 动作元素,但不能包含 JSP 的脚本元素
    • tagdependent: 表示标签体交由标签本身去解析处理。

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

    <body-content>tagdependent</body-content>

    练习

    定义一个自定义标签: <aidata:printUpper time="10">abcdefg</atguigu> 把标签体内容转换为大写,并输出 time 次到
    浏览器上

    标签处理器类

    package com.aidata.web;
    
    import java.io.IOException;
    import java.io.StringWriter;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.JspFragment;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class PrintUpperTag extends SimpleTagSupport {
    
        private String time;
    
        public void setTime(String time) {
            this.time = time;
        }
    
        @Override
        public void doTag() throws JspException, IOException {
            // 1.得到标签体内容
            JspFragment bodyContent = getJspBody();
            StringWriter sw = new StringWriter();
            bodyContent.invoke(sw);
            String content = sw.toString();
    
            // 2.变为大写
            content = content.toUpperCase();
    
            // 3.得到out隐含变量
            // 4.循环输出
            int count = 1;
            try {
                count = Integer.parseInt(time);
            } catch (Exception e) {
            }
    
            for (int i = 0; i < count; i++) {
                getJspContext().getOut().print(i + 1 + "." + content + "<br>");
            }
        }
    }

    配置tld

        <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>printUpper</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.aidata.web.PrintUpperTag</tag-class>
            <!-- 标签体的类型 -->
            <body-content>scriptless</body-content>
            <attribute>
                <name>time</name>
                <required>true</required>
                <rtexprvalue>true</rtexprvalue>
            </attribute>
        </tag>

    test.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="aidata" uri="http://www.aidata.com/mytag/core" %> 
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    <aidata:printUpper time="10">abcdefg</aidata:printUpper>
    
    </body>
    </html>

    实现 forEach 标签

    首先使用JSTL的forEach标签

    定义Java bean Customer

    package com.aidata.web;
    
    public class Customer {
    
        private Integer id;
        private String name;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Customer() {
    
        }
    
        public Customer(Integer id, String name) {
            super();
            this.id = id;
            this.name = name;
        }
    
    }

    test.jsp

    <%@page import="com.aidata.web.Customer"%>
    <%@page import="java.util.ArrayList"%>
    <%@page import="java.util.List"%>
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 
    
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    
    
        <%
            List<Customer> customers = new ArrayList<Customer>();
            customers.add(new Customer(1, "aaa"));
            customers.add(new Customer(2, "bbb"));
            customers.add(new Customer(3, "ccc"));
            customers.add(new Customer(4, "ddd"));
            customers.add(new Customer(5, "eee"));
            request.setAttribute("customers", customers);
        %>
        
        <c:forEach items="${requestScope.customers }" var="cust">
            ${cust.id } -- ${cust.name } <br>
        </c:forEach>
    
    </body>
    </html>

    自定义forEach

    处理器类

    > 两个属性: items(集合类型, Collection), var(String 类型)

    > doTag: 

    • 遍历 items 对应的集合
    • 把正在遍历的对象放入到 pageContext 中, 键: var, 值: 正在遍历的对象.
    • 把标签体的内容直接输出到页面上.
    package com.aidata.web;
    
    import java.io.IOException;
    import java.util.Collection;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class ForEachTag extends SimpleTagSupport {
    
        private Collection<?> items;
    
        public void setItems(Collection<?> items) {
            this.items = items;
        }
    
        private String var;
    
        public void setVar(String var) {
            this.var = var;
        }
    
        @Override
        public void doTag() throws JspException, IOException {
            // 遍历items对应的集合
            if (items != null) {
                for (Object obj : items) {
                    // 把正在遍历的对象放入到pageContext中,键:var,值:正在遍历的对象
                    getJspContext().setAttribute(var, obj);
                    // 把标签体的内容直接输出到页面
                    getJspBody().invoke(null);
                }
            }
        }
    }

    tld配置文件

            <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>forEach</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.aidata.web.ForEachTag</tag-class>
            <!-- 标签体的类型 -->
            <body-content>scriptless</body-content>
            <attribute>
                <name>items</name>
                <required>true</required>
                <rtexprvalue>true</rtexprvalue>
            </attribute>
             <attribute>
                <name>var</name>
                <required>true</required>
                <rtexprvalue>true</rtexprvalue>
            </attribute>
        </tag>

    jsp

        <!--<c:forEach items="${requestScope.customers }" var="cust">
            ${cust.id } -- ${cust.name } <br>
        </c:forEach>-->
    
        <aidata:forEach items="${requestScope.customers }" var="cust">
             ${cust.id } -- ${cust.name } <br>
        </aidata:forEach>

    2.4 带父标签的自定义标签

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

    test.jsp

        <!-- 父标签打印name到控制台 -->
        <aidata:parentTag>
            <!-- 子标签以父标签的标签体存在,希望子标签把父标签的name属性打印到JSP页面上 -->
            <aidata:sonTag/>
        </aidata:parentTag>

    ParentTag

    package com.aidata.web;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class ParentTag extends SimpleTagSupport {
    
        private String name = "www.aidata.com";
    
        public String getName() {
            return name;
        }
    
        @Override
        public void doTag() throws JspException, IOException {
            System.out.println("父标签处理器类的name" + name);
            getJspBody().invoke(null);
        }
    }

    2). 子标签可以通过 getParent() 方法来获取父标签的引用(需继承 SimpleTagSupport 或自实现 SimpleTag 接口的该方法):
    若子标签的确有父标签, JSP 引擎会把代表父标签的引用通过 setParent(JspTag parent) 赋给标签处理器

    3). 注意: 父标签的类型是 JspTag 类型. 该接口是一个空接口, 但是来统一 SimpleTag 和 Tag 的. 实际使用需要进行类型的强制转换.

     SonTag

    package com.aidata.web;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.JspTag;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class SonTag extends SimpleTagSupport {
    
        @Override
        public void doTag() throws JspException, IOException {
            // 1. 得到父标签的引用
            JspTag parent = getParent();
    
            // 2. 获取父标签的 name 属性
            ParentTag parentTag = (ParentTag) parent;
            String name = parentTag.getName();
    
            // 3. 把 name 值打印到 JSP 页面上.
            getJspContext().getOut().print("子标签输出name: " + name);
        }
    
    }

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

        <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>parentTag</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.aidata.web.ParentTag</tag-class>
            <!-- 标签体的类型 -->
            <body-content>scriptless</body-content>
        </tag>
        <tag>
            <!-- 标签的名字: 在 JSP 页面上使用标签时的名字 -->
            <name>sonTag</name>
    
            <!-- 标签所在的全类名 -->
            <tag-class>com.aidata.web.SonTag</tag-class>
            <!-- 标签体的类型 -->
            <body-content>empty</body-content>
        </tag>

    实现choose when otherwise标签

    <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是否执行。判断子标签在满足条件的情况下是否执行.

    • 若 when 的 test 为 true,即满足条件,且 when 的父标签的 flag 也为 true,则执行 when 的标签体(正常输出标签体的内容)
    • 同时把 flag 设置为 false
    • 若 when 的 test 为 true,且 when 的父标签的 flag 为 false, 则不执行标签体.
    • 若flag 为 true, otherwise 执行标签体.

    父标签

    ChooseTag

    package com.aidata.web;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    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 {
            getJspBody().invoke(null);
        }
    }

    子标签

    WhenTag

    package com.aidata.web;
    
    import java.io.IOException;
    
    import javax.servlet.jsp.JspException;
    import javax.servlet.jsp.tagext.SimpleTagSupport;
    
    public class WhenTag extends SimpleTagSupport{
    
        private boolean test;
        
        public void setTest(boolean test) {
            this.test = test;
        }
        
        @Override
        public void doTag() throws JspException, IOException {
            if(test){
                
                ChooseTag chooseTag = (ChooseTag) getParent();
                boolean flag = chooseTag.isFlag();
                
                if(flag){
                    getJspBody().invoke(null);
                    chooseTag.setFlag(false);
                }
                
            }
        }
        
    }

    OtherwiseTag

    package com.aidata.web;
    
    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 chooseTag = (ChooseTag) getParent();
            
            if(chooseTag.isFlag()){
                getJspBody().invoke(null);
            }
        }
        
    }

     jsp

        <aidata:choose>
            <aidata:when test="${param.age > 24}">^大学毕业</aidata:when>
            <aidata:when test="${param.age > 20}">^高中毕业</aidata:when>
            <aidata:otherwise>^高中以下...</aidata:otherwise>
        </aidata:choose>

    tld

        <tag>
            <name>choose</name>
            <tag-class>com.aidata.web.ChooseTag</tag-class>
            <body-content>scriptless</body-content>
        </tag>
        
        <tag>
            <name>when</name>
            <tag-class>com.aidata.web.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.aidata.web.OtherwiseTag</tag-class>
            <body-content>scriptless</body-content>
        </tag>

    EL自定义函数

    JSTL提供了一些EL自定义函数

    引入

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

    使用

        <!-- 使用一个 EL 的自定义函数 -->
        ${fn:length(param.name) } 
        <br><br>
        
        ${fn:toUpperCase(param.name1) }

    自定义EL函数

    步骤

    • 编写 EL 自定义函数映射的Java 类中的静态方法,这个 Java 类必须带有 public 修饰符,方法必须是这个类的带有 public 修饰符的静态方法
      • package com.aidata.web;
        
        public class MyELFunction {
        
            public static String concat(String str1, String str2) {
        
                return str1 + str2;
            }
        }
    • 编写标签库描述文件(tld 文件), 在 tld 文件中描述自定义函数
      •     <!-- 描述 EL 的自定义函数 -->
            <function>    
                <name>concat</name>
                <function-class>com.atguigu.javaweb.MyELFunction</function-class>
                <function-signature>java.lang.String concat(java.lang.String, java.lang.String)</function-signature>
            </function>
    • 在 JSP 页面中导入和使用自定义函数
    •     <!-- 测试自定义的 EL 函数 -->
          ${aidata:concat(param.name1, param.name2)}

    三、JSTL

    JSTL 全名为JavaServer Pages Standard Tag Library,是由JCP(Java Community Process)所指定的标准规格,它主要提供给Java Web 开发人员一个标准通用的标签函数库。Web 程序开发人员能够利用JSTL 和EL来开发Web 程序,取代传统直接在页面上嵌入Java程序(Scripting)的做法,以提高程序可读性、维护性和方便性。

    最重要的是核心标签库

    使用前引入jstl.jar和standard.jar即可

    核心标签库(Core)主要有:基本输入输出、流程控制、迭代操作和URL操作。

    3.1  表达式操作

    <c:out>

    <c:out>主要用来显示数据的内容,就像是 <%= scripting-language %> 一样

    语法

     属性

     假若 value为null,会显示default 的值;假若没有设定default的值,则会显示一个空的字符串。

    一般来说,<c:out>默认会将 <、>、’、” 和 & 转换为 &lt;、&gt;、&#039;、&#034; 和 &amp;。假若不想转换时,只需要设定<c:out>的escapeXml 属性为fasle 就可以了

    EL表达式遇到特殊字符如下面的<,无法输出

    out则可以自动的转换

        <h4>c:out: 可以对特殊字符进行转换. </h4>
        
        <% 
            request.setAttribute("book", "<<Java>>");
        %>
        book: ${requestScope.book }
        <br><br>
        book: <c:out value="${requestScope.book }" default="booktitle"></c:out>

    <c:set>

    <c:set>主要用来将变量储存至JSP范围中或是JavaBean 的属性

    语法

                              语法3:

    使用<c:set>时,var 主要用来存放表达式的结果;scope 则是用来设定储存的范围,例如:假若scope="session",则将会把数据储存在session中。如果<c:set>中没有指定scope时,则它会默认存在Page 范围里。

        <h4>c:set: 可以为域赋属性值, 其中 value 属性支持 EL 表达式; 还可以为域对象中的 JavaBean 的属性赋值, target, value都支持 EL 表达式</h4>
        
        <c:set var="name" value="ATGUIGU" scope="page"></c:set>
        <%-- 
            pageContext.setAttribute("name", "atguigu");
        --%>
        name: ${pageScope.name }    
        
        <br><br>
        <c:set var="subject" value="${param.subject }" scope="session"></c:set>
        subject: ${sessionScope.subject }
        
        <br><br>
        <% 
            Customer cust = new Customer();
            cust.setId(1001);
            request.setAttribute("cust", cust);
        %>
        ID: ${requestScope.cust.id }
    <!-- 为javabean属性赋值 -->
    <c:set target="${requestScope.cust }" property="id" value="${param.id }"></c:set> <br> ID: ${requestScope.cust.id }

    <c:remove>

    <c:remove>主要用来移除变量。

    语法

    <c:remove var="varName" [scope="{ page|request|session|application }"] />

    属性

    <c:remove>必须要有var属性,即要被移除的属性名称,scope 则可有可无,例如:

    <c:remove var="number" scope="session" />

    将number 变量从Session 范围中移除。若我们不设定scope,则<c:remove>将会从Page、Request、Session 及Application 中顺序寻找是否存在名称为number 的数据,若能找到时,则将它移除掉,反之则不会做任何的事情。

        <h4>c:remove: 移除指定域对象的指定属性值</h4>
        <c:set value="1997-09-1" var="date" scope="session"></c:set>
        date: ${sessionScope.date }
        <br><br>
    
        <c:remove var="date" scope="session"/>
        date: --${sessionScope.date }--

    3.2 流程控制

    流程控制分类中包含四个标签:<c:if>、<c:choose>、<c:when>和<c:otherwise>

    <c:if>
    <c:if>的用途就和我们一般在程序中用的if 一样。

    语法

     属性

     <c:if> 标签必须要有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如果为false,则不会执行。例如:${param.username = = 'admin'},如果param.username 等于admin时,结果为true;若它的内容不等于admin时,则为false。

        <h4>c:if: 不能实现 else 操作, 但可以把结果储存起来。 </h4>
        <c:if test="${requestScope.age > 18 }">成年了!</c:if>
        <br><br>
        
        <c:if test="${param.age > 18 }" var="isAdult" scope="request"></c:if>
        isAdult: <c:out value="${requestScope.isAdult }"></c:out>

    <c:choose>
    <c:choose>本身只当做 <c:when> 和 <c:otherwise> 的父标签。

    语法

    <c:choose>的本体内容只能有:

    • ·空白
    • ·1 或多个 <c:when>
    • ·0 或多个 <c:otherwise>

    若使用<c:when>和<c:otherwise>来做流程控制时,两者都必须为<c:choose>的子标

    <c:when>
    <c:when> 的用途就和我们一般在程序中用的when 一样。

    语法

     属性

     <c:when>必须在<c:choose>和</c:choose>之间,在同一个<c:choose>中时,<c:when>必须在<c:otherwise>之前。<c:when>必须有test 属性,当test 中的表达式结果为true 时,则会执行本体内容;如果为false 时,则不会执行。

    <c:otherwise>
    在同一个 <c:choose> 中,当所有 <c:when> 的条件都没有成立时,则执行<c:otherwise> 的本体内容。

    语法

    <c:otherwise> 必须在 <c:choose> 和 </c:choose>之间,在同一个 <c:choose> 中时,<c:otherwise> 必须为最后一个标签。在同一个<c:choose> 中,假若所有<c:when> 的test 属性都不为true 时,则执行<c:otherwise> 的本体内容。

    <h4>
            c:choose, c:when, c:otherwise: 可以实现 if...else if...else if...else 的效果. 但较为麻烦
            其中: c:choose 以 c:when, c:otherwise 的父标签出现.
            c:when, c:otherwise 不能脱离 c:choose 单独使用.
            c:otherwise 必须在 c:when 之后使用。 
        </h4>
        <c:choose>        
            <c:when test="${param.age > 60 }">
                老年
            </c:when>
            <c:when test="${param.age > 35 }">
                中年
            </c:when>
            <c:when test="${param.age > 18 }">
                青年
            </c:when>
            <c:otherwise>
                未成年. 
            </c:otherwise>
        </c:choose>
    
        <c:set value="20" var="age" scope="request"></c:set>

    3.3 迭代操作

    <c:forEach>
    <c:forEach> 为循环控制,它可以将集合(Collection)中的成员循序浏览一遍。运作方式为当条件符合时,就会持续重复执行<c:forEach>的本体内容。

    语法

    属性

    假若有begin 属性时,begin 必须大于等于 0

    • ·假若有end 属性时,必须大于begin
    • ·假若有step 属性时,step 必须大于等于0

    Null 和错误处理

    • ·假若items 为null时,则表示为一空的集合对象
    • ·假若begin 大于或等于items 时,则迭代不运算

    如果要循序浏览一个集合对象,并将它的内容显示出来,就必须有items 属性。

    <!-- 遍历 Collection, 遍历数组同 Collection -->
        <c:forEach items="${requestScope.custs }" var="cust"
            varStatus="status">
            ${status.index}, ${status.count}, ${status.first}, ${status.last}: ${cust.id }: ${cust.name }<br>
        </c:forEach>
        
        <!-- 遍历 Map -->
        <% 
            Map<String, Customer> custMap = new HashMap<String, Customer>();
            custMap.put("a", new Customer(1, "AAA")); //index: 0 
            custMap.put("b", new Customer(2, "BBB")); //index: 0 
            custMap.put("c", new Customer(3, "CCC")); //index: 0 
            custMap.put("d", new Customer(4, "DDD")); //index: 0 
            custMap.put("e", new Customer(5, "EEE")); //index: 0 
            custMap.put("f", new Customer(6, "FFF")); //index: 0 
            
            request.setAttribute("custMap", custMap);
        %>
        
        <br><br>
        <c:forEach items="${requestScope.custMap }" var="cust">
            ${cust.key } - ${cust.value.id } - ${cust.value.name }<br>
        </c:forEach>
        
        <% 
            String [] names = new String[]{"A", "B", "C"};
            request.setAttribute("names", names);
        %>
        <br><br>
        <c:forEach var="name" items="${names }">${name }-</c:forEach>
        
        <br><br>
        <c:forEach items="${pageContext.session.attributeNames }" var="attrName">
            ${attrName }-
        </c:forEach>
       <h4>c:forTokens: 处理字符串的, 类似于 String 的 split() 方法</h4>
        <c:set value="a,b,c.d.e.f;g;h;j" var="test" scope="request"></c:set>
        <c:forTokens items="${requestScope.test }" delims="." var="s">
            ${s }<br>
        </c:forTokens>

    3.4 URL操作

    JSTL 包含三个与URL 操作有关的标签,它们分别为:<c:import>、<c:redirect>和<c:url>。它们主要的功能是:用来将其他文件的内容包含起来、网页的导向,还有url 的产生。

    <c:url>
    <c:url>主要用来产生一个URL。

    语法

    属性

        <h4>
            c:url 产生一个 url 地址. 可以 Cookie 是否可用来智能进行 URL 重写, 对 GET 请求的参数进行编码
            可以把产生的 URL 存储在域对象的属性中.
            还可以使用 c:param 为 URL 添加参数. c:url 会对参数进行自动的转码. 
            value 中的 / 代表的是当前 WEB 应用的根目录. 
        </h4>
        <c:url value="/test.jsp" var="testurl" scope="page">
            <c:param name="name" value="尚硅谷"></c:param>
        </c:url>
        
        url: ${testurl }
    
        <h4>
            c:redirect 使当前 JSP 页面重定向到指定的页面. 使当前 JSP 转发到指定页面可以使用
            <%--  
            <jsp:forward page="/test.jsp"></jsp:forward>    
            --%>
            / 代表的是当前 WEB 应用的根目录. 
            
            response.sendRedirect("/test.jsp") / 代表 WEB 站点的根目录
        </h4>
        <%-- 
        <c:redirect url="http://www.atguigu.com"></c:redirect>
        <c:redirect url="/test.jsp"></c:redirect>
        --%>
        
        <h4>c:import 可以包含任何页面到当前页面</h4>
        <c:import url="http://www.baidu.com"></c:import>

      

  • 相关阅读:
    sqlplus时报Linux-x86_64 Error: 13: Permission denied
    thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 合并石子
  • 原文地址:https://www.cnblogs.com/aidata/p/12005273.html
Copyright © 2011-2022 走看看