JSP和JSTL详解

什么是JSP

JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术。

JSP这门技术的最大的特点在于,写jsp就像在写html,但它相比html而言,html只能为用户提供静态数据,而Jsp技术允许在页面中嵌套java代码,为用户提供动态数据。

JSP原理

Web服务器是如何调用并执行一个jsp页面的

浏览器向服务器发请求,不管访问的是什么资源,其实都是在访问Servlet,所以当访问一个jsp页面时,其实也是在访问一个Servlet,服务器在执行jsp的时候,首先把jsp翻译成一个Servlet,所以我们访问jsp时,其实不是在访问jsp,而是在访问jsp翻译过后的那个Servlet,例如下面的代码:

index.jsp:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">

    <title>First Jsp</title>

  </head>

  <body>
    <%
        out.print("Hello Jsp");
    %>
  </body>
</html>

当我们通过浏览器访问index.jsp时,服务器首先将index.jsp翻译成一个index_jsp.class,在Tomcat服务器的work\Catalina\localhost\项目名\org\apache\jsp目录下可以看到index_jsp.class的源代码文件index_jsp.java,index_jsp.java的代码如下:

package org.apache.jsp;

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

public final class index_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 _jspx_dependants;

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public Object getDependants() {
    return _jspx_dependants;
  }

  public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  public void _jspDestroy() {
  }

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

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    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');
      out.write('\n');

String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

      out.write("\r\n");
      out.write("\r\n");
      out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("    <base href=\"");
      out.print(basePath);
      out.write("\">\r\n");
      out.write("    \r\n");
      out.write("    <title>First Jsp</title>\r\n");
      out.write("\t\r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    ");

        out.print("Hello Jsp");

      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);
    }
  }
}

我们可以看到,index_jsp这个类是继承 org.apache.jasper.runtime.HttpJspBase这个类的,通过查看Tomcat服务器的源代码,可以知道在apache-tomcat-6.0.20-src\java\org\apache\jasper\runtime目录下存HttpJspBase这个类的源代码文件,如下图所示:

我们可以看看HttpJsBase这个类的源代码,如下所示:

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.jasper.runtime;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.HttpJspPage;
import javax.servlet.jsp.JspFactory;

import org.apache.jasper.compiler.Localizer;

/**
 * This is the super class of all JSP-generated servlets.
 *
 * @author Anil K. Vijendran
 */
public abstract class HttpJspBase
    extends HttpServlet
    implements HttpJspPage


