关于jsp:JSP转载3y

55次阅读

共计 43268 个字符,预计需要花费 109 分钟才能阅读完成。

什么是 JSP

JSP 全名为 Java Server Pages,java 服务器页面。JSP 是一种基于文本的程序,其特点就是 HTML 和 Java 代码独特存在

为什么须要 JSP

JSP 是为了简化 Servlet 的工作呈现的替代品,Servlet 输入 HTML 十分艰难,JSP 就是代替 Servlet 输入 HTML 的。

简略应用一下 JSP

  • 在 idea 下生成一个 JSP,咱们来看一下 JSP 长什么样子

        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <html>
        <head>
            <title> 简略应用 JSP</title>
        </head>
        <body>
        
        </body>
        </html>
  • 看起来就像一个 HTML 页面,后面也说了:JSP 的特点就是 HTML 和 Java 代码独特存在
  • 咱们向浏览器输入一句 HelloWorld,至于 <%%> 这个货色,我先不解释!

        <%@ page contentType="text/html;charset=UTF-8" language="java" %>
        <html>
        <head>
            <title> 简略应用 JSP</title>
        </head>
        <body>
        <%
            String s = "HelloWorld";
            out.println(s);
        %>
        </body>
        </html>

JSP 的工作原理

  • 在 Tomcat 博客中我提到过:Tomcat 拜访任何的资源都是在拜访 Servlet!,当然了,JSP 也不例外!JSP 自身就是一种 Servlet。为什么我说 JSP 自身就是一种 Servlet 呢?其实 JSP 在第一次被拜访的时候会被编译为 HttpJspPage 类(该类是 HttpServlet 的一个子类)
  • 方才我简略应用了一下 JSP,它被编译成了这么一个 Servlet:


package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import java.util.Date;