{

    protected HttpJspBase() {
    }

    public final void init(ServletConfig config)
    throws ServletException
    {
        super.init(config);
    jspInit();
        _jspInit();
    }

    public String getServletInfo() {
    return Localizer.getMessage("jsp.engine.info");
    }

    public final void destroy() {
    jspDestroy();
    _jspDestroy();
    }

    /**
     * Entry point into service.
     */
    public final void service(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
    {
        _jspService(request, response);
    }

    public void jspInit() {
    }

    public void _jspInit() {
    }

    public void jspDestroy() {
    }

    protected void _jspDestroy() {
    }

    public abstract void _jspService(HttpServletRequest request,
                     HttpServletResponse response)
    throws ServletException, IOException;
}

HttpJspBase类是继承HttpServlet的,所以HttpJspBase类是一个Servlet,而index_jsp又是继承HttpJspBase类的,所以index_jsp类也是一个Servlet,所以当浏览器访问服务器上的index.jsp页面时,其实就是在访问index_jsp这个Servlet,index_jsp这个Servlet使用_jspService这个方法处理请求。

jsp页面中的html排版标签是如何被发送到客户端的?

浏览器接收到的这些数据:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="http://localhost:8080/JavaWeb_Jsp_Study_20140603/">

    <title>First Jsp</title>

  </head>

  <body>
    Hello Jsp
  </body>
</html>

都是在_jspService方法中使用如下的代码输出给浏览器的:

out.write('\r');
out.write('\n');

String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

out.write("\r\n");
out.write("\r\n");
out.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
out.write("<html>\r\n");
out.write("  <head>\r\n");
out.write("    <base href=\"");
out.print(basePath);
out.write("\">\r\n");
out.write("    \r\n");
out.write("    <title>First Jsp</title>\r\n");
out.write("\t\r\n");
out.write("  </head>\r\n");
out.write("  \r\n");
out.write("  <body>\r\n");
out.write("    ");

out.print("Hello Jsp");

out.write("\r\n");
out.write("  </body>\r\n");
out.write("</html>\r\n");

在jsp中编写的java代码和html代码都会被翻译到_jspService方法中去,在jsp中编写的java代码会原封不动地翻译成java代码,如<%out.print("Hello Jsp");%>直接翻译成out.print("Hello Jsp");,而HTML代码则会翻译成使用out.write("<html标签>\r\n");的形式输出到浏览器。在jsp页面中编写的html排版标签都是以out.write("<html标签>\r\n");的形式输出到浏览器,浏览器拿到html代码后才能够解析执行html代码。

jsp页面中的java代码服务器是如何执行的?

在jsp中编写的java代码会被翻译到_jspService方法中去,当执行_jspService方法处理请求时,就会执行在jsp编写的java代码了,所以jsp页面中的java代码服务器是通过调用_jspService方法处理请求时执行的。

web服务器在调用jsp时,会给jsp提供一些什么java对象?

查看_jspService方法可以看到,web服务器在调用jsp时,会给jsp提供如下的8个java对象

PageContext pageContext;
HttpSession session;
ServletContext application;
ServletConfig config;
JspWriter out;
Object page = this;
HttpServletRequest request,
HttpServletResponse response

其中page对象,request和response已经完成了实例化,而其它5个没有实例化的对象通过下面的方式实例化:

pageContext = _jspxFactory.getPageContext(this, request, response,null, true, 8192, true);
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();

这8个java对象在jsp页面中是可以直接使用的,如下所示:

<%
        session.setAttribute("name", "session对象");//使用session对象,设置session对象的属性
        out.print(session.getAttribute("name")+"<br/>");//获取session对象的属性
        pageContext.setAttribute("name", "pageContext对象");//使用pageContext对象,设置pageContext对象的属性
        out.print(pageContext.getAttribute("name")+"<br/>");//获取pageContext对象的属性
        application.setAttribute("name", "application对象");//使用application对象,设置application对象的属性
        out.print(application.getAttribute("name")+"<br/>");//获取application对象的属性
        out.print("Hello Jsp"+"<br/>");//使用out对象
        out.print("服务器调用index.jsp页面时翻译成的类的名字是:"+page.getClass()+"<br/>");//使用page对象
        out.print("处理请求的Servlet的名字是:"+config.getServletName()+"<br/>");//使用config对象
        out.print(response.getContentType()+"<br/>");//使用response对象
        out.print(request.getContextPath()+"<br/>");//使用request对象
%>

jsp的最佳实践

jsp最佳实践就是jsp技术在开发中该怎么去用。

不管是JSP还是Servlet,虽然都可以用于开发动态web资源。但由于这2门技术各自的特点,在长期的软件实践中,人们逐渐把servlet作为web应用中的控制器组件来使用,而把JSP技术作为数据显示模板来使用。其原因为,程序的数据通常要美化后再输出:让jsp既用java代码产生动态数据,又做美化会导致页面难以维护。让servlet既产生数据,又在里面嵌套html代码美化数据,同样也会导致程序可读性差,难以维护。因此最好的办法就是根据这两门技术的特点

tomcat服务器的执行流程

第一次执行:

  1. 客户端通过电脑连接服务器,因为是请求是动态的,所以所有的请求交给WEB容器来处理
  2. 在容器中找到需要执行的*.jsp文件
  3. 之后*.jsp文件通过转换变为*.java文件
  4. *.java文件经过编译后,形成*.class文件
  5. 最终服务器要执行形成的*.class文件

第二次执行:

  1. 因为已经存在了*.class文件,所以不在需要转换和编译的过程

修改后执行:

  1. 源文件已经被修改过了,所以需要重新转换,重新编译。

JSP基础语法

任何语言都有自己的语法,JAVA中有,JSP虽然是在JAVA上的一种应用,但是依然有其自己扩充的语法,而且在JSP中,所有的JAVA语句都可以使用。

JSP模板元素

JSP页面中的HTML内容称之为JSP模版元素。 

JSP模版元素定义了网页的基本骨架,即定义了页面的结构和外观。

JSP表达式

JSP脚本表达式(expression)用于将程序数据输出到客户端。

语法:<%= 变量或表达式 %>

举例:输出系统当前时间

<%= new java.util.Date() %> 

JSP引擎在翻译脚本表达式时,会将程序数据转成字符串,然后在相应位置用out.print(…) 将数据输给客户端。

JSP脚本表达式中的变量或表达式后面不能有分号(;)。

JSP脚本片段

JSP脚本片断(scriptlet)用于在JSP页面中编写多行Java代码。语法:

<% 
    多行java代码 
%>

在<% %>中可以定义变量、编写语句,不能定义方法(因为这些都是在_jspService方法中)。

范例:在Scriptlet中定义变量、编写语句

<%
    int sum=0;//声明变量

    /*编写语句*/
    for (int i=1;i<=100;i++){
        sum+=i;
    }
    out.println("<h1>Sum="+sum+"</h1>");
%>

注意事项:

  • JSP脚本片断中只能出现java代码,不能出现其它模板元素, JSP引擎在翻译JSP页面中,会将JSP脚本片断中的Java代码将被原封不动地放到Servlet的_jspService方法中。
  • JSP脚本片断中的Java代码必须严格遵循Java语法,例如,每执行语句后面必须用分号(;)结束。
  • 在一个JSP页面中可以有多个脚本片断,在两个或多个脚本片断之间可以嵌入文本、HTML标记和其他JSP元素。

举例:

<%
    int x = 10;
    out.println(x);
%>
<p>这是JSP页面文本</p>
<%
    int y = 20;
    out.println(y);
%>

多个脚本片断中的代码可以相互访问,犹如将所有的代码放在一对<%%>之中的情况。如:out.println(x);

单个脚本片断中的Java语句可以是不完整的,但是,多个脚本片断组合后的结果必须是完整的Java语句,例如:

<%
    for (int i=1; i<5; i++)
    {
%>
    <H1>http://localhost:8080/JavaWeb_Jsp_Study_20140603/</H1>
<%
    }
%>

JSP声明

JSP页面中编写的所有代码,默认会翻译到servlet的service方法中, 而Jsp声明中的java代码被翻译到_jspService方法的外面。语法:

<%! 
    java代码
%>

所以,JSP声明可用于定义JSP页面转换成的Servlet程序的静态代码块、成员变量和方法 。 

多个静态代码块、变量和函数可以定义在一个JSP声明中,也可以分别单独定义在多个JSP声明中。

JSP隐式对象的作用范围仅限于Servlet的_jspService方法,所以在JSP声明中不能使用这些隐式对象。

JSP声明示例:

<%!
static {
    System.out.println("loading Servlet!");
}

private int globalVar = 0;

public void jspInit(){
    System.out.println("initializing jsp!");
}
%>

<%!
public void jspDestroy(){
    System.out.println("destroying jsp!");
}
%>

JSP注释

在JSP中,注释有两大类:

显式注释:直接使用HTML风格的注释:<!- - 注释内容- ->

隐式注释:直接使用JAVA的注释://、/*……*/

JSP自己的注释:<%- - 注释内容- -%>

这三种注释的区别:

<!--这个注释可以看见-->

<%
    //JAVA中的单行注释

    /*
        JAVA中的多行注释
    */
%>

<%--JSP自己的注释--%>

HTML的注释在浏览器中查看源文件的时候是可以看得到的,而JAVA注释和JSP注释在浏览器中查看源文件时是看不到注释的内容的,这就是这三种注释的区别。

JSP指令

JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分。

在JSP 2.0规范中共定义了三个指令:

  • page指令
  • Include指令
  • taglib指令

JSP指令的基本语法格式:<%@ 指令 属性名="值" %>

例如:

<%@ page contentType="text/html;charset=gb2312"%>

如果一个指令有多个属性,这多个属性可以写在一个指令中,也可以分开写。

<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.Date"%>

也可以写作:

<%@ page contentType="text/html;charset=gb2312" import="java.util.Date"%>

page指令

page指令用于定义JSP页面的各种属性,无论page指令出现在JSP页面中的什么地方,它作用的都是整个JSP页面,为了保持程序的可读性和遵循良好的编程习惯,page指令最好是放在整个JSP页面的起始位置。例如:

JSP 2.0规范中定义的page指令的完整语法:

<%@ 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" ]
%>

page指令的import属性

在jsp页面中,Jsp引擎会自动导入下面的包:

  • java.lang.*
  • javax.servlet.*
  • javax.servlet.jsp.*
  • javax.servlet.http.*

可以在一条page指令的import属性中引入多个类或包,其中的每个包或类之间使用逗号(,)分隔

 <%@ page import="java.util.*,java.io.*,java.sql.*"%>

上面的语句也可以改写为使用多条page指令的import属性来分别引入各个包或类:

<%@ page import="java.util.Date"%>
<%@ page import="java.io.*" %>
<%@ page import="java.sql.*" %>

page指令的errorPage属性

  • errorPage属性的设置值必须使用相对路径,如果以“/”开头,表示相对于当前Web应用程序的根目录(注意不是站点根目录),否则,表示相对于当前页面
  • 可以在web.xml文件中使用<error-page>元素为整个Web应用程序设置错误处理页面。
  • <error-page>元素有3个子元素,<error-code>、<exception-type>、<location>
  • <error-code>子元素指定错误的状态码,例如:<error-code>404</error-code>
  • <exception-type>子元素指定异常类的完全限定名,例如:<exception-type>java.lang.ArithmeticException</exception-type>
  • <location>子元素指定以“/”开头的错误处理页面的路径,例如:<location>/ErrorPage/404Error.jsp</location>
  • 如果设置了某个JSP页面的errorPage属性,那么在web.xml文件中设置的错误处理将不对该页面起作用。

使用errorPage属性指明出错后跳转的错误页面

比如Test.jsp页面有如下的代码:

<%@ page language="java" import="java.util.*" errorPage="/ErrorPage/error.jsp" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>测试page指令的errorPage属性</title>
  </head>
  <body>
    <%
      //这行代码肯定会出错,因为除数是0,一运行就会抛出异常
        int x = 1/0;
    %>
  </body>
</html>

在Test.jsp中,page指令的errorPage属性指明了出错后跳转到"/ErrorPage/error.jsp",error.jsp页面代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>错误信息友好提示页面</title>
  </head>
  <body>
           对不起,出错了,请联系管理员解决!
  </body>
</html>

在web.xml中使用<error-page>标签为整个web应用设置错误处理页面

例如:使用<error-page>标签配置针对404错误的处理页面

web.xml的代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
    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-app_3_0.xsd">
  <display-name></display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <!-- 针对404错误的处理页面 -->
  <error-page>
      <error-code>404</error-code>
      <location>/ErrorPage/404Error.jsp</location>
  </error-page>

</web-app>

404Error.jsp代码如下: 

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>404错误友好提示页面</title>
    <!-- 3秒钟后自动跳转回首页 -->
    <meta http-equiv="refresh" content="3;url=${pageContext.request.contextPath}/index.jsp">
  </head>
  <body>
    <img alt="对不起,你要访问的页面没有找到,请联系管理员处理!"
    src="${pageContext.request.contextPath}/img/404Error.png"/><br/>
    3秒钟后自动跳转回首页,如果没有跳转,请点击<a href="${pageContext.request.contextPath}/index.jsp">这里</a>
  </body>
</html>

当访问一个不存在的web资源时,就会跳转到在web.xml中配置的404错误处理页面404Error.jsp。

关于在web.xml中使用<error-page>标签为整个web应用设置错误处理页面在IE下无法跳转的解决办法

这里需要注意的是,如果错误页面比较小,那么当访问服务器上不存在的web资源或者访问服务器出错时在IE浏览器下是无法跳转到错误页面的,显示的是ie自己的错误页面,而在火狐和google浏览器下(其他浏览器没有测试过)是不存在注意的问题的。

我们可以通过下面的实验来证明:

在web.xml中配置500错误时的错误友好提示页面

<!-- 针对500错误的处理页面 -->
<error-page>
      <error-code>500</error-code>
      <location>/ErrorPage/500Error.jsp</location>
</error-page>

500Error.jsp页面的代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<html>
  <head>
    <title>500(服务器错误)错误友好提示页面</title>
    <!-- 3秒钟后自动跳转回首页 -->
    <meta http-equiv="refresh" content="3;url=${pageContext.request.contextPath}/index.jsp">
  </head>
  <body>
    <img alt="对不起,服务器出错!"
    src="${pageContext.request.contextPath}/img/500Error.png"/><br/>
    3秒钟后自动跳转回首页,如果没有跳转,请点击<a href="${pageContext.request.contextPath}/index.jsp">这里</a>
  </body>
</html>

使用page指令的的isErrorPage属性显式声明页面为错误页面

如果某一个jsp页面是作为系统的错误处理页面,那么建议将page指令的isErrorPage属性(默认为false)设置为"true"来显式声明这个Jsp页面是一个错误处理页面。

例如:将error.jsp页面显式声明为错误处理页面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" isErrorPage="true"%>
<html>
  <head>
    <title>错误信息友好提示页面</title>
  </head>

  <body>
           对不起,出错了,请联系管理员解决!
  </body>
</html>

将error.jsp页面显式声明为错误处理页面后,有什么好处呢,好处就是Jsp引擎在将jsp页面翻译成Servlet的时候,在Servlet的 _jspService方法中会声明一个exception对象,然后将运行jsp出错的异常信息存储到exception对象中,如下所示:

由于Servlet的_jspService方法中声明了exception对象,那么就可以在error.jsp页面中使用exception对象,这样就可以在Jsp页面中拿到出错的异常信息了,如下: 

如果没有设置isErrorPage="true",那么在jsp页面中是无法使用exception对象的,因为在Servlet的_jspService方法中不会声明一个exception对象,如下所示:

Jsp有9大内置对象,而一般情况下exception对象在Jsp页面中是获取不到的,只有设置page指令的isErrorPage属性为"true"来显式声明Jsp页面是一个错误处理页面之后才能够在Jsp页面中使用exception对象。

include指令 

在JSP中对于包含有两种语句形式:

  • @include指令
  • <jsp:include>指令

@include指令

@include可以包含任意的文件,当然,只是把文件的内容包含进来。

include指令用于引入其它JSP页面,如果使用include指令引入了其它JSP页面,那么JSP引擎将把这两个JSP翻译成一个servlet。所以include指令引入通常也称之为静态引入。

语法:<%@ include file="relativeURL"%>,其中的file属性用于指定被引入文件的路径。路径以“/”开头,表示代表当前web应用。

include指令细节注意问题:

  • 被引入的文件必须遵循JSP语法。
  • 被引入的文件可以使用任意的扩展名,即使其扩展名是html,JSP引擎也会按照处理jsp页面的方式处理它里面的内容,为了见明知意,JSP规范建议使用.jspf(JSP fragments(片段))作为静态引入文件的扩展名。
  • 由于使用include指令将会涉及到2个JSP页面,并会把2个JSP翻译成一个servlet,所以这2个JSP页面的指令不能冲突(除了pageEncoding和导包除外)。

include指令使用范例:

新建head.jspf页面和foot.jspf页面,分别作为jsp页面的头部和尾部,存放于WebRoot下的jspfragments文件夹中,代码如下:

head.jspf代码:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1 style="color:red;">网页头部</h1>

foot.jspf代码:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1 style="color:blue;">网页尾部</h1>

在WebRoot文件夹下创建一个IncludeTagTest.jsp页面,在IncludeTagTest.jsp页面中使用@include指令引入head.jspf页面和foot.jspf页面,代码如下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>jsp的Include指令测试</title>
  </head>

  <body>
  <%--使用include标签引入引入其它JSP页面--%>
    <%@include file="/jspfragments/head.jspf" %>
    <h1>网页主体内容</h1>
    <%@include file="/jspfragments/foot.jspf" %>
  </body>
</html>

打开IncludeTagTest_jsp.java,里面的代码如下所示:

package org.apache.jsp;

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

public final class IncludeTagTest_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 _jspx_dependants;

  static {
    _jspx_dependants = new java.util.ArrayList(2);
    _jspx_dependants.add("/jspfragments/head.jspf");
    _jspx_dependants.add("/jspfragments/foot.jspf");
  }

  private javax.el.ExpressionFactory _el_expressionfactory;
  private org.apache.AnnotationProcessor _jsp_annotationprocessor;

  public Object getDependants() {
    return _jspx_dependants;
  }

  public void _jspInit() {
    _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory();
    _jsp_annotationprocessor = (org.apache.AnnotationProcessor) getServletConfig().getServletContext().getAttribute(org.apache.AnnotationProcessor.class.getName());
  }

  public void _jspDestroy() {
  }

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

    PageContext pageContext = null;
    HttpSession session = null;
    ServletContext application = null;
    ServletConfig config = null;
    JspWriter out = null;
    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("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\r\n");
      out.write("<html>\r\n");
      out.write("  <head>\r\n");
      out.write("  \r\n");
      out.write("    <title>jsp的Include指令测试</title>\r\n");
      out.write("  \r\n");
      out.write("  </head>\r\n");
      out.write("  \r\n");
      out.write("  <body>\r\n");
      out.write("    ");
      out.write("\r\n");
      out.write("<h1 style=\"color:red;\">网页头部</h1>\r\n");
      out.write("\r\n");
      out.write("    <h1>网页主体内容</h1>\r\n");
      out.write("    ");
      out.write("\r\n");
      out.write("<h1 style=\"color:blue;\">网页尾部</h1>\r\n");
      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);
    }
  }
}

可以看到,head.jspf和foot.jspf页面的内容都使用out.write输出到浏览器显示了。

使用@include可以包含任意的内容,文件的后缀是什么都无所谓。这种把别的文件内容包含到自身页面的@include语句就叫作静态包含,作用只是把别的页面内容包含进来,属于静态包含。

jsp:include指令

jsp:include(JSP标签)指令为动态包含,如果被包含的页面是JSP,则先处理之后再将结果包含,而如果包含的是非*.jsp文件,则只是把文件内容静态包含进来,功能与@include类似。

JSP标签

JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护。

jsp的常用标签有以下三个

  • <jsp:include>标签  
  • <jsp:forward>标签  
  • <jsp:param>标签

jsp:include 标签

<jsp:include>标签用于把另外一个资源的输出内容插入进当前JSP页面的输出内容之中,这种在JSP页面执行时的引入方式称之为动态引入。

语法:<jsp:include page="relativeURL | <%=expression%>" flush="true|false" />

  • page属性用于指定被引入资源的相对路径,它也可以通过执行一个表达式来获得。
  • flush属性指定在插入其他资源的输出内容时,是否先将当前JSP页面的已输出的内容刷新到客户端。 

范例:使用jsp:include标签引入资源

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>jsp的jsp:include标签测试</title>
  </head>

  <body>
  <%--使用jsp:include标签引入其它JSP页面--%>
  <jsp:include page="/jspfragments/head.jsp"/>
    <h1>网页主体内容</h1>
    <jsp:include page="/jspfragments/foot.jsp"/>
  </body>
</html>

<jsp:include>标签与include指令的区别

<jsp:include>标签是动态引入, <jsp:include>标签涉及到的2个JSP页面会被翻译成2个servlet,这2个servlet的内容在执行时进行合并。 

而include指令是静态引入,涉及到的2个JSP页面会被翻译成一个servlet,其内容是在源文件级别进行合并。

不管是<jsp:include>标签,还是include指令,它们都会把两个JSP页面内容合并输出,所以这两个页面不要出现重复的HTML全局架构标签,否则输出给客户端的内容将会是一个格式混乱的HTML文档。

*.jspf扩展名文件在jsp:include、@include和c:import中的区别

JSP规范建议使用.jspf(JSP fragments)作为静态引入文件的扩展名。今天无意中发现,把一个JSP文件命名为jspf扩展名,然后include到另一个jsp文件中的,发现只有用"@include"指令的时候,jspf文件的内容才会被解析并执行其中的jsp指令和tag,而使用"jsp:include"和JSTL的"c:import"都没有用,jspf文件被当作纯文本文件处理了。

比如现在有一个head.jspf页面和foot.jspf页面:

head.jspf:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1 style="color:red;">网页头部</h1>

foot.jspf:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1 style="color:blue;">网页尾部</h1>

首先使用"@include"指令将"head.jspf和foot.jspf" include到IncludeTagTest.jsp页面,如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>jsp的Include指令测试</title>
  </head>

  <body>
   <%--使用include标签引入引入jspf页面--%>
    <%@include file="/jspfragments/head.jspf" %>
    <h1>网页主体内容</h1>
    <%@include file="/jspfragments/foot.jspf" %>
  </body>
</html>

然后再使用<jsp:include>"标签将"head.jspf和foot.jspf" include到JspIncludeTagTest.jsp页面中,如下所示:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <title>jsp的jsp:include标签测试</title>
  </head>

  <body>
  <%--使用jsp:include标签引入其它JSPf页面--%>
  <jsp:include page="/jspfragments/head.jspf"/>
    <h1>网页主体内容</h1>
    <jsp:include page="/jspfragments/foot.jspf"/>
  </body>
</html>

通过浏览器查看两者的html源码,发现head.jspf和foot.jspf中的<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>没有解析执行,而是原封不动地作为文本内容输出到页面上了,在IE下看不到<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>的输出,在google和火狐浏览器下运行可以看到页面上输出<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>。这说明jspf文件Tomcat服务器被当作纯文本文件处理了,没有当作jsp页面来解析执行,那么该如何解决这个问题呢?如何让tomcat服务器能够解析执行*.jspf文件中的java代码和标签呢,有如下的几种解决办法:

(1) 解决办法一:修改web.xml文件,添加对扩展名为*.jspf文件的映射

<!-- 让jspf扩展名同样成为JSP Servlet处理的文件。 -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jspf</url-pattern>
</servlet-mapping>
<!-- 让jsp扩展名同样成为JSP Servlet处理的文件。 -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
</servlet-mapping>

上面的配置方式也可以简写成这样:

<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <!-- 让jspf扩展名同样成为JSP Servlet处理的文件。-->
    <url-pattern>*.jspf</url-pattern>
</servlet-mapping>

两种写法的效果都是一样的。

添加这样的配置信息后,此时tomcat服务器就可以正常解析执行*.jspf文件了。

(2) 解决办法二:修改Tomcat服务器的web.xml文件,添加对扩展名为*.jspf文件的映射

找到tomcat服务器的web.xml文件,找到名字为jsp的那个Servlet,如下所示:

<servlet>
    <servlet-name>jsp</servlet-name>
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
    <init-param>
        <param-name>fork</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>xpoweredBy</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>

然后根据Servlet名找到对应的servlet-mapping配置,如下所示:

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
</servlet-mapping>

在这里可以看到,名字为jsp的那个Servlet只支持*.jsp和*.jspx两种扩展名,因此可以在这个地方添加多一个<url-pattern>*.jspf</url-pattern>,如下所示:

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
    <servlet-name>jsp</servlet-name>
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspx</url-pattern>
    <url-pattern>*.jspf</url-pattern>
</servlet-mapping>

经过这样的配置之后,Tomcat服务器就可以正常解析和执行*.jspf文件了。

jsp:forward 标签

<jsp:forward>标签用于把请求转发给另外一个资源。

语法:<jsp:forward page="relativeURL | <%=expression%>" /> 

page属性用于指定请求转发到的资源的相对路径,它也可以通过执行一个表达式来获得。

范例:使用<jsp:forward>标签跳转页面

forwarddemo01.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%--使用<jsp:forward>标签跳转到forwarddemo02.jsp--%>
<jsp:forward page="/forwarddemo02.jsp"/>

forwarddemo02.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1>跳转之后的页面!!</h1>

运行后,我们可以看出,页面已经完成了跳转,但地址栏没有变化,地址栏中显示的地址还是forwarddemo01.jsp,但页面显示的内容却是forwardemo02.jsp中的内容。因为此跳转属于服务器端跳转。只要是服务器端跳转,则地址栏永远没有变化。

jsp:param 标签

当使用<jsp:include>和<jsp:forward>标签引入或将请求转发给其它资源时,可以使用<jsp:param>标签向这个资源传递参数。

语法1:

<jsp:include page="relativeURL | <%=expression%>">
    <jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>

语法2:

<jsp:forward page="relativeURL | <%=expression%>">
    <jsp:param name="parameterName" value="parameterValue|<%= expression %>" />
</jsp:include>

<jsp:param>标签的name属性用于指定参数名,value属性用于指定参数值。在<jsp:include>和<jsp:forward>标签中可以使用多个<jsp:param>标签来传递多个参数。

范例:使用<jsp:param>标签向被包含的页面传递参数

JspIncludeTagDemo03.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1>JspIncludeTagDemo03.jsp</h1>
<hr/>
<jsp:include page="/jspfragments/Inc.jsp">
    <jsp:param name="parm1" value="hello" />
    <jsp:param name="parm2" value="gacl" />
</jsp:include>

Inc.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1>接收从JspIncludeTagDemo03.jsp页面中传递过来的参数:</h1>
<h2><%=request.getParameter("parm1")%></h2>
<h2><%=request.getParameter("parm2")%></h2>

在JspIncludeTagDemo03.jsp页面中使用<jsp:include>标签将Inc.jsp页面包含进来,使用<jsp:param/>标签向Inc.jsp页面传递了两个参数parm1和parm2。

范例:使用<jsp:param>标签向要跳转的页面传递参数

forwarddemo03.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%--使用<jsp:forward>标签跳转到forwarddemo04.jsp--%>
<%--使用<jsp:param>标签向forwarddemo04.jsp传递参数--%>
<jsp:forward page="/forwarddemo04.jsp">
    <jsp:param name="ref1" value="hello" />
    <jsp:param name="ref2" value="gacl" />
</jsp:forward>