public final class _1_jsp extends org.apache.jasper.runtime.HttpJspBase
    implements org.apache.jasper.runtime.JspSourceDependent {private static final JspFactory _jspxFactory = JspFactory.getDefaultFactory();

  private static java.util.List<String> _jspx_dependants;

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.tomcat.InstanceManager _jsp_instancemanager;

  public java.util.List<String> getDependants() {return _jspx_dependants;}

  public void _jspInit() {_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
  }

  public void _jspDestroy() {}

  public void _jspService(final HttpServletRequest request, final HttpServletResponse response)
        throws java.io.IOException, ServletException {

    final PageContext pageContext;
    HttpSession session = null;
    final ServletContext application;
    final ServletConfig config;
    JspWriter out = null;
    final Object page = this;
    JspWriter _jspx_out = null;
    PageContext _jspx_page_context = null;


    try {response.setContentType("text/html;charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                  null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<title> 简略应用 JSP</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");

    String s = "HelloWorda";
    out.println(s);

      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>\r\n");
    } catch (Throwable t) {if (!(t instanceof SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {out.clearBuffer(); } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
      }
    } finally {_jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
}
  • 编译过程是这样子的:浏览器第一次申请 1.jsp 时,Tomcat 会将 1.jsp 转化成 1_jsp.java 这么一个类,并将该文件编译成 class 文件。编译结束后再运行 class 文件来响应浏览器的申请
  • 当前拜访 1.jsp 就不再从新编译 jsp 文件了,间接调用 class 文件来响应浏览器。当然了,如果 Tomcat 检测到 JSP 页面改变了的话,会从新编译的
  • 既然 JSP 是一个 Servlet,那 JSP 页面中的 HTML 排版标签是怎么样被发送到浏览器的 ?咱们来看下下面 1_jsp.java 的源码就晓得了。原来就是用 write() 进来的罢了。说到底,JSP 就是封装了 Servlet 的 java 程序罢了。

      out.write("\r\n");
      out.write("\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<title> 简略应用 JSP</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
  • 有人可能也会问:JSP 页面的代码服务器是怎么执行的 ?再看回 1_jsp.java 文件,java 代码就间接在类中的 service() 中。

    String s = "HelloWorda";
    out.println(s);
  • JSP 比 Servlet 更不便更简略的一个重要起因就是:内置了 9 个对象!内置对象有:out、session、response、request、config、page、application、pageContext、exception,这几个内置对象不在这里讲。当初先晓得一下即可!
    • *

JSP 生命周期

JSP 也是 Servlet,运行时只有一个实例,JSP 初始化和销毁时也会调用 Servlet 的 init()和 destroy()办法。另外,JSP 还有本人初始化和销毁的办法


  public void _jspInit() {_el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig());
  }

  public void _jspDestroy() {}

JSP 的语法

JSP 代码能够分为两局部:

  1. 模板数据:就是 HTML 代码
  2. 元素:JSP 页面中的 java 代码、JSP 指令、JSP 标签

JSP 脚本

  • JSP 的脚本就是 JSP 页面中的 java 代码,也叫做 scriptlet。JSP 的脚本必须应用 <%%> 括起来,不然会被当成是模板数据的!
  • JSP 脚本有三种形式:

    • <%%>【定义局部变量,编写语句】
    • <%!%>【定义类或办法,然而没人这样用!
    • <%=%>(也称之为 表达式输入)【输入各种类型的变量,int、double、String、Object 等】
  • 如果过多地应用 <%%> 会导致代码凌乱,JSP 还提供了一种 scriptlet 标签,应用此标签和 <%%> 有雷同的性能,只不过它更好看了一些

    <jsp:scriptlet>
    
        String s = "HelloWorld";
        out.println(s);
    
    </jsp:scriptlet>

JSP 正文

    
    <%-- 这是 JSP 正文 --%>
    <%--%>

    // 这是 java 的当行正文
    //

    
    /* 这是 java 的多行正文 */
    /**/
    

JSP 指令

JSP 指令用来申明 JSP 页面的相干属性,例如编码方式、文档类型等等

JSP 指令的语法:


    <%@指令  属性名 ="值"  %>

page 指令

  • 我在 idea 生成的 JSP 页面就有 page 指令了。

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  • page 指令常见属性:
  • language=”java”
  • extends=”package.class”
  • import=”{package.class | package.*}, …”
  • session=”true | false”
  • buffer=”none | 8kb | sizekb”
  • autoFlush=”true | false”
  • isThreadSafe=”true | false”
  • info=”text”
  • errorPage=”relative_url”
  • isErrorPage=”true | false”
  • contentType=”mimeType ;charset=characterSet ” | “text/html ; charset=ISO-8859-1”
  • pageEncoding=”characterSet | ISO-8859-1″
  • isELIgnored=”true | false”
  • 个别地,在 eclipse 或 idea 这些高级开发工具上开发,咱们只须要在 page 指令中指定 contentType=”text/html;charset=UTF-8″,就不会呈现中文乱码问题!
  • 当然了 contentType 不仅仅能够指定以 text/html 的形式显示,还能够应用其余的模式显示进去。在 conf/web.xml 文件中能够查问进去

  • 比方,我以 doc 模式显示 jsp 的数据

    <%@ page contentType="application/msword;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 简略应用 JSP</title>
    </head>
    <body>
    
        1111
    </body>
    </html>
  • 成果是这样子的:

  • 咱们上网的时候,如果咱们操作不当,或者服务器出错了,页面都是会呈现敌对提醒的 !这个也能通过 page 指令来实现 跳转到敌对提醒页面上
  • page 指令 errorPage= 和 isErrorPage 这两个属性,上面咱们来看一下怎么应用!
  • 1.jsp 呈现了谬误,通过 page 指令的 errorPage 属性跳转到 error.jsp 页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
    <html>
    <head>
        <title> 该页面出错了!</title>
    </head>
    <body>
        <%-- 模仿页面出错了!!!--%>
        <%
            int result = 2 / 0;
        %>
        你好呀
    </body>
    </html>
  • error.jsp 页面要通过 page 指令的 isErrorPage 属性设置页面就是谬误页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true"   %>
    <html>
        <head>
            <title> 敌对提醒页面 </title>
        </head>
        <body>
            服务器正忙着呢!</body>
    </html>
  • 上面是成果:

  • 当然了,仔细的敌人能够发现地址栏是没有变动的,所以属于是服务器跳转。以上的做法是单个页面设置的,如果我会有很多谬误(JSP 多的状况下,谬误就会多),单个设置太麻烦了!
  • 咱们能够在 web.xml 文件中全局设置谬误页,只有产生了 404 谬误或者空指针异样的谬误都会跳转到 error.jsp 页面上

    <error-page>
        <error-code>404</error-code>
        <location>/error.jsp</location>
    </error-page>

    <error-page>
        <exception-type>java.lang.NullPointerException</exception-type>
        <location>/error.jsp</location>
    </error-page>
  • 轻易输个资源进行,会产生发 404 谬误的,跳转到谬误页面。上面是成果:


include 指令

  • 在解说 request 对象的时候,咱们已经应用过 request.getRequestDispatcher(String url).include(request,response)来对页头和页尾面进行蕴含
  • inclue 指令也是做这样的事件,咱们来试验一下吧!
  • 这是页头

    <%@ page contentType="text/html;charset=UTF-8" language="java"   %>
    <html>
        <head>
            <title> 页头 </title>
        </head>
        <body>
        我是页头
        <br>
        <br>
        <br>
        </body>
    </html>
  • 这是页尾

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 页尾 </title>
    </head>
    <body>
    
    我是页尾
    
    </body>
    </html>
  • 在 1.jsp 中把页头和页尾蕴含进来


    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 蕴含页头和页尾进来 </title>
    </head>
    <body>
    
    
    <%@include file="head.jsp" %>
    <%@include file="foot.jsp" %>
    </body>
    </html>
  • 拜访 1.jsp

  • include 指令是动态蕴含 。动态蕴含的意思就是: 把文件的代码内容都蕴含进来,再编译!,看一下 jsp 的源代码就晓得了!

  • jsp 还提供另一种蕴含文件的形式:JSP 行为 — 动静蕴含。jsp 行为在上面会讲到!
    • *

taglib 指令

  • JSP 反对标签技术,要应用标签技术就先得申明标签库和标签前缀。taglib 指令就是用来指明 JSP 页面内应用标签库技术。
  • 这里就不具体阐明了,等到学习 JSP 标签的时候再应用吧!当初记住有这个指令即可。
    • *

JSP 行为

JSP 行为(JSP Actions)是一组 JSP 内置的标签,只书写大量的标记代码就可能应用 JSP 提供丰盛的性能,JSP 行为是对罕用的 JSP 性能的形象和封装

为什么我不把它间接称为 JSP 标签呢?我把这些 JSP 内置的标签称之为 JSP 行为,可能和 JSTL 标签辨别开来。当然了,你也能够把它称之为 JSP 标签,你不要搞混就行了。我集体喜爱把这些 JSP 内置标签称之为 JSP 行为。

include 行为

  • 下面曾经提及到了,include 指令是动态蕴含,include 行为是动静蕴含 其实 include 行为就是封装了 request.getRequestDispatcher(String url).include(request,response)
  • include 行为语法是这个样子的

    <jsp:include page=""/>
  • 咱们先来应用一下把,在 1.jsp 页面中也将页头和页尾蕴含进来

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 蕴含页头和页尾进来 </title>
    </head>
    <body>
        <jsp:include page="head.jsp"/>
        <jsp:include page="foot.jsp"/>
    </body>
    </html>
  • 拜访 1.jsp 页面看一下成果:

  • 应用 jsp 行为来蕴含文件,jsp 源文件是这样子的:

  • jsp 行为蕴含文件就是 先编译被蕴含的页面,再将页面的后果写入到蕴含的页面中(1.jsp)
  • 当然了,当初有动态蕴含和动静蕴含,应用哪一个更好呢?答案是:动静蕴含
  • 动静蕴含能够 向被蕴含的页面传递参数(用途不大),并且是 别离解决蕴含页面的(将被蕴含页面编译后得出的后果再写进蕴含页面)【如果有雷同名称的参数,应用动态蕴含就会报错!】
  • 模仿一下场景吧,当初我的头页面有个名为 s 的字符串变量

    <%@ page contentType="text/html;charset=UTF-8" language="java"   %>
    <html>
        <head>
            <title> 页头 </title>
        </head>
        <body>
        
        <%
            String s = "zhongfucheng";
        %>
        我是页头呀
        <br>
        <br>
        <br>
        </body>
    </html>
  • 我的页尾也有个名为 s 的字符串变量

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 页尾 </title>
    </head>
    <body>
    <%
        String s = "zhongfucheng";
    %>
    
    我是页尾呀
    
    </body>
    </html>
  • 当初我应用动态蕴含看看会产生什么,出现异常了。

  • 出现异常的起因很简略,就是 同一个文件中有两个雷同的变量 s

  • 应用动静蕴含就能够防止这种状况


param 行为

  • 当应用 <jsp:include> 和 <jsp:forward> 行为引入或将申请转发给其它资源时,能够应用 <jsp:param> 行为向这个资源传递参数。

forward 行为

  • 在解说 request 对象的时候,咱们应用 request.getRequestDispatcher(String url).forward(request,response)进行跳转。其实 forward 行为就是对其封装
  • 咱们来看一下 forward 的语法:
    <jsp:forward page=""/>
  • 好的,咱们来应用一下吧。拜访 1.jsp 页面就跳转到 head.jsp 页面中

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 拜访 1.jsp 就跳转到 head.jsp</title>
    </head>
    <body>
    
    <jsp:forward page="head.jsp"/>
    
    
    </body>
    </html>
  • 看一下成果

  • 如果我要传递参数,就要在 forward 行为嵌套 param 行为
  • 在跳转到 head.jsp 时传入参数 username 值为 zhongfucheng
    <jsp:forward page="head.jsp">
        <jsp:param name="username" value="zhongfucheng"/>
    </jsp:forward>
  • 在 head.jsp 页面中获取到传递过去的参数

        <%
            String ss = request.getParameter("username");
        %>
    
        获取到的参数是:<%=ss%>
  • 成果如下图所示

directive 行为

  • directive 的中文意思就是指令 该行为就是代替指令 <%@%> 的语法的

    • <jsp:directive.include file=””/> 相当于 <%@include file=”” %>
    • <jsp:directive.page/> 相当于 <%@page %>
    • <jsp:directive.taglib/> 相当于 <%@taglib %>
  • 咱们来试一下能不能用的

    <jsp:directive.include file="head.jsp"></jsp:directive.include>
    <jsp:directive.include file="foot.jsp"></jsp:directive.include>
  • 看下成果,失常能够蕴含页面:

  • 应用该指令能够让 JSP 页面更加好看
  • 应用 scriptlet 行为 <jsp:scriptlet> 代替 <%%> 是同样一个情理

javaBean 行为

  • JSP 还提供了操作 javaBean 对象的行为 在这里就不具体阐明了,前面会讲到的!当初记住 JSP 提供了 javaBean 行为来操作简略类即可!

    • <jsp:useBean id=""/>
    • <jsp:setProperty name=""property=""/>
    • <jsp:getProperty name=""property=""/>
    • 什么是 JSP 内置对象

==========

JSP 引擎在调用 JSP 对应的 jspServlet 时,会传递或创立 9 个与 web 开发相干的对象供 jspServlet 应用 。JSP 技术的设计者为便于开发人员在编写 JSP 页面时取得这些 web 对象的援用,特意定义了 9 个相应的变量, 开发人员在 JSP 页面中通过这些变量就能够疾速取得这 9 大对象的援用

仔细的敌人会发现,咱们没有在 JSP 页面上定义过 out 对象,却能够间接应用!其实 out 对象就是 JSP 内置对象之一

九个内置对象:

  • pageContext
  • page
  • config
  • request
  • response
  • session
  • application
  • exception
  • out
    • *

out 对象

out 对象的 API

  • int getBufferSize()【失去缓存大小】
  • int getRemaining()【失去未应用缓存的大小】
  • boolean isAutoFlush()
  • void println()
  • void flush()
  • void close()
  • void clearBuffer()
  • void clear()
  • out 对象用于向浏览器输入数据,与之对应的是 Servlet 的 PrintWriter 对象。然而这个 out 对象的类型并不是 PrintWriter,是 JspWriter

  • 咱们能够简略了解为:JspWriter 就是带缓存的 PrintWrieter
  • out 对象的原理如下:

  • 只有向 out 对象中写入了内容,且 满足如下任何一个条件时,out 对象才去调用 ServletResponse.getWriter 办法 ,并 通过该办法返回的 PrintWriter 对象将 out 对象的缓冲区中的内容真正写入到 Servlet 引擎提供的缓冲区中

    • 设置 page 指令的 buffer 属性敞开了 out 对象的缓存性能
    • out 对象的缓冲区已满
    • 整个 JSP 页面完结
  • 个别咱们在 JSP 页面输入都是用表达式(<%=%>),所以 out 对象用得并不是很多
    • *

request

  • 内置对象 request 其实就是 HttpServletRequest,在 Servlet 解说的时候曾经具体阐明了,没什么好说的

response

  • 内置对象 response 其实就是 HttpServletResponse,在 Servlet 解说的时候曾经具体阐明了,没什么好说的

config

  • 内置对象 config 其实就是 ServletConfig,在 Servlet 解说的时候曾经具体阐明了,没什么好说的

session

  • 内置对象 session 其实就是 HttpSession。,在 Servlet 解说的时候曾经具体阐明了,没什么好说的

留神:在 page 指令配置如下信息,session 将不可应用


    <%@page session="false" %>

application

  • 内置对象 application 其实就是 ServletContext 对象,在 Servlet 解说的时候曾经具体阐明了,没什么好说的

page

  • 内置对象 page 是 HttpJasPage 对象,其实 page 对象代表的就是以后 JSP 页面,是以后 JSP 编译后的 Servlet 类的对象。也就是说:page 对象相当于一般 java 类的 this

exception

  • 内置对象 exception 是 java.lang.Exception 类的对象,exception 封装了 JSP 页面抛出的异样信息。exception 常常被用来处理错误页面
  • 后面咱们已 经讲过了怎么设置谬误页面了,上面咱们就来简略应用一下 exception 对象吧
  • 1.jsp 页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="error.jsp" %>
    
    <html>
    <head>
        <title></title>
    </head>
    <body>
    
    <%-- 模仿空指针异样的谬误 --%>
    <%
    
        String sss = null;
        sss.length();
    %>
    
    </body>
    </html>
  • error.jsp 页面


    <%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
    
    <html>
    <head>
        <title> 谬误页面 </title>
    </head>
    <body>
    
    <%
        out.println("程序抛出了异样:" + exception);
    %>
    
    </body>
    </html>
  • 成果:

pageContext

pageContext 是内置对象中最重要的一个对象,它代表着 JSP 页面编译后的内容(也就是 JSP 页面的运行环境)!

pageContext 获取 8 个内置对象

  • 既然它代表了 JSP 页面编译后的内容,天经地义的:它封装了对其余 8 大内置对象的援用!,也就是说,通过 pageContext 能够获取到其余的 8 个内置对象!

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 获取八大内置对象 </title>
    </head>
    <body>
    <%
    
        System.out.println(pageContext.getSession());
        System.out.println(pageContext.getRequest());
        System.out.println(pageContext.getResponse());
    
        System.out.println(pageContext.getException());
    
        System.out.println(pageContext.getPage());
        System.out.println(pageContext.getServletConfig());
        System.out.println(pageContext.getServletContext());
        System.out.println(pageContext.getOut());
    
    %>
    
    </body>
    </html>
  • 看下成果:

pageContext 作为域对象

  • 相似于 request,session,ServletContext 作为域对象而言 都有以下三个办法

    • setAttribute(String name,Objcet o)
    • getAttribute(String name)
    • removeAttribute(String name)
  • 当然了,pageContext 也不例外,pageContext 也有这三个办法
  • pageContext 实质上代表的是以后 JSP 页面编译后的内容,作为域对象而言,它就代表着以后 JSP 页面(也就是 page)!也就是说:pageContext 域对象只在 page 范畴内无效,超出了 page 范畴就有效了
  • 首先来看看在page 范畴内能不能应用

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 应用 page 域对象 </title>
    </head>
    <body>
    <%
        pageContext.setAttribute("name", "zhongfucheng");
    %>
    <%
        String value = (String) pageContext.getAttribute("name");
        System.out.println(value);
    %>
    
    </body>
    </html>
  • 成果如下:

  • 咱们当初来试验一下 是不是超出了 page 范畴就有效了!
  • 在 2.jsp 中 request 域对象设置属性

    
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>request 域对象设置属性 </title>
    </head>
    <body>
    <%
        // 这是 request 域对象保留的内容
        request.setAttribute("name","zhongfucheng");
    %>

    <%-- 跳转到 1.jsp 中 --%>

    <jsp:forward page="1.jsp"/>
    
    </body>
    </html>
  • 希图在 1.jsp 中 pageContext 取出 request 存进去的属性

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 在 page 域对象获取属性 </title>
    </head>
    <body>
    
    <%
        // 希图获取 request 域对象存进的属性
        String value = (String) pageContext.getAttribute("name");
        System.out.println(value);
    %>
    
    </body>
    </html>
  • 成果如下:


  • pageContext 实质上代表着编译后 JSP 的内容,pageContext 还能够封装了拜访其余域的办法
  • 下面的 pageContext 默认是 page 范畴的 但 pageContext 对象重载了 set、get、removeAttribute 这三个办法

    • getAttribute(String name,int scope)
    • setAttribute(String name,Object value,int scope)
    • removeAttribute(String name,int scope)
  • 多了一个设置域范畴的一个参数,如果不指定默认就是 page。当然了,pageContext 把 request、session、application、page 这几个域对象封装着了动态变量供咱们应用

    • PageContext.APPLICATION_SCOPE
    • PageContext.SESSION_SCOPE
    • PageContext.REQUEST_SCOPE
    • PageContext.PAGE_SCOPE
  • 方才咱们没有应用重载办法的时候,应用 pageContext 是无奈获取到 request 域对象设置的属性的。当初咱们应用重载后的办法看一下能不能获取失去

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 在 page 域对象获取 request 域对象的属性 </title>
    </head>
    <body>
    
    <%
        // 应用重载的办法获取 request 域对象的属性
        String value = (String) pageContext.getAttribute("name",pageContext.REQUEST_SCOPE);
        System.out.println(value);
    %>
    
    </body>
    </html>
  • 成果:


  • pageContexst 还有这么一个办法:

    • findAttribute(String name)
  • 该办法会查找各个域的属性,从小到大开始寻找!也就是 page—>request->session->application。这个是 EL 表达式的原理!,EL 表达式前面会讲到!
  • 咱们用此办法看能不能查找出 request 域对象的属性吧!

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title> 应用 findAttribute</title>
    </head>
    <body>
    
    <%
    
        // 应用 findAttribute 查找 2.jsp 中 request 域对象的属性
        String value = (String) pageContext.findAttribute("name");
        System.out.println(value);
    %>
    
    </body>
    </html>
  • 成果如下:


引入和跳转

PageContext 类中定义了一个forward 办法和两个 include 办法来别离简化和代替 RequestDispatcher.forward 办法和 include 办法

  • pageContext.forward(String url)
  • pageContext.include(String url)


4 种属性范畴

到目前为止,咱们曾经学了 4 种属性范畴了。

  1. page【只在一个页面中保留属性,跳转页面有效】
  2. requet【只在一次申请中保留属性,服务器跳转无效,浏览器跳转有效】
  3. session【在一个会话范畴中保留属性,无论何种跳转均无效,敞开浏览器后有效】
  4. application【在整个服务器中保留,所有用户都能够应用】
    • *
  • 4 个内置对象都反对以下的办法:
  1. setAttribute(String name, Object o)
  2. getAttribute(String name)
  3. removeAttribute(String name)

利用场景

  1. request:如果客户向服务器发申请,产生的数据,用户看完就没用了,像这样的数据就存在 request 域, 像新闻数据,属于用户看完就没用的
  2. session:如果客户向服务器发申请,产生的数据,用户用完了等一会儿还有用,像这样的数据就存在 session 域中,像购物数据,用户须要看到本人购物信息,并且等一会儿,还要用这个购物数据结帐
  3. servletContext:如果客户向服务器发申请,产生的数据,用户用完了,还要给其它用户用,像这样的数据就存在 servletContext 域中,像聊天数据

什么是 javaBean

  • JavaBean 就是一个一般的 java 类 ,也称之为简略 java 对象 –POJO(Plain Ordinary Java Object), 是 Java 程序设计中一种设计模式,是一种基于 Java 平台的软件组件思维
  • JavaBean 遵循着特定的写法,通常有以下的规定:

    • 有无参的构造函数
    • 成员属性私有化
    • 封装的属性如果须要被外所操作,必须编写 public 类型的 setter、getter 办法
  • 下面的文字看起来如同很高大上,javaBean 其实非常简单,上面的代码就是依照特定写法、规定编写的一个 JavaBean 对象


    public class Person {
        private String username ;
        private int age;
    
        public Person() {}
    
    
        public String getUsername() {return username;}
    
        public void setUsername(String username) {this.username = username;}
    
        public int getAge() {return age;}
    
        public void setAge(int age) {this.age = age;}
    }

为什么须要应用 Javabean

  • 应用 javaBean 的益处就是:封装,重用, 可读
  • 上面援用知乎一段答复:

JaveBean 你能够了解为一辆货车,在你的 java 端和 web 页面进行数据传递的载体,你当然能够每个变量独自传递,或者应用汇合传递,然而javabean 能够使你的数据更有可读性,不便开发时明确变量的意义,也使其余浏览你代码的人能间接你的用意

如果把 bean 类与数据库联结应用,一张表应用 bean 类,能够使你的代码更加简洁高效,易于了解,当初大多数框架都会应用这种机制。


JSP 行为 –JavaBean

  • JSP 技术提供了 三个对于 JavaBean 组件的动作元素,即 JSP 行为(标签),它们别离为:

    • <jsp:useBean>【在 JSP 页面中查找 javaBean 对象或者实例化 javaBean 对象】
    • <jsp:setProperty>【设置 javaBean 的属性】
    • <jsp:getProperty>【获取 javaBean 的属性】

jsp:useBean

  • <jsp:useBean>标签用于 在指定的域范畴内查找指定名称的 JavaBean 对象

    • 存在则间接返回该 JavaBean 对象的援用
    • 不存在则实例化一个新的 JavaBean 对象并将它以指定的名称存储到指定的域范畴中
  • 语法:

    <jsp:useBean id="实例化对象的名称" class="类的全名" scope="保留范畴"/>
  • 如果 JSP 不反对 <jsp:useBean> 这个行为,咱们要应用 Person 类是这样应用的

    <%-- 这里须要导入 Person 类 --%>
    <%@ page import="domain.Person" %>

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title></title>
    </head>
    <body>
    
    
    <%
        //new 出对象
        Person person = new Person();

        person.setName("zhongfucheng");
        System.out.println(person.getName());
    %>
    
    </body>
    </html>
  • 成果如下

  • 咱们应用 <jsp:useBean> 就显得十分简洁,不必导包,不必 new 出对象

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title></title>
    </head>
    <body>
    
    <jsp:useBean id="person" class="domain.Person" scope="page"/>
    <%
        person.setName("zhongfucheng");
        System.out.println(person.getName());
    %>
    </body>
    </html>
  • 也能够实现同样的成果:

  • 有人可能会想,凭什么写一个 <jsp:useBean> 这样的代码就能够创立出一个对象进去
  • 当初我把 JavaBean 中 无参的构造函数改成有参的 ,咱们看看会呈现什么状况, 出现异常了!

     public Person(int age) {this.age = age;}

  • <jsp:useBean id="person" class="domain.Person" scope="page"/>外部原理是这样子的:

  • 通过下面的代码咱们也 晓得了为什么要有一个无参的构造函数 外部在 new 对象的时候是没有传递参数进去的!

jsp:setProperty

  • 语法:

    <jsp:setProerty name="对象名称" property="属性名" param="参数名" value="值">
  • 在语法上可分为 4 种模式

    • <jsp:setProperty name=” 对象名称 ” property=”*”/> 主动匹配
    • <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ”/> 指定属性
    • <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ” param=” 参数名称 ”/> 指定参数【很少用】
    • <jsp:setProperty name=” 对象名称 ” property=” 属性名称 ” value=” 内容 ”/> 指定内容【很少用】
  • 咱们没有学习到 <jsp:setProperty>,咱们获 取表单的信息,而后导入到 javaBean 对象中是这样的一种状况
  • 这是 表单的页面代码

    <form action="/zhongfucheng/1.jsp" method="post">
        用户名:<input type="text" name="username">
        年龄:<input type="text" name="age">
        <input type="submit" value="提交">
    </form>
  • 这是 解决表单提交过去数据的 jsp 的代码


    <jsp:useBean id="person" class="domain.Person" scope="page"/>
    <%
        int age = Integer.parseInt(request.getParameter("age"));
        
        person.setAge(age);
    
        System.out.println(person.getAge());
        
    %>
  • 这是能够实现的,然而相对来说,比拟麻烦!

  • 咱们来 应用 <jsp:setProperty> 了来看看:

    <jsp:useBean id="person" class="domain.Person" scope="page"/>

    <%-- 指定属性名称为 age--%>
    <jsp:setProperty name="person" property="age"/>
    <%
        System.out.println(person.getAge());
    %>
  • 也能够实现,并且 代码更少,性能更弱小

  • 代码更少能够直观看进去,为什么我说它性能更加弱小呢?表单提交过去的数据都是字符串,在咱们没有用 <jsp:setProperty> 前,咱们存储设置 int 类型或其余非字符串类型的数据是须要强转的 !然而 <jsp:setProperty> 不须要咱们强转,它 外部主动帮咱们转换了
  • 咱们再来 应用一下主动匹配来感触它的弱小之处吧

    <jsp:useBean id="person" class="domain.Person" scope="page"/>

    <%--property 的值设置为 * 就代表主动匹配 --%>
    <jsp:setProperty name="person" property="*"/>
    <%
        System.out.println(person.getAge());
        System.out.println(person.getName());
    %>
  • 咱们再来看一下成果:

  • 看到这里,有人可能会感觉好神奇:只有设置 property 的值就能够将表单传递过去的数据封装到 JavaBean 对象中了!这到底是这样做到的???
  • 仔细的敌人会发现,JavaBean 的属性名称和表单的 name 属性设置的名称是截然不同的

        private String username ;
        private int age;

        用户名:<input type="text" name="username">
        年龄:<input type="text" name="age">
  • 如果我设置不一样还能不能用?咱们试试:表单 name 属性的 username 改成是 user

     用户名:<input type="text" name="user">
  • 咱们再来 看看还能不能把表单的数据残缺地封装 JavaBean 对象中

  • 咱们能够发现:要想可能把表单带过去的数据胜利封装到 JavaBean 对象上,名字要统一!也就是说:JavaBean 属性名要和表单的 name 的名称统一
  • 至于原理,它是通过反射来做的, 调用了内省的办法!,咱们看编译后的 JSP 就明确了。


jsp:getProperty

  • 语法:

    • <jsp:getProperty name=” 对象名 ” property=” 属性名 ”/>
  • 该 jsp 行为非常简略,咱们来应用一下就晓得了。

    <%-- 应用 <jsp:getProperty> 输入 --%>
    <jsp:getProperty name="person" property="username"/>
    <jsp:getProperty name="person" property="age"/>
  • 成果:

  • 原理如下

什么是 EL 表达式?

表达式语言(Expression Language,EL),EL 表达式是用 ”${}” 括起来的脚本,用来更不便的读取对象!

  • EL 表达式次要用来读取数据,进行内容的显示!

为什么要应用 EL 表达式?

  • 为什么要应用 EL 表达式,咱们先来看一下 没有 EL 表达式是怎么样读取对象数据的吧
  • 在 1.jsp 中设置了 Session 属性

<%@ page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<html>
<head>
    <title> 向 session 设置一个属性 </title>
</head>
<body>

<%
    // 向 session 设置一个属性
    session.setAttribute("name", "aaa");
    System.out.println("向 session 设置了一个属性");
%>

</body>
</html>
  • 在 2.jsp 中获取 Session 设置的属性

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
</head>
<body>

<%
        String value = (String) session.getAttribute("name");
        out.write(value);
%>
</body>
</html>
  • 成果:

  • 下面看起来,也没有多简单呀,那咱们试试 EL 表达式的!
  • 在 2.jsp 中读取 Session 设置的属性

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title></title>
</head>
<body>

${name}

</body>
</html>
  • 只用了简简单单的几个字母就能输入 Session 设置的属性了!并且输入在浏览器上!

  • 应用 EL 表达式 能够不便地读取对象中的属性、提交的参数、JavaBean、甚至汇合
    • *

EL 表达式的作用

  • 首先来 看一下 EL 表达式的语法吧

${标识符}
  • EL 表达式如果找不到相应的对象属性,返回的的空白字符串“”,而不是 null,这是 EL 表达式最大的特点!

获取各类数据

获取域对象的数据

  • 下面在例子中,咱们曾经体验到了 获取 Session 域对象的数据是如许中央便!其实 EL 表达式能够让咱们获取各个域范畴的数据
  • 在 1.jsp 中设置 ServeltContext 属性(也就是 application)

<%
    // 向 ServletContext 设置一个属性
    application.setAttribute("name", "aaa");
    System.out.println("向 application 设置了一个属性");
%>
  • 在 2.jsp 中获取 application 的属性


<%
    ${name}

%>
  • 和 Session 一样,也能获取失去!

  • 之前咱们来讲 ServletContext 对象的时候讲过一个办法 findAttribute(String name),EL 表达式语句在执行的时候会调用该办法,用标识符作为关键字别离从 page、request、session、application 四个域中查找相应的对象。这也解释了为什么 EL 表达式能够仅仅通过标识符就可能获取到存进域对象的数据!
  • findAttribute()的查找程序:从小到大,也就是 page->request->session->application

获取 JavaBean 的属性

  • 以前在 JSP 页面获取 JavaBean 的数据是这样子的
  • 1.jsp 页面 Session 存进一个 Person 对象,设置 age 的属性为 22

    <jsp:useBean id="person" class="domain.Person" scope="session"/>
    <jsp:setProperty name="person" property="age" value="22"/>

在 2.jsp 中取出 Session 的属性


<%

    Person person = (Person) session.getAttribute("person");

    System.out.println(person.getAge());
%>
  • 成果如下

  • 当初我 应用了 EL 表达式读取数据又会十分不便了

    // 等同于 person.getAge()
    ${person.age}

  • 下面的代码 等同于调用对象的 getter 办法,外部是通过反射机制实现的

获取汇合的数据

  • 汇合操作在开发中被宽泛地采纳,在 EL 表达式中也很好地反对了汇合的操作!能够 十分不便地读取 Collection 和 Map 汇合的内容
  • 为了更好地看出 EL 表达式的弱小之处,咱们也来比照一下应用 EL 表达式和不应用 EL 表达式的区别
  • 上面 不应用 EL 表达式输入汇合的元素
  • 在 1.jsp 页面中设置 session 的属性,session 属性的值是 List 汇合,List 汇合装载的又是 Person 对象

    <%
        List<Person> list = new ArrayList();
    
        Person person1 = new Person();
        person1.setUsername("zhongfucheng");
    
        Person person2 = new Person();
        person2.setUsername("ouzicheng");
    
        list.add(person1);
        list.add(person2);
    
        session.setAttribute("list",list);
    %>
  • 在 2.jsp 中获取到 session 的属性,并输入到页面上
    <%
    
        List<Person> list = (List) session.getAttribute("list");
    
        out.write(list.get(0).getUsername()+"<br>");
    
        out.write(list.get(1).getUsername());
    
    %>

应用 EL 表达式又是怎么样的成果呢?咱们来看看

<%-- 取出 list 汇合的第 1 个元素(下标从 0 开始),获取 username 属性 --%>
${list[0].username}
<br>
<%-- 取出 list 汇合的第 2 个元素,获取 username 属性 --%>
${list[1].username}
  • 同样也能够有雷同的成果:

  • 咱们再来 应用一下 Map 汇合
  • 在 1.jsp 中 session 属性存储了 Map 汇合,Map 汇合的关键字是字符串,值是 Person 对象

<%

    Map<String, Person> map = new HashMap<>();

    Person person1 = new Person();
    person1.setUsername("zhongfucheng1");

    Person person2 = new Person();
    person2.setUsername("ouzicheng1");

    map.put("aa",person1);
    map.put("bb",person2);

    session.setAttribute("map",map);
%>
  • 看起来如同取出数据的时候是会有点简单,然而有了 EL 表达式也是十分轻松的

${map.aa.username}
<br>
${map.bb.username}
  • 成果:

  • 如果Map 汇合存储的关键字是一个数字,就不能应用 ”.” 号运算符了,如下所示

  • 对于这种状况,咱们能够 应用 ”[]” 的模式读取 Map 汇合的数据

${map["1"].username}
<br>
${map["2"].username}
  • EL 表达式配合 JSTL 标签能够很不便的迭代汇合,前面讲到 JSTL 标签的时候会用到!这里就不具体阐明了。
    • *

EL 运算符

  • EL 表达式反对简略的运算符:加减乘除取摸,逻辑运算符empty 运算符(判断是否为 null),三目运算符

  • empty 运算符能够判断对象是否为 null,用作于流程管制!
  • 三目运算符简化了 if 和 else 语句,简化代码书写

<%
    List<Person> list = null;
%>

${list==null?"list 汇合为空":"list 汇合不为空"}
  • 成果:


EL 表达式 11 个内置对象

EL 表达式次要是来对内容的显示,为了显示的不便,EL 表达式提供了 11 个内置对象

  1. pageContext 对应于 JSP 页面中的 pageContext 对象(留神:取的是 pageContext 对象)
  2. pageScope 代表 page 域中用于保留属性的 Map 对象
  3. requestScope 代表 request 域中用于保留属性的 Map 对象
  4. sessionScope 代表 session 域中用于保留属性的 Map 对象
  5. applicationScope 代表 application 域中用于保留属性的 Map 对象
  6. param 示意一个保留了所有申请参数的 Map 对象
  7. paramValues 示意一个保留了所有申请参数的 Map 对象,它对于某个申请参数,返回的是一个 string[]
  8. header 示意一个保留了所有 http 申请头字段的 Map 对象
  9. headerValues 同上,返回 string[]数组。
  10. cookie 示意一个保留了所有 cookie 的 Map 对象
  11. initParam 示意一个保留了所有 web 利用初始化参数的 map 对象
  • 上面 测试各个内置对象

<%--pageContext 内置对象 --%>
<%
    pageContext.setAttribute("pageContext1", "pageContext");
%>
pageContext 内置对象:${pageContext.getAttribute("pageContext1")}
<br>

<%--pageScope 内置对象 --%>
<%
    pageContext.setAttribute("pageScope1","pageScope");
%>
pageScope 内置对象:${pageScope.pageScope1}
<br>

<%--requestScope 内置对象 --%>
<%
    request.setAttribute("request1","reqeust");
%>
requestScope 内置对象:${requestScope.request1}
<br>

<%--sessionScope 内置对象 --%>
<%
    session.setAttribute("session1", "session");
%>
sessionScope 内置对象:${sessionScope.session1}
<br>

<%--applicationScope 内置对象 --%>
<%
    application.setAttribute("application1","application");
%>
applicationScopt 内置对象:${applicationScope.application1}
<br>

<%--header 内置对象 --%>
header 内置对象:${header.Host}
<br>

<%--headerValues 内置对象, 取出第一个 Cookie--%>
headerValues 内置对象:${headerValues.Cookie[0]}
<br>


<%--Cookie 内置对象 --%>
<%
    Cookie cookie = new Cookie("Cookie1", "cookie");
%>
Cookie 内置对象:${cookie.JSESSIONID.value}
<br>

<%--initParam 内置对象,须要为该 Context 配置参数能力看出成果【jsp 配置的有效!亲测】--%>

initParam 内置对象:${initParam.name}

<br>
  • 效果图:

注意事项:

  • 测试 headerValues 时,如果头外面有“-”,例 Accept-Encoding,则要 headerValues[“Accept-Encoding”]
  • 测试 cookie 时,例 ${cookie.key} 取的是 cookie 对象,如拜访 cookie 的名称和值,须 ${cookie.key.name}${cookie.key.value}
  • 测试 initParam 时,初始化参数要的 web.xml 中的配置 Context 的,仅仅是 jsp 的参数是获取不到的
  • 下面曾经测过了 9 个内置对象了,至于 param 和 parmaValues 内置对象个别都是别的页面带数据过去的(表单、地址栏)
  • 表单页面

<form action="/zhongfucheng/1.jsp" method="post">
用户名:<input type="text" name="username"><br>
年龄:<input type="text" name="age"><br>
喜好:<input type="checkbox" name="hobbies" value="football"> 足球
<input type="checkbox" name="hobbies" value="basketball"> 篮球
<input type="checkbox" name="hobbies" value="table tennis"> 兵乓球 <br>
<input type="submit" value="提交"><br>
</form>
  • 解决表单页面:


${param.username}
<br>

${param.age}
<br>

// 没有学习 jstl 之前就一个一个写吧。${paramValues.hobbies[0]}
<br>

${paramValues.hobbies[1]}
<br>

${paramValues.hobbies[2]}
<br>
  • 成果:

  • 当然了,应用地址栏形式提交数据给解决页面也是用 param 内置对象去获取数据的


EL 表达式回显数据

EL 表达式最大的特点就是:如果获取到的数据为 null,输入空白字符串 ””!这个特点能够让咱们数据回显

  • 在 1.jsp 中模仿场景

<%-- 模仿数据回显场景 --%>
<%
    User user = new User();
    user.setGender("male");

    // 数据回显
    request.setAttribute("user",user);
%>


<input type="radio" name="gender" value="male" ${user.gender=='male'?'checked':''}> 男
<input type="radio" name="gender" value="female" ${user.gender=='female'?'checked':''}> 女
  • 成果:


EL 自定义函数

EL 自定义函数用于扩大 EL 表达式的性能,能够让 EL 表达式实现一般 Java 程序代码所能实现的性能

  • 开发 HTML 本义的 EL 函数
  • 咱们 有时候想在 JSP 页面中输入 JSP 代码,然而 JSP 引擎会主动把 HTML 代码解析,输入给浏览器。此时咱们就要对 HTML 代码本义

步骤:

  • 编写一个蕴含静态方法的类 EL 表达式只能调用静态方法),该办法很罕用,Tomcat 都有此办法, 可在 webappsexamplesWEB-INFclassesutil 中找到

public static 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("&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());

}
  • 在 WEB/INF 下创立 tld(taglib description)文件,在 tld 文件中形容自定义函数

<?xml version="1.0" encoding="ISO-8859-1"?>

<taglib 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 http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
        version="2.1">

    <tlib-version>1.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>/zhongfucheng</uri>

    <!-- 函数的形容 -->
    <function>

        <!-- 函数的名字 -->
        <name>filter</name>

        <!-- 函数地位 -->
        <function-class>utils.HTMLFilter</function-class>

        <!-- 函数的办法申明 -->
        <function-signature>java.lang.String filter(java.lang.String)</function-signature>
    </function>

</taglib>
  • 在 JSP 页面中导入和应用自定义函数,EL 自定义的函数个别前缀为 ”fn”,uri 是 ”/WEB-INF/tld 文件名称 ”

<%@ page language="java" contentType="text/html" pageEncoding="UTF-8" %>
<%@taglib prefix="fn" uri="/WEB-INF/zhongfucheng.tld" %>


<html>
<head>
    <title></title>
</head>
<body>

// 实现了 HTML 本义的性能
${fn:filter("<a href='#'> 点我 </a>")}


</body>
</html>
  • 成果:


EL 函数库(fn 办法库)

  • 因为 在 JSP 页面中显示数据时,常常须要对显示的字符串进行解决,SUN 公司针对于一些常见解决定义了一套 EL 函数库供开发者应用
  • 其实 EL 函数库就是 fn 办法库,是 JSTL 标签库中的一个库,也有人称之为 fn 标签库,然而该库 长得不像是标签,所以称之为 fn 办法库
  • 既然作为 JSTL 标签库中的一个库,要应用 fn 办法库就须要导入 JSTL 标签 要想应用 JSTL 标签库就要导入 jstl.jar 和 standard.jar 包!
  • 所以,要对 fn 办法库做测试,首先 导入开发包(jstl.jar、standard.jar)

  • 在 JSP 页面中指明应用标签库

<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
  • fn 办法库全都是跟字符串无关的(能够把它想成是 String 的办法)
  • fn:toLowerCase
  • fn:toUpperCase
  • fn:trim
  • fn:length
  • fn:split
  • fn:join【接管字符数组,拼接字符串】
  • fn:indexOf
  • fn:contains
  • fn:startsWith
  • fn:replace
  • fn:substring
  • fn:substringAfter
  • fn:endsWith
  • fn:escapeXml【疏忽 XML 标记字符】
  • fn:substringBefore
  • 测试代码:
contains:${fn:contains("zhongfucheng",zhong)}<br>

containsIgnoreCase:${fn:containsIgnoreCase("zhongfucheng",ZHONG)}<br>

endsWith:${fn:endsWith("zhongfucheng","eng")}<br>

escapeXml:${fn:escapeXml("<zhongfucheng> 你是谁呀 </zhongfucheng>")}<br>

indexOf:${fn:indexOf("zhongfucheng","g")}<br>

length:${fn:length("zhongfucheng")}<br>

replace:${fn:replace("zhongfucheng","zhong" ,"ou")}<br>

split:${fn:split("zhong,fu,cheng",",")}<br>

startsWith:${fn:startsWith("zhongfucheng","zho")}<br>

substring:${fn:substring("zhongfucheng","2" , fn:length("zhongfucheng"))}<br>

substringAfter:${fn:substringAfter("zhongfucheng","zhong")}<br>

substringBefore:${fn:substringBefore("zhongfucheng","fu")}<br>

toLowerCase:${fn:toLowerCase("zhonGFUcheng")}<br>

toUpperCase:${fn:toUpperCase("zhongFUcheng")}<br>

trim:${fn:trim("zhong    fucheng")}<br>

<%-- 将宰割成的字符数组用 "." 拼接成一个字符串 --%>
join:${fn:join(fn:split("zhong,fu,cheng",","),"." )}<br>
  • 成果:

  • 应用 fn 办法库数据回显

<%
    User user = new User();
    String likes[] = {"sing"};
    user.setLikes(likes);

    // 数据回显
    request.setAttribute("user",user);
%>


<%--java 的字符数组以 "," 号宰割开,首先拼接成一个字符串,再判读该字符串有没有蕴含关键字,如果有就 checked--%>
<input type="checkbox"${fn:contains(fn:join(user.likes,","),"sing")?'checked':'' }> 唱歌
<input type="checkbox"${fn:contains(fn:join(user.likes,","),"dance")?'checked':'' }> 跳舞
  • 成果:

什么是 JSTL

JSTL 全称为 JSP Standard Tag Library 即 JSP 规范标签库

JSTL 作为最根本的标签库,提供了一系列的 JSP 标签,实现了根本的性能:汇合的遍历、数据的输入、字符串的解决、数据的格式化等等!

为什么要应用 JSTL

  • EL 表达式不够完满,须要 JSTL 的反对 !在 JSP 中,咱们后面曾经用到了 EL 表达式,领会到了 EL 表达式的弱小性能: 应用 EL 表达式能够很不便地援用一些 JavaBean 以及其属性,不会抛出 NullPointerException 之类的谬误!然而,EL 表达式十分无限,它不能遍历汇合,做逻辑的管制。这时,就须要 JSTL 的反对了
  • Scriptlet 的可读性,维护性,重用性都非常差!JSTL 与 HTML 代码非常相似,遵循着 XML 标签语法,应用 JSTL 让 JSP 页面显得整洁,可读性十分好,重用性十分高,能够实现简单的性能!
  • 在 JSP 中不举荐应用 scriptlet 输入,举荐应用 JSP 标签。

应用 JSTL 标签库步骤:

  1. 导入 jstl.jar 和 standard.jar 开发包
  2. 在 JSP 页面中用 tablib 指令引入须要用到的 JSTL 标签

core 标签库

  • core 标签库是 JSTL 的外围标签库,实现了最根本的性能:流程管制、迭代输入等操作
  • core 标签库的前缀个别是 c

c:out

  • 简略应用一下

    <%
        session.setAttribute("name", "zhongfucheng");
    %>
    
    //<c:out/> 标签反对标签体,default 属性上的数据能够写在标签体中
    //<c:out value="${name}" escapeXml="true"> 您要的数据找不着 </c:out>

    <c:out value="${name}" default="您要的数据找不着" escapeXml="true"/>

    
  • 咱们发现 下面的代码实现的成果和 EL 表达式是一样的 它杰出的中央就多了两个属性,default 和 escapeXml 属性。如果咱们用到这两个属性,咱们就应用该标签,如果没有用到这两个属性就用 EL 表达式就能够了。
    • *

c:set

  • 该标签有 5 个属性,用起来有略微有些简单了!当初要记住的就是:var 属性操作的是 Integer、Double、Float、String 等类型的数据,target 属性操作的是 JavaBean 或 Map 对象的数据,scope 代表的是 Web 域,value 是值,property 是对象的属性

应用 var 属性

  • 既然 var 属性只能操作 Integer、Double、String 等类型,那么存在 var 属性就肯定没有 property 属性(property 代表的是对象的成员属性,Integer、String 这些类型哪来的成员变量呀)
  • 上面的代码流程是这样的:创立了一个 name 的变量,设置的值为 zhongfucheng,范畴是 page

    <c:set var="name" value="fucheng" scope="page"/>
    
    ${name}
  • 成果:

  • 当然了,set 标签也反对标签体,value 的值能够写在标签体里边

    <c:set var="name" scope="page">
        zhongfucheng
    </c:set>
  • 应用 var 属性和 scope 属性实现计数器


    <%-- 因为上面变量须要做加法运算,所以要定义进去,不然服务器是不晓得我的变量是 Integer 类型的 --%>
    <%
        Integer sessionCount = 0;
        Integer applicationCount = 0;
    %>
    <c:set var="sessionCount" value="${sessionCount+1}" scope="session"/>
    
    <c:set var="applicationCount" value="${applicationCount+1}" scope="application"/>
  • 成果:

应用 target 属性

  • 应用 target 属性与之配对的是 property 属性,target 属性只能操作 JavaBean 或 Map 对象,property 就是对应的成员变量或 key 了。
  • 既然 target 属性操作的是 JavaBean 或 Map 对象,那么肯定是通过 EL 表达式来获取到对象了。taget 属性如果获取不到数据会抛出异样!应用 target 属性就肯定没有 scope 属性(scope 属性代表的是保留范畴,target 的值都是获取来的,难道你还能扭转人家的范畴?)

    <%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
    <jsp:useBean id="person" class="domain.Person" scope="session"/>
    
    <%-- 获取到 person 对象,设置 age 属性的值为 32--%>
    <c:set target="${person}" property="age" value="32"/>
    
    ${person.age}
  • 成果:

c:remove

remove 标签就相当简略了,只有 var 和 scope 属性,代表的是删除域范畴的属性

  • 上面简略来测试一下吧:

    <%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
    <jsp:useBean id="person" class="domain.Person" scope="session"/>
    
    <%-- 获取到 person 对象,设置 age 属性的值为 32--%>
    <c:set target="${person}" property="age" value="32"/>
    
    ${person.age}
    <br>
    
    <%-- 删除 session 属性 --%>
    <c:remove var="person" scope="session"></c:remove>
    ${person.age==null?"存在 session 的 person 对象被删除了!":"我还在呢!"}
  • 成果:

c:catch

该标签次要用来处理程序中产生的异样。

catch 标签也非常简略,只有一个 var 属性,var 属性封装了异样的信息!


    <%-- 创立出 JavaBean 对象,设置为 session 范畴的属性 --%>
    <jsp:useBean id="person" class="domain.Person" scope="session"/>
    
    <c:catch var="message">
    
        <%--target 属性只能是 EL 表达式,当初我是字符串,获取不到对象,必定会抛出异样的!--%>
        <c:set target="person" property="age" value="32"/>
    
    </c:catch>
    
    ${message}
  • 成果:

c:if

JSTL 提供了 if 标签实现分支语句的实现,test 属性是不可或缺的

var 和 scope 属性我看来如同没什么用的(保留执行后果有什么用?)

  • 依据传递过去的参数的不同显示不同的页面!

    <%-- 如果带过去的名字是 zhongfucheng,那么能够登陆 --%>
    <c:if test="${param.name=='zhongfucheng'}">
        用户名:<input type="text" name="username"><br>
        明码:<input type="password" name="password"><br>
        <input type="submit" value="登陆">
    </c:if>
    
    <%-- 如果带过去的名字是 ouzicheng,那么就是注册 --%>
    <c:if test="${param.name=='ouzicheng'}">
        用户名:<input type="text" name="username"><br>
        明码:<input type="password" name="password"><br>
        <input type="submit" value="注册">
    </c:if>
  • 留神地址栏的参数!


c:choose

if 标签没有 else 的性能,如果须要相似于 java 中的 if else 流程就须要应用 choose 标签。

choose 标签须要联结 when 和 otherwise 标签一起应用!


    <c:choose>
        <c:when test="${param.name=='zhongfucheng'}">
            你好啊,zhongfucheng
        </c:when>
        <c:when test="${param.name=='ouzicheng'}">
            你好啊,ouzicheng
        </c:when>
        <c:otherwise>
            你是谁啊?别轻易过去!</c:otherwise>
    </c:choose>
  • 成果:


c:forEach

forEach 为循环标签,相当于 Java 中的 while 和 for

  • 之前咱们在应用 EL 表达式获取到汇合的数据,遍历汇合都是用 scriptlet 代码循环,当初咱们学了 forEach 标签就能够舍弃 scriptlet 代码了。
  • 向 Session 中设置属性,属性的类型是 List 汇合

    <%
        List list = new ArrayList<>();
        list.add("zhongfucheng");
        list.add("ouzicheng");
        list.add("xiaoming");
    
        session.setAttribute("list", list);
    %>
  • 遍历 session 属性中的 List 汇合,items:行将要迭代的汇合。var:以后迭代到的元素

    <c:forEach  var="list" items="${list}" >
        ${list}<br>
    </c:forEach>
  • 成果:

  • 遍历 Map 对象有略微地不一样,咱们来看一下,var 属性保留的不是每个迭代的对象,而是 Map.Entry。


    <%
        Map map = new HashMap();
        map.put("1", "zhongfucheng");
        map.put("2", "xiaohong");
        map.put("3", "xiaoming");
    
        session.setAttribute("map",map);
    %>
    
    <c:forEach  var="me" items="${map}" >
    
        ${me.key}  ${me.value}<br>
    
    </c:forEach>

  • begin 默认从 0 开始、end 默认为汇合的最初一个元素、step 默认为 1
  • varStatus 代表着以后对象被迭代的信息,它有以下的属性

    • index【返回以后是第几个对象,从 0 开始计数】
    • count【曾经遍历多少个对象了,从 1 开始计数】
    • first【是否是第一个】
    • last【是否是最初一个】
    • current【以后被迭代的对象】
    • begin【开始的地位】
    • end【最初的地位】
    • step【步长】

    <c:forEach  var="list" items="${list}" varStatus="varStatus" >
    
        ${list}您的下标是:${varStatus.index}<br>
    
    </c:forEach>
  • 成果:


c:forTokens

该标签相似于 String 类的 split()和 for 循环的一种汇合

它与 forEach 标签十分类似,都有 begin、end、step、items、var、varStatus 属性,不同的是 forTokens 标签的 items 属性外面是字符串,这个字符串会被 delims 属性的内容宰割成多个字符串!


    <c:forTokens items="zhongfucheng,ouzicheng,xiaoming,xiaohong" var="name" delims="," >
        ${name}
    </c:forTokens>
  • 效果图:


c:import

import 标签相似于 JSP 行为 <jsp:include/> 和 JSP 指令<%include>

import 标签的属性:

  1. url【指定要蕴含的门路,Internet 所有的 url 都能够】
  2. context【拜访同一个 web 容器的其余资源,以 ”/” 结尾】
  3. var【保留导入的文件的内容,以 String 类型存储】
  4. socpe【保留的范畴,默认是 page】
  5. charEncoding【字符编码】
  6. varReader【保留导入文件的内容,以 Reader 类型存储】

当然了,import 标签性能更加更大!弱小在哪里呢?import 标签能够引入 Internet 网页上的内容,也就是说,csdn 也能够引入进来!

  • 咱们来用一下把!

    <c:import url="http://www.csdn.net" charEncoding="UTF-8" />
  • 咱们一看,是没有款式的

  • 打印 csdn 的源代码

    <c:import url="http://www.csdn.net" charEncoding="UTF-8" var="net"/>
    
    CSDN 的源码是:<br><br><br><br><br>
    <c:out value="${net}" escapeXml="true"></c:out>
  • 成果:


c:param

  • 在 JSP 页面进行 URL 的相干操作时,常常要在 URL 地址前面附加一些参数。<c:param> 标签能够嵌套在 <c:import>、<c:url> 或 <c:redirect> 标签内,为这些标签所应用的 URL 地址附加参数。
  • <c:param> 标签在为一个 URL 地址附加参数时,将主动对参数值进行 URL 编码,例如,如果传递的参数值为“中国”,则将其转换为“%d6%d0%b9%fa”后再附加到 URL 地址前面 ,这也就 是应用 <c:param> 标签的最大益处
    • *

c:url

url 标签非常实用!在浏览器禁用 Cookie 的时候,咱们之前学 Servlet 时解决办法是:response.encodeURL()。url 标签也能够实现这样的性能,再配合 param 标签应用,就非常实用了!

  • 咱们 配合 param 标签来应用一下吧

    <c:url value="2.jsp" var="url">
        <c:param name="name" value="中国!">
        </c:param>
    </c:url>
    
    <a href="${url}"> 我通过了 URL 地址重写!</a>
  • 成果:


c:redirect

redirect 标签用于实现 Redirect 性能,当然了,此标签也可能配合 param 标签应用!

  • 简略应用一下,重定向到 2.jsp,带了一个参数

    <c:redirect url="2.jsp" >
        <c:param name="name" value="zhongfucheng">
        </c:param>
    </c:redirect>
  • 在 2.jsp 中获取到参数


fmt 标签库

fmt 标签库也叫做国际化标签库。这里就不具体阐明了,等我讲到 Web 国际化的时候才讲吧!

fn 办法库

fn 办法库也叫做 EL 函数库、fn 标签库。这个在解说 EL 表达式的时候有具体的阐明,可转移到我 EL 表达式的博文中

为什么要应用自定义标签?

JSTL 标签库只提供了简略的输入等性能,没有实现任何的 HTML 代码封装,并且某些简单类型转换,或者逻辑解决的时候,JSTL 标签库实现不了,须要自定义标签!

编写自定义标签的步骤:

  1. 编写一个实现 Tag 接口的 Java 类【标签处理器类】
  2. 在 WEB-INF 目录下创立 tld(Tag Library Descriptor)文件,在 tld 文件中对标签解决类(实现 Tag 接口的 Java 类)进行形容

疾速入门

  • 指标:应用标签输入客户机的 IP 地址
  • 依照步骤来:首先编写一个实现 Tag 接口的 Java 类

    public class showIp implements Tag {
    
        @Override
        public void setPageContext(PageContext pageContext) { }
    
        @Override
        public void setParent(Tag tag) { }
    
        @Override
        public Tag getParent() {return null;}
    
        @Override
        public int doStartTag() throws JspException {return 0;}
    
        @Override
        public int doEndTag() throws JspException {return 0;}
    
        @Override
        public void release() {}
    }
  • 既然要获取到客户机的 IP 地址,那么 request 对象是必不可少的 。当初问题来了, 在 Tag 重写的办法如同不能间接获取到 request 对象啊
  • 通过我一番认真的察看,发现了上面这个办法:
        @Override
        public void setPageContext(PageContext pageContext) {}
  • 既然能获取到 pageContext 对象,那么其余 8 大内置对象还不是随随便便?于是乎,我就 定义一个成员变量 pageContext,在 setPageContext()办法中传递过去的 pageContext 赋值给我定义的成员变量即可

        private PageContext pageContext = null;

        @Override
        public void setPageContext(PageContext pageContext) {this.pageContext = pageContext;}
  • 好的,看回咱们的需要:应用标签输入客户机的 IP 地址。在下面残余 5 个办法中,最有可能就是在 doStartTag()办法中编写代码


    @Override
    public int doStartTag() throws JspException {

        // 获取到 request 对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();

        // 获取到客户机的 ip 地址
        String ip = httpServletRequest.getRemoteAddr();
        
        // 获取输入到浏览器的对象
        JspWriter jspWriter = pageContext.getOut();
        
        // 上面的异样只能捕捉,因为子类的异样不能比父类多
        try {jspWriter.write(ip);
        } catch (IOException e) {e.printStackTrace();
        }

        return 0;
    }
  • 接着,编写 tld 文件,形容实现 Tag 接口的 Java 类【标签解决类】

    <?xml version="1.0" encoding="ISO-8859-1"?>
    
    <taglib 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 http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
            version="2.1">
    
        <tlib-version>1.0</tlib-version>
        <short-name>zhongfucheng</short-name>
        <uri>/zhongfucheng</uri>
    
        <!-- Invoke 'Generate' action to add tags or functions -->
        <tag>
            <name>viewIp</name>
            <tag-class>tag.showIp</tag-class>
            <body-content>empty</body-content>
        </tag>
    
    
    </taglib>
  • 上面咱们 来测试一下看能不能用


标签解决类具体阐明

看完下面的程序,大部分人都是懵逼的。因为还不晓得它具体是怎么用的,调用程序是什么

  • 首先咱们来看一下 Tag 接口的源码

    public interface Tag extends JspTag {
        int SKIP_BODY = 0;
        int EVAL_BODY_INCLUDE = 1;
        int SKIP_PAGE = 5;
        int EVAL_PAGE = 6;
    
        void setPageContext(PageContext var1);
    
        void setParent(Tag var1);
    
        Tag getParent();
    
        int doStartTag() throws JspException;
    
        int doEndTag() throws JspException;
    
        void release();}
  • 下面程序的执行流程:

    • JSP 引擎遇到自定义标签,首先创立标签处理器类的实例对象
    • JSP 引擎实例化完标签处理器类后,调用 setPageContext()办法,将 pageContext 对象传递给标签处理器类,使得标签处理器类能够通过 pageContext 对象与 JSP 页面进行通信!
    • setPageContext()办法执行完后,调用 setParent()办法,将以后标签的父标签传递给以后处理器类,如果以后标签没有父标签,则传入 null
    • 当 WEB 容器执行到自定义标签的开始标记时,调用 doStartTag()办法。
    • 当 WEB 容器执行到自定义标签的完结标记时,调用 doEndTag()办法。
    • 一般来说,当 WEB 容器执行完自定义标签后,标签处理器类会驻留在内存中,直至进行 WEB 利用时,WEB 容器才会调用 release()办法


  • 咱们当初曾经分明了办法的执行程序了,可 Tag 接口的源码还有 4 个变量阿,它们是用来做什么的呢?咱们在编写 JSP 页面时,常常须要在页面中引入一些逻辑,例如:

    • 管制 JSP 页面某一部分(标签体)是否执行
    • 管制整个 JSP 页面是否执行
    • 管制 JSP 页面内容反复执行
    • 批改 JSP 页面内容输入
  • 再看回 4 个变量的名字,咱们能够发现,这 4 个变量就是用来做逻辑判断的
  • 咱们来测试一下吧,在 doEndTag()办法中,返回的是 SKIP_PAGE 变量,看下会怎么样

    @Override
    public int doEndTag() throws JspException {return SKIP_PAGE;}
  • 咱们再来看一看成果:

  • 如同是没什么区别!咱们再查看一下源代码,发现执行完标签后,前面的代码全都没有执行!

  • doStartTag()办法应用的是 SKIP_BODY 和 EVAL_BODY_INCLUDE 这两个变量,判断是否执行标签体的内容。
  • doEndTag()办法应用的是 SKIP_PAGE 和 EVAL_PAGE 这两个变量,判断是否执行剩下页面的内容
  • 管制 JSP 页面内容反复执行和批改 JSP 页面内容输入前面会有!
    • *

tld 文件具体阐明

  • 首先咱们来 看一下 tld 文件以后用到的内容吧

    <tlib-version>1.0</tlib-version>
    <short-name>myshortname</short-name>
    <uri>http://mycompany.com</uri>
    
    <tag>
        <name></name>
        <tag-class></tag-class>
        <body-content></body-content>
    </tag>
  • 咱们一个一个来看:

    • shortname 举荐应用 prefix
    • uri 就是引入这个标签库应用的 uri
    • name 为标签名
    • tagclass 为实现类
    • bodycontent 为标签体的限度,它有 4 个值:EMPTY【不容许有标签体】,JSP【容许有 JSP 代码】,scriptless【不容许有脚本代码(也就是 <%%>),容许有 EL 表达式,文本,JSP 行为】,tagdepentend【标签体内的 JSP 代码不会被解析,间接输入文本】
    • *

TagSupport 类

大部分时候咱们都不须要实现 Tag 接口来编写自定义标签,TagSupport 是 Tag 的一个模板类,实现了 pageContext,parent 的 getter、setter 办法以及一些其余的性能。咱们要做的就是重写 doStartTag()和 doEndTag()办法

  • 上面咱们就来简略应用一下吧:
  • 继承 TagSupport 类,重写 doStartTag()办法,比间接实现 Tag 接口简洁很多!

    public class Demo1 extends TagSupport {
    
        @Override
        public int doStartTag() throws JspException {
    
            // 获取到 request 对象
            HttpServletRequest httpServletRequest = (HttpServletRequest) pageContext.getRequest();
    
            String method = httpServletRequest.getMethod();
    
            JspWriter jspWriter = pageContext.getOut();
    
            try {jspWriter.write(method);
            } catch (IOException e) {e.printStackTrace();
            }
    
            return 0;
        }
    }
  • 在 tld 文件中形容一把


    <tag>
        <name>showMethod</name>
        <tag-class>tag.Demo1</tag-class>
        <body-content>empty</body-content>
    </tag>
  • 成果:

带属性的标签

下面咱们编写的自定义标签都没有附带属性的,咱们在应用 core 标签库的时候,标签个别都带有属性

其实JSTL 标签库的原理就是自定义标签,把自定义标签搞明确了,对 JSTL 标签库的应用就有更好的了解了

  • 想要自定义标签带有属性也非常简单,只有在标签处理器类上加一个成员变量和 setter、getter(),再在 tld 文件中形容下该属性即可!它的原理是这样的:当标签应用到属性的时候,引擎就会调用它的 setter()办法
  • 上面我想要实现的性能是:应用标签的人,传入一个字符串格局就能够显示想要的格局日期
  • 编写标签处理器类,减少一个成员变量以及对应的 setter、getter 办法

    public class Demo1 extends TagSupport {
    
    
        // 创立成员对象,对应的 setter、getter 办法
        private String format = null;
    
    
        @Override
        public int doStartTag() throws JspException {
    
            // 创立日期格式化对象
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
    
            // 格式化日期并向浏览器输入
            try {pageContext.getOut().write(simpleDateFormat.format(new Date()));
            } catch (IOException e) {e.printStackTrace();
            }
    
            return 0;
        }
    
        public String getFormat() {return format;}
    
        public void setFormat(String format) {this.format = format;}
    }
  • 在 tld 文件中形容标签和属性,name 代表的是属性的名字,required 代表的是是否为必须,rtexprvalue 代表是否应用 EL 表达式

    <tag>
        <name>formatDate</name>
        <tag-class>tag.Demo1</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>format</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
  • 咱们来看一下成果:


标签的继承关系

  • 在深刻解说之前,咱们先来看一下各种 Tag 接口、类之间的关系,这样学习上来才不会晕

IterationTag 阐明

  • 咱们曾经应用过了 Tag 接口和 TagSupport 类了。接下来咱们看一下 IterationTag 是什么玩意。

    public interface IterationTag extends Tag {
        int EVAL_BODY_AGAIN = 2;
    
        int doAfterBody() throws JspException;}
  • 从关系图咱们也能够看出,IterationTag 接口实现了 Tag 接口,InterationTag 接口和 Tag 接口最次要的区别就是多了个 doAfterBody()办法和 EVAL_BODY_AGAIN 变量
  • 了解起来也很简略:当 doAfterBody()返回的是 EVAL_BODY_AGAIN 变量,那么标签体的内容就始终循环!当然了,TagSupport 也实现了 Iteration 接口,也就是说 TagSupport 类也能实现 Iteration 接口的事件
  • 咱们来应用一下吧:

    public class Demo1 extends TagSupport {
    
        @Override
        public int doStartTag() throws JspException {
    
            try {pageContext.getOut().write("hello");
            } catch (IOException e) {e.printStackTrace();
            }
    
            // 执行标签体
            return EVAL_BODY_INCLUDE;
        }
    
        @Override
        public int doAfterBody() throws JspException {// 标签体一直循环,直到 doAfterBody()返回的是 SKIP_BODY
            return EVAL_BODY_AGAIN;
        
        }
    }
  • tld 文件中形容,既然标签体有内容,就不能用 empty 了

    <tag>
        <name>foreverEval</name>
        <tag-class>tag.Demo1</tag-class>
        <body-content>tagdependent</body-content>
    </tag>
  • 留神看横向的滑轮,曾经死循环输入了:

  • doAfterBody()中只有返回的是 SKPI_BODY 就退出循环,执行 doEndTag()办法

        // 定义一个变量,规定标签体循环的次数
        int x = 0;
        
        @Override
        public int doStartTag() throws JspException {
    
            try {pageContext.getOut().write("hello");
            } catch (IOException e) {e.printStackTrace();
            }
    
            // 执行标签体
            return EVAL_BODY_INCLUDE;
        }
    
        @Override
        public int doAfterBody() throws JspException {
            
            x++;
            if (x >= 10) {return SKIP_BODY;}
    
            // 标签体一直循环,直到 doAfterBody()返回的是 SKIP_BODY
            return EVAL_BODY_AGAIN;
    
        }
  • 当初咱们曾经能管制循环的次数了


BodyTag 阐明

后面咱们曾经应用到了带标签体的自定义标签了,后面的都是只能间接输入而得不到标签体的内容,既然得不到标签体的内容,就更别说批改标签体了

  • 此时,咱们就须要 BodyTag 接口的反对了!它专门用来解决带标签体的标签 ,上面咱们来 看一下 BodyTag 的源码

    public interface BodyTag extends IterationTag {
        /** @deprecated */
        int EVAL_BODY_TAG = 2;

        int EVAL_BODY_BUFFERED = 2;
    
        void setBodyContent(BodyContent var1);
    
        void doInitBody() throws JspException;}
  • BodyTag 多了 EVAL_BODY_BUFFERED 变量【一个曾经标识过期了】,多了 setBodyContent 和 doInitBody()两个办法
  • 其实 应用 BodyTag 非常简略

    • 如果 doStartTag()办法返回的是 EVAL_BODY_BUFFERED,把标签体的内容缓存起来
    • 接着调用 setBodyContent()办法和 doInitBody()办法,封装标签体的内容到 BodyContent 对象中
    • 接着调用 doEndTag()办法
    • 对于标签体的内容,咱们能够通过 getBodyContenet()来获取!
  • 再看回下面的关系图,BodyTag 实现了 IterationTag 和 Tag 接口,如果间接实现 BodyTag 接口做开发,要实现的办法就太多了。个别咱们应用继承 BodyTag 的 BodyTagSupport 来做开发
    • *

BodyTagSupport 阐明

  • 首先来看一下源代码吧:


    public class BodyTagSupport extends TagSupport implements BodyTag {
        protected BodyContent bodyContent;
    
        public BodyTagSupport() {}
    
        public int doStartTag() throws JspException {return 2;}
    
        public int doEndTag() throws JspException {return super.doEndTag();
        }
    
        public void setBodyContent(BodyContent b) {this.bodyContent = b;}
    
        public void doInitBody() throws JspException {}
    
        public int doAfterBody() throws JspException {return 0;}
    
        public void release() {
            this.bodyContent = null;
            super.release();}
    
        public BodyContent getBodyContent() {return this.bodyContent;}
    
        public JspWriter getPreviousOut() {return this.bodyContent.getEnclosingWriter();
        }
    }
  • 能够发现:BodyTagSupport 次要裁减了以下的内容:

    • 把 BodyContent 间接定义为成员变量,在获取标签体内容的时候就不须要通过 getBodyContent()获取了
    • 提供获取 JspWriter 的办法,不须要从 pageConext 中获取了
    • 以上的两个裁减都简化了咱们的代码书写


        protected BodyContent bodyContent;

        public JspWriter getPreviousOut() {return this.bodyContent.getEnclosingWriter();
        }
  • 从 BodyTag 接口中,我就说到了:标签体的内容封装到了 BodyContent 类中,那么 BodyContent 类到底是什么?咱们来看一下源码

    public abstract class BodyContent extends JspWriter {
        private JspWriter enclosingWriter;
    
        protected BodyContent(JspWriter e) {super(-2, false);
            this.enclosingWriter = e;
        }
    
        public void flush() throws IOException {throw new IOException("Illegal to flush within a custom tag");
        }
    
        public void clearBody() {
            try {this.clear();
            } catch (IOException var2) {throw new Error("internal error!;");
            }
        }
    
        public abstract Reader getReader();
    
        public abstract String getString();
    
        public abstract void writeOut(Writer var1) throws IOException;
    
        public JspWriter getEnclosingWriter() {return this.enclosingWriter;}
    }
  • 原来 BodyContent 继承着 JspWriter,它与 JspWriter 最大的区别是:BodyContent 类的任何写入的内容并不主动地向页面输入!
  • 咱们个别应用 BodyContent 都应用两个办法:

    // 将数据转变成 Reader 对象
    public abstract Reader getReader();

    // 将数据转变成 String 对象
    public abstract String getString();
  • 再从关系图咱们能够看初,BodyTagSupport 继承了 TagSupport 类实现了 BodyTag 接口,能够说:BodyTagSupport 有着后面讲的接口和类的所有性能!
  • 上面咱们来 应用下 BodyTagSupport 将标签体的内容转成是小写的
  • 标签处理器类

    public class Demo1 extends BodyTagSupport {
    
        @Override
        public int doStartTag() throws JspException {
    
    
            // 想要获取到标签体的内容,就要返回 EVAL_BODY_BUFFERED 变量
    
            return EVAL_BODY_BUFFERED;
    
        }
    
        @Override
        public int doEndTag() throws JspException {
    
            // 获取到标签体的内容
            String value = bodyContent.getString();
    
            // 将标签体的内容转成小写并输入
            try {this.getPreviousOut().write(value.toLowerCase());
            } catch (IOException e) {e.printStackTrace();
            }
    
            return super.doEndTag();}
    
    }
  • tld 文件:

    <tag>
        <name>BodyContentToLowerCase</name>
        <tag-class>tag.Demo1</tag-class>
        <body-content>tagdependent</body-content>
    </tag>
  • 成果:

正文完
 0