forwarddemo04.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<h1>跳转之后的页面!!</h1>
<h1>接收从forwarddemo03.jsp传递过来的参数:</h1>
<h1>ref1:<%=request.getParameter("ref1")%></h1>
<h1>ref2:<%=request.getParameter("ref2")%></h1>

JSP中的九个内置对象

每个JSP 页面在第一次被访问时,WEB容器都会把请求交给JSP引擎(即一个Java程序)去处理。JSP引擎先将JSP翻译成一个_jspServlet(实质上也是一个servlet) ,然后按照servlet的调用方式进行调用。

由于JSP第一次访问时会翻译成servlet,所以第一次访问通常会比较慢,但第二次访问,JSP引擎如果发现JSP没有变化,就不再翻译,而是直接调用,所以程序的执行效率不会受到影响。

JSP引擎在调用JSP对应的_jspServlet时,会传递或创建9个与web开发相关的对象供_jspServlet使用。JSP技术的设计者为便于开发人员在编写JSP页面时获得这些web对象的引用,特意定义了9个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这9大对象的引用。

九个内置对象:

  • pageContext:javax.servlet.jsp.PageContext
  • request:javax.servlet.http.HttpServletRequest
  • response:javax.servlet.http.HttpServletResponse
  • session:javax.servlet.http.HttpSession
  • application:javax.servlet.ServletContext
  • config:javax.servlet.ServletConfig
  • out:javax.servlet.jsp.JspWriter
  • page:java.lang.Object
  • exception:java.lang.Throwable

pageContext

pageContext 对象的作用是取得任何范围的参数,通过它可以获取 JSP页面的out、request、reponse、session、application 等对象。pageContext对象的创建和初始化都是由容器来完成的,在JSP页面中可以直接使用 pageContext对象。

提供了对JSP页面内所有的对象及名字空间的访问,也就是说他可以访问到本页所在的SESSION,也可以取本页面所在的application的某一属性值,他相当于页面中所有功能的集大成者,它的本类名也叫pageContext。

作用域是page。

常用方法:

  • JspWriter getOut() 返回当前客户端响应被使用的JspWriter流(out)
  • HttpSession getSession() 返回当前页中的HttpSession对象(session)
  • Object getPage() 返回当前页的Object对象(page)
  • ServletRequest getRequest() 返回当前页的ServletRequest对象(request) (常用)
  • ServletResponse getResponse() 返回当前页的ServletResponse对象(response) (常用)
  • Exception getException() 返回当前页的Exception对象(exception)
  • ServletConfig getServletConfig() 返回当前页的ServletConfig对象(config)
  • ServletContext getServletContext() 返回当前页的ServletContext对象(application)
  • void setAttribute(String name,Object attribute) 设置属性及属性值 (常用)
  • void setAttribute(String name,Object obj,int scope) 在指定范围内设置属性及属性值 (常用)
  • public Object getAttribute(String name) 取属性的值 (常用)
  • Object getAttribute(String name,int scope) 在指定范围内取属性的值 (常用)
  • public Object findAttribute(String name) 寻找一属性,返回起属性值或NULL
  • void removeAttribute(String name) 删除某属性 (常用)
  • void removeAttribute(String name,int scope) 在指定范围删除某属性 (常用)
  • int getAttributeScope(String name) 返回某属性的作用范围
  • Enumeration getAttributeNamesInScope(int scope) 返回指定范围内可用的属性名枚举
  • void release() 释放pageContext所占用的资源
  • void forward(String relativeUrlPath) 使当前页面重导到另一页面
  • void include(String relativeUrlPath) 在当前位置包含另一文件 

request

该对象代表了客户端的请求信息,主要用于接受通过HTTP协议传送到服务器的数据。(包括头信息、系统信息、请求方式以及请求参数等)。request对象的作用域为一次请求

是客户端对服务器发出的请求。客户端的请求信息被封装在request对象中,通过它发送请求给服务器。它是HttpServletRequest类的实例。

作用域是request。

常用方法:

  • Object getAttribute(String name) 返回指定属性的属性值(常用)
  • Enumeration getAttributeNames() 返回所有可用属性名的枚举
  • String getCharacterEncoding() 返回字符编码方式(常用)
  • int getContentLength() 返回请求体的长度(以字节数)
  • String getContentType() 得到请求体的MIME类型
  • ServletInputStream getInputStream() 得到请求体中一行的二进制流
  • String getParameter(String name) 返回name指定参数的参数值(常用)
  • Enumeration getParameterNames() 返回可用参数名的枚举
  • String[] getParameterValues(String name) 返回包含参数name的所有值的数组
  • String getProtocol() 返回请求用的协议类型及版本号
  • String getScheme() 返回请求用的计划名,如:http.https及ftp等
  • String getServerName() 返回接受请求的服务器主机名
  • int getServerPort() 返回服务器接受此请求所用的端口号
  • BufferedReader getReader() 返回解码过了的请求体
  • String getRemoteAddr() 返回发送此请求的客户端IP地址
  • String getRemoteHost() 返回发送此请求的客户端主机名
  • void setAttribute(String key,Object obj) 设置属性的属性值(常用)
  • String getRealPath(String path) 返回一虚拟路径的真实路径

response

response 代表的是对客户端的响应,主要是将JSP容器处理过的对象传回到客户端。response对象也具有作用域,它只在JSP页面内有效。

服务器收到客户端请求后做出的响应。它是HttpServletResponse类的实例。

作用域为page(页面执行期)。

常用方法:

  • String getCharacterEncoding() 返回响应用的是何种字符编码 (常用)
  • ServletOutputStream getOutputStream() 返回响应的一个二进制输出流
  • PrintWriter getWriter() 返回可以向客户端输出字符的一个对象
  • void setContentLength(int len) 设置响应头长度
  • void setContentType(String type) 设置响应的MIME类型
  • sendRedirect(java.lang.String location) 重新定向客户端的请求 (常用)

session

session 对象是由服务器自动创建的与用户请求相关的对象。服务器为每个用户都生成一个session对象,用于保存该用户的信息,跟踪用户的操作状态。session对象内部使用Map类来保存数据,因此保存数据的格式为 “Key/value”。 session对象的value可以使复杂的对象类型,而不仅仅局限于字符串类型。

是客户端与服务器的一次会话,从客户连到服务器的一个WebApplication开始,直到客户端与服务器断开连接为止。它是HttpSession类的实例。

作用域session(会话期)。

常用方法:

  • long getCreationTime() 返回SESSION创建时间
  • public String getId() 返回SESSION创建时JSP引擎为它设的惟一ID号 (常用)
  • long getLastAccessedTime() 返回此SESSION里客户端最近一次请求时间
  • int getMaxInactiveInterval() 返回两次请求间隔多长时间此SESSION被取消(ms)
  • String[] getValueNames() 返回一个包含此SESSION中所有可用属性的数组 (常用)
  • void invalidate() 取消SESSION,使SESSION不可用 (常用)
  • boolean isNew() 返回服务器创建的一个SESSION,客户端是否已经加入
  • void removeValue(String name) 删除SESSION中指定的属性
  • void setMaxInactiveInterval() 设置两次请求间隔多长时间此SESSION被取消(ms)

application

application 对象可将信息保存在服务器中,直到服务器关闭,否则application对象中保存的信息会在整个应用中都有效。与session对象相比,application对象生命周期更长,类似于系统的“全局变量”。

实现了用户间数据的共享,可存放全局变量。它开始于服务器的启动,直到服务器的关闭,在此期间,此对象将一直存在;这样在用户的前后连接或不同用户之间的连接中,可以对此对象的同一属性进行操作;在任何地方对此对象属性的操作,都将影响到其他用户对此的访问。服务器的启动和关闭决定了application对象的生命。它是ServletContext类的实例。

作用域是application。

常用方法:

  • Object getAttribute(String name) 返回给定名的属性值 (常用)
  • Enumeration getAttributeNames() 返回所有可用属性名的枚举
  • void setAttribute(String name,Object obj) 设定属性的属性值 (常用)
  • void removeAttribute(String name) 删除一属性及其属性值 (常用)
  • String getServerInfo() 返回JSP(SERVLET)引擎名及版本号
  • String getRealPath(String path) 返回一虚拟路径的真实路径
  • ServletContext getContext(String uripath) 返回指定WebApplication的application对象
  • int getMajorVersion() 返回服务器支持的Servlet API的最大版本号
  • int getMinorVersion() 返回服务器支持的Servlet API的最大版本号
  • String getMimeType(String file) 返回指定文件的MIME类型
  • URL getResource(String path) 返回指定资源(文件及目录)的URL路径 (常用)
  • InputStream getResourceAsStream(String path) 返回指定资源的输入流
  • RequestDispatcher getRequestDispatcher(String uripath) 返回指定资源的RequestDispatcher对象 (常用)
  • Servlet getServlet(String name) 返回指定名的Servlet (常用)
  • Enumeration getServlets() 返回所有Servlet的枚举
  • Enumeration getServletNames() 返回所有Servlet名的枚举
  • void log(String msg) 把指定消息写入Servlet的日志文件
  • void log(Exception exception,String msg) 把指定异常的栈轨迹及错误消息写入Servlet的日志文件
  • void log(String msg,Throwable throwable) 把栈轨迹及给出的Throwable异常的说明信息 写入Servlet的日志文件 

config

config 对象的主要作用是取得服务器的配置信息。通过 pageConext对象的 getServletConfig() 方法可以获取一个config对象。当一个Servlet 初始化时,容器把某些信息通过 config对象传递给这个 Servlet。 开发者可以在web.xml 文件中为应用程序环境中的Servlet程序和JSP页面提供初始化参数。

config对象是在一个Servlet初始化时,JSP引擎向它传递信息用的,此信息包括Servlet初始化时所要用到的参数(通过属性名和属性值构成)以及服务器的有关信息(通过传递一个ServletContext对象)。

作用域是page。

常用方法:

  • ServletContext getServletContext() 返回含有服务器相关信息的ServletContext对象
  • String getInitParameter(String name) 返回初始化参数的值
  • Enumeration getInitParameterNames() 返回Servlet初始化所需所有参数的枚举

out

out 对象用于在Web浏览器内输出信息,并且管理应用服务器上的输出缓冲区。在使用 out 对象输出数据时,可以对数据缓冲区进行操作,及时清除缓冲区中的残余数据,为其他的输出让出缓冲空间。待数据输出完毕后,要及时关闭输出流。

out对象是JspWriter类的实例,是向客户端输出内容常用的对象 。

作用域为page(页面执行期)。

常用方法:

  • void clear() 清除缓冲区的内容
  • void clearBuffer() 清除缓冲区的当前内容 
  • void flush() 清空流
  • int getBufferSize() 返回缓冲区以字节数的大小,如不设缓冲区则为0
  • int getRemaining() 返回缓冲区还剩余多少可用
  • boolean isAutoFlush() 返回缓冲区满时,是自动清空还是抛出异常
  • void close() 关闭输出流 

page

page 对象代表JSP本身,只有在JSP页面内才是合法的。 page隐含对象本质上包含当前 Servlet接口引用的变量,类似于Java编程中的 this 指针。

page对象就是指向当前JSP页面本身,有点象类中的this指针,它是java.lang.Object类的实例 。“page” 对象代表了正在运行的由JSP文件产生的类对象,不建议一般读者使用。

作用域 page。

常用方法:

  • Class getClass 返回此Object的类 (常用)
  • int hashCode() 返回此Object的hash码
  • boolean equals(Object obj) 判断此Object是否与指定的Object对象相等 (常用)
  • void copy(Object obj) 把此Object拷贝到指定的Object对象中
  • Object clone() 克隆此Object对象
  • String toString() 把此Object对象转换成String类的对象 (常用)
  • void notify() 唤醒一个等待的线程
  • void notifyAll() 唤醒所有等待的线程
  • void wait(int timeout) 使一个线程处于等待直到timeout结束或被唤醒
  • void wait() 使一个线程处于等待直到被唤醒
  • void enterMonitor() 对Object加锁
  • void exitMonitor() 对Object开锁

exception

exception 对象的作用是显示异常信息,只有在包含 isErrorPage=“true” 的页面中才可以被使用,在一般的JSP页面中使用该对象将无法编译JSP文件。excepation对象和Java的所有对象一样,都具有系统提供的继承结构。exception 对象几乎定义了所有异常情况。在Java程序中,可以使用try/catch关键字来处理异常情况; 如果在JSP页面中出现没有捕获到的异常,就会生成 exception 对象,并把 exception 对象传送到在page指令中设定的错误页面中,然后在错误页面中处理相应的 exception 对象。

是一个例外对象,当一个页面在运行过程中发生了例外,就产生这个对象。如果一个JSP页面要应用此对象,就必须把isErrorPage设为true,否则无法编译。它实际上是java.lang.Throwable的对象。

作用域 page。

常用方法:

  • String getMessage() 返回描述异常的消息
  • String toString() 返回关于异常的简短描述消息
  • void printStackTrace() 显示异常及其栈轨迹
  • Throwable FillInStackTrace() 重写异常的执行栈轨迹

四种作用域

四种作用域:page、request、session、application

作用域的有效期限:

  • page作用域的变量只在当前页面中有效,只要页面跳转了,page域中的变量重新计算。
  • request里的变量可以跨越forward前后的两页。但是只要刷新页面,它们就重新计算了。
  • session作用域体现在会话中,当未关闭游览器时,session中的变量是通用的,当关闭重新启动游览器,重新发起新的会话时,session域中的变量重新计算。
  • application里的变量一直在累加,除非你重启tomcat,否则它会一直变大

九大内置对象对应的作用域:

  • request作用域:request对象
  • session作用域:session对象
  • application作用域:application对象
  • page作用域:response、page、pageContext、out、config、exception 对象

JSP JavaBean

JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范。

接下来给出的是 JavaBean 与其它 Java 类相比而言独一无二的特征:

  • 提供一个默认的无参构造函数。
  • 需要被序列化并且实现了 Serializable 接口。
  • 可能有一系列可读写属性。
  • 可能有一系列的 getter 或 setter 方法。

示例:

public class StudentsBean implements java.io.Serializable
{
   private String firstName = null;
   private String lastName = null;
   private int age = 0;

   public StudentsBean() {
   }
   public String getFirstName(){
      return firstName;
   }
   public String getLastName(){
      return lastName;
   }
   public int getAge(){
      return age;
   }

   public void setFirstName(String firstName){
      this.firstName = firstName;
   }
   public void setLastName(String lastName){
      this.lastName = lastName;
   }
   public void setAge(int age) {
      this.age = age;
   }
}

<jsp:useBean> 标签可以在 JSP 中声明一个 JavaBean,然后使用。声明后,JavaBean 对象就成了脚本变量,可以通过脚本元素或其他自定义标签来访问。

其中,根据具体情况,scope 的值可以是 page,request,session 或 application。id值可任意只要不和同一 JSP 文件中其它 <jsp:useBean> 中 id 值一样就行了。

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

<jsp:useBean id="date" class="java.util.Date" /> 
<p>日期为:<%= date %>

</body>
</html>

在 <jsp:useBean> 标签主体中使用 <jsp:getProperty/> 标签来调用 getter 方法,使用 <jsp:setProperty/> 标签来调用 setter 方法,语法格式如下:

<jsp:useBean id="id" class="bean 编译的类" scope="bean 作用域">
   <jsp:setProperty name="bean 的 id" property="属性名"  
                    value="value"/>
   <jsp:getProperty name="bean 的 id" property="属性名"/>
   ...........
</jsp:useBean>

name属性指的是Bean的id属性。property属性指的是想要调用的getter或setter方法。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<head>
<title>get 和 set 属性实例</title>
</head>
<body>

<jsp:useBean id="students" 
                    class="com.runoob.StudentsBean"> 
   <jsp:setProperty name="students" property="firstName"
                    value="小强"/>
   <jsp:setProperty name="students" property="lastName" 
                    value="王"/>
   <jsp:setProperty name="students" property="age"
                    value="10"/>
</jsp:useBean>

<p>学生名字: 
   <jsp:getProperty name="students" property="firstName"/>
</p>
<p>学生姓氏: 
   <jsp:getProperty name="students" property="lastName"/>
</p>
<p>学生年龄: 
   <jsp:getProperty name="students" property="age"/>
</p>

</body>
</html>

EL表达式和JSTL标签库

EL表达式

特点

(1)是一个由java开发的工具包

(2)用于从特定域对象中读取并写入到响应体开发任务,不能向域对象中写入。

(3)EL工具包自动存在Tomcat的lib中(el-api.jar),开发是可以直接使用,无需其他额外的包。

(4)标准格式 : ${域对象别名.关键字} 到指定的域中获取相应关键字的内容,并将其写入到响应体。

注意事项:JavaBean的属性名或map的键名中如果有特殊字符的写法:

变量名["键名"] 可以使用双引号,如:${map["no3-no4"]}

域对象

jsp el 描述
application applicationScope 全局作用域对象
session sessionScope 会话作用域
request requestScope 请求作用域对象
pageContext pageScope 当前页作用域对象

 注:使用时可以省略域对象别名

默认查找顺序: pageScope -> requestScope -> sessionScope -> applicationScope

最好只在pageScope中省略

使用示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>jsp</title>
</head>
<body>
  <%
    application.setAttribute("name","application");
    session.setAttribute("name","session");
    request.setAttribute("name","request");
    pageContext.setAttribute("name","pageContext");
  %>
  <br>--------------------使用java语言---------------------------<br>
  application中的值:<%= application.getAttribute("name") %> <br>
  session中的值:<%= session.getAttribute("name") %> <br>
  request中的值:<%= request.getAttribute("name") %> <br>
  pageContext中的值:<%= pageContext.getAttribute("name") %> <br>

  <br>--------------------使用EL表达式---------------------------<br>
  application中的值:${applicationScope.name} <br>
  session中的值:${sessionScope.name} <br>
  request中的值:${requestScope.name} <br>
  pageContext中的值:${pageScope.name} <br>

  <br>----------------使用EL表达式,省略域对象---------------------<br>
  application中的值:${name} <br>

</body>
</html>

支持的运算

(1)算术表达式

 

(2)比较表达式

(3)逻辑表达式

(4)三元表达式

 (5)判空表达式

示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>EL运算</title>
</head>
<body>
<%
    request.setAttribute("num1","12");
    request.setAttribute("num2","14");

    application.setAttribute("flag1",true);
    application.setAttribute("flag2",false);
%>
<br>--------------------使用java语言---------------------------<br>
<%
    String num1 = (String)request.getAttribute("num1");
    String num2 = (String)request.getAttribute("num2");
    int num3 = Integer.parseInt(num1) + Integer.parseInt(num2);
    
    boolean flag1 = (Boolean) application.getAttribute("flag1");
    boolean flag2 = (Boolean) application.getAttribute("flag2");
    boolean flag3 = flag1 && flag2;
    //输出方式一
    out.write(Boolean.toString(flag3));
%>
<!-- 输出方式二 -->
<h1><%=num3%></h1>

<br>--------------------使用EL表达式--------------------------<br>
<h1>${ requestScope.num1 + requestScope.num2 }</h1>
<h1>${ requestScope.num1 > requestScope.num2 }</h1>
<h1>${ applicationScope.flag1 && applicationScope.flag2 }</h1>

</body>
</html>

其他内置对象

(1)param 使用 ${param.请求参数名} 从请求参数中获取参数内容

(2)paramValues 使用 ${ paramValues.请求参数名 } 从请求参数中获取多个值,以数组的形式

(3)initParam 使用 ${ initParam.参数名 } 获取初始化参数

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>其他内置对象</title>
</head>
<body>
    url: ...?username=zhangsan&password=admin                            <br>
    url中的参数: 用户名:${param.username} ---- 密码:${param.password}   <br>
--------------------------------------------------------------------------------------    <br>
    url: ...?username=zhangsan&username=lisi                            <br>
    url中的参数: 用户1:${paramValues.username[0]} -- 用户2:${paramValues.username[1]}   <br>
    ----------------------------------------------------------------------------------   <br>
    <!--
     在web.xml中添加启动参数
     <context-param>
          <param-name>driver</param-name>
          <param-value>com.mysql.jdbc.Driver</param-value>
      </context-param>
    -->
   获取启动参数:${initParam.driver}
</body>
</html>

EL表达式的缺陷

(1)只能读取域对象中的值,不能写入

(2)不支持if判断和控制语句

JSTL标签工具类

JSTL(JSP Standard Tag Library) 即JSP标准标签库

JSTL一共包含四大标签库:

  • core:核心标签库,我们学习的重点;
  • fmt:格式化标签库,只需要学习两个标签即可;
  • sql:数据库标签库,不需要学习了,它过时了;
  • xml:xml标签库,不需要学习了,它过时了。

使用方式

(1)导入依赖的jar包 jstl.jar standard.jar

(2)在jsp中引入JSTL的core和fmt的包依赖约束

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"% >

core核心库重要标签的使用

<c:set>

在JSP文件上设置域对象中的共享数据

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
    <title>  c:set  </title>
</head>
    <body>
        <!--
        相当于
        <%--  <%   --%>
        <%--   request.setAttribute("name","zhangsan");--%>
        <%--  %>  --%>
        -->
        <c:set scope="request" var="name" value="zhangsan" />
        通过JSTL标签添加的作用域中的值:${requestScope.name}   <br>
        <c:set scope="application" var="name" value="lisi" />
        通过JSTL标签添加的作用域中的值:${applicationScope.name}   <br>
        <c:set scope="request" var="name" value="wangwu" />
        通过JSTL标签添加的作用域中的值:${requestScope.name}   <br>
        <c:set scope="page" var="name" value="zhaoliu" />
        通过JSTL标签添加的作用域中的值:${pageScope.name}   <br>
    </body>
</html>

<c:if >

控制哪些内容能够输出到响应体

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title> c:if </title>
</head>
<body>
    <c:set scope="page" var="age" value="20"/>
    <br>------------------------------使用java语言-------------------------------------<br>
    <%
        if( Integer.parseInt((String)pageContext.getAttribute("age")) >= 18 ){
    %>
    输入:欢迎光临!
    <%  } else { %>
    输入:未满十八,不准入内!
    <%  }  %>
    <br>------------------------------使用JSTL标签-------------------------------------<br>

    <c:if test="${ age ge 18 }">
        输入:欢迎光临!
    </c:if>
    <c:if test="${ age lt 18 }">
        输入:未满十八,不准入内!
    </c:if>
</body>
</html>

<c:choose>

 在jsp中进行多分支判断,决定哪个内容写入响应体

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title> c:choose </title>
</head>
<body>
    <c:set scope="page" var="age" value="6"/>
    <br>------------------------------使用java语言-------------------------------------<br>
    <%
        if( Integer.parseInt((String)pageContext.getAttribute("age")) == 18 ){
    %>
    输入:您今年成年了
    <%  } else if( Integer.parseInt((String)pageContext.getAttribute("age")) > 18 ){ %>
    输入:您已经成年了
    <%  }  else {%>
    输出:您还是个孩子
    <% } %>
    <br>------------------------------使用JSTL标签-------------------------------------<br>

    <c:choose>
        <c:when test="${age eq 18}">
            输入:您今年成年了
        </c:when>
        <c:when test="${age gt 18}">
            输入:您已经成年了
        </c:when>
        <c:otherwise>
            输入:您还是个孩子
        </c:otherwise>
    </c:choose>
</body>
</html>

<c:forEach>

循环遍历。

使用方式:

<c:forEach var="申明循环变量的名称" begin="初始化循环变量" 
           end="循环变量可以接受的最大值" step="循环变量的递增或递减值">
    *** step属性可以不写,默认递增1
    *** 循环变量默认保存在pageContext中
</c:forEach>

示例:

<%@ page import="com.zn.Student" %>
<%@ page import="java.util.List" %>
<%@ page import="java.util.ArrayList" %>
<%@ page import="java.util.Map" %>
<%@ page import="java.util.HashMap" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title> c:forEach </title>
</head>
<body>
    <%
        pageContext.setAttribute("students",new ArrayList(){{
            add(new Student("s1","zhangsan",16));
            add(new Student("s2","lisi",19));
            add(new Student("s3","wangwu",15));
        }});
        pageContext.setAttribute("stuMap", new HashMap(){{
            put("m1",new Student("s1","zhangsan",16));
            put("m2",new Student("s2","lisi",18));
            put("m3",new Student("s3","wangwu",15));
        }});
    %>
    <br>------------------------使用java语言------------------------------<br>
    <table>
        <tr><td>学号</td><td>姓名</td><td>年龄</td></tr>
        <%
            List<Student> stus =            (ArrayList<Student>)pageContext.getAttribute("students");
            for (int i = 0; i < stus.size(); i++) {
        %>
          <tr><td><%=stus.get(i).getSid()%></td>
              <td><%=stus.get(i).getName()%></td>
              <td><%=stus.get(i).getAge()%></td>
          </tr>
        <% } %>
    </table>
    
    <br>----------------------使用JSTL标签读取list-----------------------<br>
    <table>
        <tr><td>学号</td><td>姓名</td><td>年龄</td></tr>
        <c:forEach var="student" items="${students}">
        <tr><td>${student.sid}</td>
            <td>${student.name}</td>
            <td>${student.age}</td>
        </tr>
        </c:forEach>
    </table>

    <br>---------------------使用JSTL标签读取map------------------------<br>
    <table>
        <tr><td>学号</td><td>姓名</td><td>年龄</td></tr>
        <c:forEach var="student" items="${stuMap}">
            <tr>
                <td>${student.key}</td>
                <td>${student.value.sid}</td>
                <td>${student.value.name}</td>
                <td>${student.value.age}</td>
            </tr>
        </c:forEach>
    </table>

    <br>--------------使用JSTL标签读取指定for循环-----------------------<br>
    <select>
      <c:forEach var="item" begin="1" end="10" step="1">
          <option> ${item} </option>
      </c:forEach>
    </select>

</body>
</html>

其中使用的javaBean:

public class Student {
    
    private String sid;
    private String name;
    private int age;

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Student(String sid, String name, int age) {
        this.sid = sid;
        this.name = name;
        this.age = age;
    }
}

 fmt标签库重要标签使用

<fmt:formatDate>

用于使用不同的方式格式化日期。

语法:

<fmt:formatDate
  value="datevalue"
  [dateStyle="default|short|medium|long|full"]
  [pattern="customPattern"]
  [scope="page|request|session|application"]
  [timeStyle="default|short|medium|long|full"]
  [timeZone="timeZone"]
  [type="time|date|both"]
  [var="varname"]
/>

其中,[ ]中的内容为可选项,参数说明如下表所示:

参 数 说 明
value 要显示的日期
dateStyle 设定日期输出的格式
pattern 自定义格式的模式
scope 设定参数 var 的有效范围,默认为 page
timeStyle 设置日期的输出风格
timeZone 设置日期的时区
type 设置输出的类别,如 time、date 和 both
var 代表格式化后的数字,若设定了该参数,需要使用 <c:out> 标签输出

示例:

<fmt:formatDate value="${isoDate}" type="both"/>
2004-5-31 23:59:59

<fmt:formatDate value="${date}" type="date"/>
2004-4-1

<fmt:formatDate value="${isoDate}" type="time"/>
23:59:59

<fmt:formatDate value="${isoDate}" type="date" dateStyle="default"/>
2004-5-31

<fmt:formatDate value="${isoDate}" type="date" dateStyle="short"/>
04-5-31

<fmt:formatDate value="${isoDate}" type="date" dateStyle="medium"/>
2004-5-31

<fmt:formatDate value="${isoDate}" type="date" dateStyle="long"/>
2004年5月31日

<fmt:formatDate value="${isoDate}" type="date" dateStyle="full"/>
2004年5月31日 星期一

<fmt:formatDate value="${isoDate}" type="time" timeStyle="default"/>
23:59:59

<fmt:formatDate value="${isoDate}" type="time" timeStyle="short"/>
下午11:59

<fmt:formatDate value="${isoDate}" type="time" timeStyle="medium"/>
23:59:59

<fmt:formatDate value="${isoDate}" type="time" timeStyle="long"/>
下午11时59分59秒

<fmt:formatDate value="${isoDate}" type="time" timeStyle="full"/>
下午11时59分59秒 CDT

<fmt:formatDate value="${date}" type="both" pattern="EEEE, MMMM d, yyyy HH:mm:ss Z"/>
星期四, 四月 1, 2004 13:30:00 -0600

<fmt:formatDate value="${date}" type="both" pattern="yyyy-MM-dd HH:mm"/>
2011-08-10 13:30

如果写成pattern="yyyy-MM-dd hh:mm",则以十二小时形式显示,2011-08-10 01:30

<fmt:formatDate value="${isoDate}" type="both" pattern="d MMM yy, h:m:s a zzzz/>
31 五月 04, 11:59:59 下午中央夏令时  

<fmt:parseDate>

用于把字符串类型的日期转换成日期数据类型。

语法:

<fmt:parseDate
   value="parseDateValue"
   [dateStyle="default|short|medium|long|full"]
   [parseLocale="parseLocale"]
   [pattern="customPattern"]
   [scope="page|request|session|application"]
   [timeStyle="default|short|medium|long|full"]
   [timeZone="timeZone"]
   [type="time|date|both"]
   [var="varname"] />

其中,[ ]中的内容为可选项,以上参数说明如下表所示:

参数 说明
value 要显示的日期
type 设置输出的类别,如 time、date 和 both
dateStyle 设定日期输出的格式
pattern 自定义格式的模式
timeStyle 设定日期的输出风格
timeZone 设定日期的时区
var 代表格式化后的数字,若设定了该参数,需要利用 <c:out> 标签输出
scope 设定参数 var 的有效范围,默认为 page

示例:

<%@ page contentType="text/html" pageEncoding="GBK"%>
<%@ page import="java.util.*"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
  <head><title>国际化标签库</title></head>	
  <body>
  	<%
  	pageContext.setAttribute("date",new Date());
  	%>
  <fmt:formatDate value="${date}" type="both" pattern="yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒" var="date1"/>
  	<h3>本地时间:${date1}</h3>
  	<fmt:parseDate value="${date1}" type="both" pattern="yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒" var="date11"/>
  	<h4>反格式化时间(local):${date11}</h4>
  	<fmt:formatDate value="${date}" type="both" pattern="yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒"  timeZone="HST" var="date2"/>
  	<h3>夏威夷时间:${date2}</h3>
  
  	  <fmt:parseDate value="${date2}" type="both" pattern="yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒"  var="date3"/>
  	<h4>反格式化时间(夏威夷):${date3}</h4>
  </body>
</html>

<fmt:formatNumber>

用于格式化数字、百分比和货币。

语法:

<fmt:formatNumber
  value="numberValue"
  [type="number|percent|currency"]
  [pattern="pattern"]
  [currencyCode="currencyCode"]
  [currencySymbol="currencySymbol"]
  [groupingUsed="true|false"]
  [maxIntegerDigits="maxIntegerDigits"]
  [minIntegerDigits="minIntegerDigits"]
  [maxFractionDigits="maxFractionDigits"]
  [minFractionDigits="minFractionDigits"]
  [var="varname"]
  [scope="page|request|session|application"] />

其中,[ ]中包含的内容为可选项,对上述参数说明如下表所示:

参数 说明
value 要格式化的数字
type 设置数字的单位,即 number(数字)、currency(货币)、percent(百分数)
pattern 设定显示的格式
currencyCode 设置货币码
currencySymbol 设置货币符号
groupingUsed 设置是否对数字分组,默认为 true。
maxIntegerDigits 设置最多的整数位,若设定的数值少于数字的实际位数时,数字的左边位数会截去相应的位数,例如 123456,maxIntegerDigits 设定为 4,则结果为 3456。
minIntegerDigits 设置最少的整数位,若设定的数值多于数字的实际位数时,会在数字的左边补 0,例如 123,minIntegerDigits 设定为 4,则结果为 123.5。
maxFractionDigits 设置最多小数位数,若设定的数值小于数字的实际小数位数,则会从右边删掉多余位数,例如 123.56,minIntegerDigits 设定为 1,则结果为 123.5。
minFractionDigits 设置最少小数位数,若设定的数值大于数字的实际小数位数,则会从右边补 0。例如 123.56,minFractionDigits 设定为 4,则结果为 123.5600。
var 代表格式化后的数字,若设定了该参数,需要 <c:out> 标签输出。
scope 设定参数 var 的有效范围,默认为 page。

pattern 属性包含的字符说明如下表所示:

符号 描述
0 代表一位数字
E 使用指数格式
# 代表一位数字,若没有则显示 0
. 小数点
, 数字分组分隔符
; 分隔格式
- 使用默认复数前缀
% 百分数
? 千分数
¤ 货币符号,使用实际的货币符号代替
X 指定可以作为前缀和后缀的字符
` 在前缀或后缀中引用特殊字符

示例:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <c:set var="Amount" value="9850.14115" />
    <fmt:formatNumber value="${Amount}" type="currency" />
    <br>
    <br>
    <fmt:formatNumber type="number" groupingUsed="true" value="${Amount}" />
    <br>
    <br>
    <fmt:formatNumber type="number" maxIntegerDigits="3" value="${Amount}" />
    <br>
    <br>
    <fmt:formatNumber type="number" maxFractionDigits="6" value="${Amount}" />
    <br>
    <br>
    <fmt:formatNumber type="percent" maxIntegerDigits="4" value="${Amount}" />
    <br>
    <br>
    <fmt:formatNumber type="number" pattern="###.###$" value="${Amount}" />
</body>
</html>

页面输出内容:

¥9,850.14

9,850.141

850.141

9,850.14115

5,014%

9850.141$

<fmt:parseNumber>

标签用于解析数字、货币和百分比。

语法:

<fmt:parseNumber
  value="number"
  type="number|currency|percent"
  pattern="customPattern"
  [parseLocale="parseLocale"]
  [integerOnly="true|false"]
  var="varname"
  scope="page|request|session|application"/>

其中,[ ] 中的内容为可选项,参数说明如下表所示:

参数 说明
value 需要解析的数字
type 设定数字的单位,有 3 种:number(数字)、currency(货币)、percent(百分比)
pattern 设定显示的格式
integerOnly 设置是否只输出整数部分
parseLocale 解析数字时所用的区域
var 代表格式化后的数字,如果设定了该参数,需要使用 <c:out> 标签输出
scope 设定参数 var 的有效范围,默认为 page。

示例:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <c:set var="Amount" value="786.970" />
    <fmt:parseNumber var="j" type="number" value="${Amount}" />
    <c:out value="${j}" />
    <fmt:parseNumber var="j" integerOnly="true" type="number"
        value="${Amount}" />
    只输出整数部分:
    <c:out value="${j}" />
</body>
</html>

页面输出内容:

786.97
只输出整数部分: 786

JSP自定义标签

标签生命周期

自定义标签的开发及使用步骤

第一步:创建一个标签助手类(继承BodyTagSupport)。

  • 标签属性必须助手类的属性对应、且要提供对应get/set方法。

第二步:创建标签库描述文件(tld),添加自定义标签的配置。

  • 注:tld文件必须保存到WEB-INF目录或其子目录。

第三步:在JSP通过taglib指令导入标签库描述文件,并通过指定后缀访问此自定义标签。

标签助手类三个重写方法 return 后面的返回值

public class MyTag extends BodyTagSupport{
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		return super.doStartTag();
	}
	
	@Override
	public int doAfterBody() throws JspException {
		// TODO Auto-generated method stub
		return super.doAfterBody();
	}
	
	@Override
	public int doEndTag() throws JspException {
		// TODO Auto-generated method stub
		return super.doEndTag();
	}
}

doStartTag() :开始标签

(1)返回值1:SKIP_BODY 跳过标签体计算;不会计算标签体的内容。

(2)返回值2:EVAL_BODY_INCLUDE 计算标签体;全部都输出。

doAfterBody():标签体

(1)返回值1:SKIP_BODY计算一次标签体;正常运行一次。

(2)返回值2:EVAL_BODY_AGAIN 再次计算标签体;无线循环。

doEndTag():结束标签

jsp界面:

<body>
<z:print>哈哈</z:print>
我是结束标签之后的文字!!!
</body>

(1)返回值1:SKIP_PAGE 从结束标签之后,直接到</body>(跳过计算标签后续内容);不会输出结束标签之后的内容(</z:print>后面的内容)。

输出:哈哈

(2)返回值2:EVAL_PAGE 计算标签后续内容;输出结束标签之后的内容(</z:print>后面的内容)。

输出:哈哈 我是结束标签之后的文字!!!

开发自定义标签

z:print

<z:print>哈哈</z:print>

功能:输出标签体内容。

第一步:创建标签助手类继承BodyTagSupport。重写方法doStartTag()、doEndTag()、doAfterBody()。

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
 
public class PrintTag extends BodyTagSupport {
	/**
	 * 开始标签:
	 * 		  1.skip_body 跳过标签体计算
	 * 		  2.eval_body_include 计算标签体
	 * @return
	 * @throws JspException
	 */
	@Override
	public int doStartTag() throws JspException {
		System.out.println("我是开始标签!");
		return EVAL_BODY_INCLUDE;
	}
	
	/**
	 * 标签体:
	 * 		1.skip_body 
	 *      2.eval_body_again 再次计算标签体
	 * @return
	 * @throws JspException
	 */
	@Override
	public int doAfterBody() throws JspException {
		System.out.println("我是标签体!");
		return SKIP_BODY;
	}
	
	/**
	 * 结束标签:
	 * 		  1.skip_page 从结束标签之后,直接到</body>(跳过后续页面计算)
	 * 		  2.eval_page 计算标签后续内容
	 * @return
	 * @throws JspException
	 */
	@Override
	public int doEndTag() throws JspException {
		System.out.println("我是结束标签!");
		return EVAL_PAGE;
	}
}

第二步,创建标签库描述文件(tld),添加自定义标签的配置。

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<!-- 标签库的版本 -->
	<tlib-version>1.0</tlib-version>
	<!-- JSP版本 -->
	<jsp-version>1.2</jsp-version>
	<!-- 自定义标签简称(给标签库取名字)-->
	<short-name>Simple Tags</short-name>
	<!-- 自定义标签引用名 -->
	<uri>/zking</uri>
 
	<!-- print标签 -->
	<tag>
		<!-- 标签名 -->
		<name>print</name>
		<!-- 该标签对应的标签助手类(完整类名),java会通过反射自动实例化标签助手类 -->
		<tag-class>com.zking.tag.HelloTag</tag-class>
		<!-- 声明标签类型 1.jsp类型:jsp; 2.空类型:empty。 -->
		<body-content>jsp</body-content>
	</tag>
 
</taglib>

第三步,在JSP通过taglib指令导入标签库描述文件,并通过指定后缀访问此自定义标签。

(1)在JSP通过taglib指令导入标签库描述文件;

<%@taglib prefix="z" uri="/zking" %>

(2)并通过指定后缀访问此自定义标签;

<z:print>哈哈</z:print>

<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!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>
	<!-- 输出标签体内容 -->
	<z:print>哈哈</z:print>
</body>
</html>

z:out

示例:

<z:out value="呵呵"/>

功能:输出value值。

第一步:写一个标签助手类继承BodyTagSupport。重写方法doStartTag()、doEndTag()、doAfterBody()。

import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
 
public class OutTag extends BodyTagSupport {
	// 标签属性必须助手类的属性对应、且要提供对应get/set方法
	private String value;
	
	public String getValue() {
		return value;
	}
	
	public void setValue(String value) {
		this.value = value;
	}
	
	/**
	 * 开始标签
	 */
	@Override
	public int doStartTag() throws JspException {
		// TODO Auto-generated method stub
		return super.doStartTag();
	}
	
	/**
	 * 标签体
	 */
	@Override
	public int doAfterBody() throws JspException {
		// TODO Auto-generated method stub
		return super.doAfterBody();
	}
	
	/**
	 * 结束标签
	 */
	@Override
	public int doEndTag() throws JspException {
		// 将属性值写入到页面显示
		PrintWriter out = null;
		try {
			out = pageContext.getResponse().getWriter();
			// 写入值并输出
			out.print(this.value);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return super.doEndTag();
	}
	
}

因为out标签是有value属性的,所以还要在标签助手类定义一个属性,并提供对应的get/set方法。

注意:标签属性必须助手类的属性对应。

比如:标签:<z:out value="呵呵"/>,标签的属性是value,

标签助手类定义的属性也应该是value。

在结束标签方法内写入out标签的输出功能。

/**
 * 结束标签
 */
@Override
public int doEndTag() throws JspException {
    // 将属性值写入到页面显示
    PrintWriter out = null;
    try {
        out = pageContext.getResponse().getWriter();
        // 写入值并输出
        out.print(this.value);
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return super.doEndTag();
}

第二步,创建标签库描述文件(tld),添加自定义标签的配置。

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<!-- 标签库的版本 -->
	<tlib-version>1.0</tlib-version>
	<!-- JSP版本 -->
	<jsp-version>1.2</jsp-version>
	<!-- 自定义标签简称(给标签库取名字)-->
	<short-name>Simple Tags</short-name>
	<!-- 自定义标签引用名 -->
	<uri>/zking</uri>
	<!-- out标签 -->
	<tag>
        <!-- 标签名 -->
		<name>out</name>
		<!-- 该标签对应的标签助手类 -->
		<tag-class>com.zking.tag.OutTag</tag-class>
		<!-- 声明标签类型 1.jsp类型:jsp; 2.空类型:empty。 -->
		<body-content>empty</body-content>
		
		<!-- 声明属性 -->
		<attribute>
			<!-- 属性名 -->
			<name>value</name>
			<!-- 该属性是否必写 -->
			<required>true</required>
			<!-- 是否接收表达式传值 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
 
</taglib>

这里要加入属性定义声明。

<!-- 声明属性 -->
<attribute>
	<!-- 属性名 -->
	<name>value</name>
	<!-- 该属性是否必写 -->
	<required>true</required>
	<!-- 是否接收表达式传值 -->
	<rtexprvalue>true</rtexprvalue>
</attribute>

第三步,在JSP通过taglib指令导入标签库描述文件,并通过指定后缀访问此自定义标签。

<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!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>
	<!-- 输出value值 -->
	<z:out value="呵呵"/>
</body>
</html>

z:if

示例:

<z:if test="ture">
        哈哈呵呵
</z:if>

功能:如果条件为true,就运行标签体内容;为false,就不运行标签体内容。

第一步:写一个标签助手类继承BodyTagSupport。重写方法doStartTag()、doEndTag()、doAfterBody()。

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
 
public class IfTag extends BodyTagSupport {
	private boolean test;
	
	public boolean isTest() {
		return test;
	}
 
	public void setTest(boolean test) {
		this.test = test;
	}
	
	/**
	 * 开始标签
	 */
	@Override
	public int doStartTag() throws JspException {
		if(this.test)
			return EVAL_BODY_INCLUDE;// 计算标签体
		else
			return SKIP_BODY;// 跳过标签体计算
	}
	
	/**
	 * 标签体
	 */
	@Override
	public int doAfterBody() throws JspException {
		// TODO Auto-generated method stub
		return super.doAfterBody();
	}
	
	/**
	 * 结束标签
	 */
	@Override
	public int doEndTag() throws JspException {
		// TODO Auto-generated method stub
		return super.doEndTag();
	}
	
}

在开始标签方法写入if标签的功能。

/**
 * 开始标签
 */
@Override
public int doStartTag() throws JspException {
    if(this.test)
        return EVAL_BODY_INCLUDE;// 计算标签体
    else
        return SKIP_BODY;// 跳过标签体计算
}

第二步,创建标签库描述文件(tld),添加自定义标签的配置。

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<!-- 标签库的版本 -->
	<tlib-version>1.0</tlib-version>
	<!-- JSP版本 -->
	<jsp-version>1.2</jsp-version>
	<!-- 自定义标签简称(给标签库取名字)-->
	<short-name>Simple Tags</short-name>
	<!-- 自定义标签引用名 -->
	<uri>/zking</uri>
 
	<!-- if标签 -->
	<tag>
		<!-- 标签名 -->
		<name>if</name>
		<!-- 该标签对应的标签助手类 -->
		<tag-class>com.zking.tag.IfTag</tag-class>
		<!-- 声明标签类型 1.jsp类型:jsp; 2.空类型:empty。 -->
		<body-content>jsp</body-content>
		
		<!-- 定义属性 -->
		<attribute>
			<!-- 属性名 -->
			<name>test</name>
			<!-- 该属性是否必写 -->
			<required>true</required>
			<!-- 是否接收表达式传值 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
	</tag>
 
</taglib>

第三步,在JSP通过taglib指令导入标签库描述文件,并通过指定后缀访问此自定义标签。

<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!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>
	<!-- 如果条件为true,就输出标签体内容;为false,就跳过标签体,不输出标签体内容。 -->
	<z:if test="ture">
		哈哈呵呵
	</z:if>
</body>
</html>

z:foreach

示例:

<z:foreach test="${myl }" items="a">
    ${a }<br/>
</z:foreach>

功能:遍历集合里面的数据。

第一步:写一个标签助手类继承BodyTagSupport。重写方法doStartTag()、doEndTag()、doAfterBody()。

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.List;
 
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.BodyTagSupport;
 
public class ForeachTag extends BodyTagSupport{
	private List test;
	private String items;
	
	public String getItems() {
		return items;
	}
 
	public void setItems(String items) {
		this.items = items;
	}
	
	public List getTest() {
		return test;
	}
 
	public void setTest(List test) {
		this.test = test;
	}
	
	/**
	 * 开始标签
	 */
	@Override
	public int doStartTag() throws JspException {
		PrintWriter out = null;
		try {
			out = pageContext.getResponse().getWriter();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// 如果得到的属性值为空,那么就不循环直接跳过标签体
		if(null == this.test) {
			return SKIP_BODY;
		}else {
			// 将值取出来
			Iterator myi = test.iterator();
			if(myi.hasNext()) {
				pageContext.getRequest().setAttribute(this.items, myi.next());
				
				// 先在开始标签输出一次值
//				out.write(myi.next().toString());
				pageContext.getRequest().setAttribute("myi", myi);
			}
			return EVAL_BODY_INCLUDE;
		}
	}
	
	/**
	 * 标签体
	 */
	@Override
	public int doAfterBody() throws JspException {
		Iterator myi =  (Iterator) pageContext.getRequest().getAttribute("myi");
		if(myi.hasNext()) {
			pageContext.getRequest().setAttribute(this.items, myi.next());
			return EVAL_BODY_AGAIN;
		}else {
			return SKIP_BODY;
		}
	}
	
	/**
	 * 结束标签
	 */
	@Override
	public int doEndTag() throws JspException {
		return super.doEndTag();
	}
	
}

在开始标签方法和标签体方法写入foreach标签的功能。

/**
 * 开始标签
 */
@Override
public int doStartTag() throws JspException {
    PrintWriter out = null;
    try {
        out = pageContext.getResponse().getWriter();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    // 如果得到的属性值为空,那么就不循环直接跳过标签体
    if(null == this.test) {
        return SKIP_BODY;
    }else {
        // 将值取出来
        Iterator myi = test.iterator();
        if(myi.hasNext()) {
            pageContext.getRequest().setAttribute(this.items, myi.next());

            // 先在开始标签输出一次值
            //				out.write(myi.next().toString());
            pageContext.getRequest().setAttribute("myi", myi);
        }
        return EVAL_BODY_INCLUDE;
    }
}

/**
 * 标签体
 */
@Override
public int doAfterBody() throws JspException {
    Iterator myi =  (Iterator) pageContext.getRequest().getAttribute("myi");
    if(myi.hasNext()) {
        pageContext.getRequest().setAttribute(this.items, myi.next());
        return EVAL_BODY_AGAIN;
    }else {
        return SKIP_BODY;
    }
}

第二步,创建标签库描述文件(tld),添加自定义标签的配置。

<!DOCTYPE taglib
  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
   "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<!-- 标签库描述符 -->
<taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
	<!-- 标签库的版本 -->
	<tlib-version>1.0</tlib-version>
	<!-- JSP版本 -->
	<jsp-version>1.2</jsp-version>
	<!-- 自定义标签简称(给标签库取名字)-->
	<short-name>Simple Tags</short-name>
	<!-- 自定义标签引用名 -->
	<uri>/zking</uri>
 
	<!-- foreach标签 -->
	<tag>
		<!-- 标签名 -->
		<name>foreach</name>
		<!-- 该标签对应的标签助手类 -->
		<tag-class>com.zking.tag.ForeachTag</tag-class>
		<!-- 声明标签类型 1.jsp类型:jsp; 2.空类型:empty。 -->
		<body-content>jsp</body-content>
		
		<!-- 定义属性1 -->
		<attribute>
			<!-- 属性名 -->
			<name>test</name>
			<!-- 该标签是否必写 -->
			<required>true</required>
			<!-- 是否接收表达式传值 -->
			<rtexprvalue>true</rtexprvalue>
		</attribute>
		<!-- 定义属性2 -->
		<attribute>
			<!-- 属性名 -->
			<name>items</name>
			<!-- 该属性是否必写 -->
			<required>false</required>
			<!-- 是否接收表达式传值 -->
			<rtexprvalue>false</rtexprvalue>
		</attribute>
	</tag>
 
</taglib>

第三步,在JSP通过taglib指令导入标签库描述文件,并通过指定后缀访问此自定义标签。

<%@page import="java.util.List"%>
<%@page import="java.util.ArrayList"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="z" uri="/zking" %>
<!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>
    <%
		List list = new ArrayList();
		for(int i = 1; i <= 100; i++){
			list.add("傻蛋: " + i + "号");
		}
		session.setAttribute("myl", list);
	%>
<body>
	<z:foreach test="${myl }" items="a">
	    ${a }<br/>
	</z:foreach>
</body>
</html>

 

posted @ 2022-09-30 17:49  残城碎梦  阅读(294)  评论(0编辑  收藏  举报