跳转到内容

Jakarta EE 编程/Jakarta Server Pages 语法

75% developed
来自 Wikibooks,开放世界的开放书籍

The JavaServer Pages 是一种技术,用于使用 Java servlet 容器将动态内容插入 HTMLXML 页面。换句话说,您可以发送 HTML 页面到 web 客户端,这些页面对于每个客户端来说都是不同的,并且每次他们收到它时都是不同的(例如使用数据库数据)。为了解释它,让我们在一个 web 服务器上有一个简单的 HTML 页面。数据流很简单。简而言之,客户端通过发送页面 URL 来请求一个 HTML 页面,服务器返回给定的 HTML 页面

客户端   服务器
         
  URL  
 
 
      检索

文件
 
 
 
 
         
         

以下是 HTML 文件和客户端上的显示

HTML 页面 显示
<html>
  <body>
    <span style="color: blue;">The current time</span>
  </body>
</html>
当前时间

返回的页面始终相同。现在我们想要显示当前时间。为此,我们在 web 服务器上添加了一个 servlet 容器。HTML 页面将不再存在于服务器上。当页面被客户端请求时,一个 Java 类将生成 HTML 页面,并且 HTML 页面将被发送到客户端。这个 Java 类被称为一个 *servlet*

客户端   Web 服务器   Servlet 容器
               
  URL        
 
       
      询问

生成

文件
 
        执行

Java
 
     
 
 
       
 
       
               
               

以下是 Java 类、生成的 HTML 文件和客户端上的显示

Servlet HTML 页面 显示
package jsp_servlet;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
    public void _jspService(javax.servlet.http.HttpServletRequest request,
    javax.servlet.http.HttpServletResponse response)
    throws javax.servlet.ServletException,
    java.io.IOException {
        javax.servlet.ServletConfig config = ; // Get the servlet config
        Object page = this;
        PageContext pageContext = ;            // Get the page context for this request
        javax.servlet.jsp.JspWriter out = pageContext.getOut();
        HttpSession session = request.getSession(true);
        try {
            out.print("<html>\r\n");
            out.print("<body>\r\n");
            out.print("<span style="color: blue;">");

            DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            Calendar cal = Calendar.getInstance();
            out.print(dateFormat.format(cal.getTime()));

            out.print("</span>\r\n");
            out.print( "</body>\r\n" );
            out.print( "</html>\r\n" );
            
        } catch (Exception _exception) {
            // Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
        }
    }
}
<html>

  <body>
    <span style="color: blue;">2024/10/24 14:47:26</span>
  </body>
</html>

2024/10/24 14:47:26

现在页面是动态的,但 servlet 很难编码和阅读。因此,我们将使用一个 *JavaServer Page* (JSP)。JSP 写起来像 HTML 页面一样。它具有用于 HTML 页面的文件扩展名 `。jsp` 和用于 XML 标记页面的文件扩展名 `。jspx`。除了 HTML 语法之外,它还嵌入了 *脚本片段* 和 *jsp 标签*。脚本片段是 Java 代码的一部分。servlet 容器从 JSP 生成一个 servlet,该 servlet 将用于生成 HTML 页面或 XML 内容。HTML 标记将保持原样。嵌入的脚本片段被插入到 servlet 的代码中。jsp 标签被转换为 Java 代码。因此,让我们使用 JSP

客户端   Web 服务器   Servlet 容器
               
  URL        
 
       
      询问

生成

文件
 
        生成

servlet
           
        执行

Java
     
 
 
       
 
       
               
               

实际上,servlet 仅在第一次请求调用时由 JSP 生成。之后会重复使用相同的 servlet。当 JSP 发生变化时,它会被再次生成。现在我们有一个 JSP,它生成一个 servlet,它生成一个 HTML 页面,该页面将显示给客户端

JSP Servlet HTML 页面 显示
<%@ page errorPage="myerror.jsp" %>
<%@ page import="java.text.DateFormat" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page import="java.util.Calendar" %>

<html>
  <body>
<% DateFormat dateFormat =
              new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            Calendar cal = Calendar.getInstance();
%>
    <span style="color: blue;">
      <%= dateFormat.format(cal.getTime()) %>
    </span>
  </body>
</html>
package jsp_servlet;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

class _myservlet implements javax.servlet.Servlet,
  javax.servlet.jsp.HttpJspPage {
    public void _jspService(
    javax.servlet.http.HttpServletRequest request,
    javax.servlet.http.HttpServletResponse response)
    throws javax.servlet.ServletException,
    java.io.IOException {
        javax.servlet.ServletConfig config = ;
        Object page = this;
        PageContext pageContext = ;
        javax.servlet.jsp.JspWriter out =
          pageContext.getOut();
        HttpSession session = request.getSession(true);
        try {
            DateFormat dateFormat =
              new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            Calendar cal = Calendar.getInstance();

            out.print("<html>\r\n");
            out.print("<body>\r\n");
            out.print("<span style="color: blue;">\r\n");
            out.print(dateFormat.format(cal.getTime()));
            out.print("\r\n");
            out.print("</span>\r\n");
            out.print( "</body>\r\n" );
            out.print( "</html>\r\n" );
            
        } catch (Exception _exception) {
        }
    }
}
<html>

  <body>
    <span style="color: blue;">2024/10/24 14:47:26</span>
  </body>
</html>

2024/10/24 14:47:26

脚本元素

[编辑 | 编辑源代码]

有 *三种* 基本的脚本元素类型,允许将 java 代码直接插入到 JSP 中。

  • 一个 *声明* 标签将变量定义放在 java servlet 类的主体内部。也可以定义静态数据成员。还可以在这里定义内部类。
<%! int serverInstanceVariable = 1; %>

声明标签还允许定义方法。

<%!
   /**
    * Converts the Object into a string or if
    * the Object is null, it returns the empty string.
    */
   public String toStringOrBlank(Object obj) {
       if (obj != null) {
           return obj.toString();
       }
       return "";
   }
%>
  • 一个 *脚本片段* 标签将它包含的所有语句放在 java servlet 类中 `_jspService()` 方法的内部。
<% int localStackBasedVariable = 1;
out.println(localStackBasedVariable); %>
  • 一个 *表达式* 标签将要计算的表达式放在 java servlet 类的内部。表达式不应以分号结尾。
<%= "expanded inline data " + 1 %>
  • 一个 *注释* 标签什么也不做。它被忽略了。它允许您对文件进行文档化。它与 HTML 注释不同,因为 HTML 注释 (<!-- -->) 将出现在生成的 HTML 文件中。
<%-- This is my first JSP. --%>

JSP 指令添加到 JSP 页面的顶部。这些指令控制 JSP 编译器如何生成 servlet。以下指令可用

包含
include 指令通知 JSP 编译器将一个完整的文件包含到当前文件中。就像将包含文件的内容直接粘贴到原始文件中一样。此功能类似于 C 预处理器提供的功能。包含的文件通常具有扩展名“jspf”(代表 JSP **片段**)
<%@ include file="somefile.jspf" %> 
页面
page 指令具有几个属性
导入 导致在结果文件中插入一个 Java `import` 语句。
contentType 指定生成的內容。如果未使用 HTML 或字符集不是默认字符集,则应使用它。
errorPage 指示在处理 HTTP 请求时发生异常时应该显示的页面的地址。
isErrorPage 如果设置为 true,则表示这是错误页面。默认值为 *false*。
isThreadSafe 如果结果 servlet 必须是线程安全的,则为 true。
autoFlush 自动刷新内容。值为 true(默认值)表示缓冲区已满时应刷新缓冲区。值为 false(很少使用)表示缓冲区溢出时应抛出异常。当也使用 buffer="none" 时,值为 false 是非法的。
会话 为了维护会话。值为 true(默认值)表示预定义变量 session(类型为 `HttpSession`)应绑定到现有会话(如果存在),否则应创建一个新会话并将其绑定到它。值为 false 表示不使用任何会话,尝试访问变量 session 将导致在 JSP 页面被翻译成 servlet 时出现错误。
缓冲区 设置缓冲区大小。默认值为 8k,建议您增加它。
isELIgnored 定义当 JSP 被翻译时是否忽略 表达式语言 (EL) 表达式。
语言 定义脚本片段、表达式和声明中使用的脚本语言。目前,唯一可能的值是“java”。
扩展 定义此 JSP 将成为的类的超类。除非您 *确实* 知道自己在做什么,否则不要使用它 - 它会覆盖容器提供的类层次结构。
信息 定义一个字符串,该字符串被放入翻译后的页面中,以便您可以使用生成的 servlet 的继承的 `getServletInfo()` 方法获取它。
pageEncoding 定义 JSP 的字符编码。默认值为“ISO-8859-1”(除非 contentType 属性已定义字符编码,或者页面使用 XML 文档语法)。
<%@ page import="java.util.*" %> <%-- example import --%>
<%@ page contentType="text/html" %> <%-- example contentType --%>
<%@ page isErrorPage="false" %> <%-- example for non error page --%>
<%@ page isThreadSafe="true" %> <%-- example for a thread safe JSP --%>
<%@ page session="true" %> <%-- example for using session binding --%>
<%@ page autoFlush="true" %> <%-- example for setting autoFlush --%>
<%@ page buffer="20kb" %> <%-- example for setting Buffer Size --%>
**注意:** 只有“import”页面指令可以在同一个 JSP 中多次使用。
taglib
taglib 指令表示要使用 JSP 标签库。该指令需要一个前缀(类似于 C++ 中的命名空间)和标签库描述的 URI。
<%@ taglib prefix="myprefix" uri="taglib/mytag.tld" %>

无论 JSP 编译器是否为 servlet 生成 Java 源代码或直接发出字节码,了解 JSP 编译器如何将页面转换为 Java servlet 都是有帮助的。例如,考虑以下输入 JSP 及其生成的 Java Servlet。

输入 JSP 生成的 servlet
<%@ page errorPage="myerror.jsp" %>
<%@ page import="com.foo.bar" %>
 
<html>
  <head>
  <%! int serverInstanceVariable = 1;%>
 
  <% int localStackBasedVariable = 1; %>
  </head>

  <body>
    <table>
      <%-- This will be ignored. --%>
      <tr><td><%= toStringOrBlank( "expanded inline data " + 1 ) %></td></tr>
    </table>
  </body>
</html>
 package jsp_servlet;
 import java.util.*;
 import java.io.*;
 import javax.servlet.*;
 import javax.servlet.http.*;
 import javax.servlet.jsp.*;
 import javax.servlet.jsp.tagext.*;

 import com.foo.bar; // Imported as a result of <%@ page import="com.foo.bar" %>
 import 

 class _myservlet implements javax.servlet.Servlet, javax.servlet.jsp.HttpJspPage {
    // Inserted as a
    // result of <%! int serverInstanceVariable = 1;%>
    int serverInstanceVariable = 1;
    

    public void _jspService(javax.servlet.http.HttpServletRequest request,
    javax.servlet.http.HttpServletResponse response)
    throws javax.servlet.ServletException,
    java.io.IOException {
        javax.servlet.ServletConfig config = ; // Get the servlet config
        Object page = this;
        PageContext pageContext = ;            // Get the page context for this request
        javax.servlet.jsp.JspWriter out = pageContext.getOut();
        HttpSession session = request.getSession(true);
        try {
            out.print("<html>\r\n");
            out.print("<head>\r\n");
            
            // From <% int localStackBasedVariable = 1; %>
            int localStackBasedVariable = 1;
            
            out.print("</head>\r\n");
            out.print("<body>\r\n");
            out.print("<table>\r\n");
            out.print(" <tr><td>");
            // From <%= toStringOrBlank( "expanded inline data " + 1) %>
            out.print(toStringOrBlank( "expanded inline data " + 1 ));
            out.print(" </td></tr>\r\n");
            out.print("</table>\r\n");
            out.print("</body>\r\n");
            out.print("</html>\r\n");
            
        } catch (Exception _exception) {
            // Clean up and redirect to error page in <%@ page errorPage="myerror.jsp" %>
        }
    }
 }

隐式对象

[编辑 | 编辑源代码]

我们已经看到,我们可以在脚本中声明对象,供以后使用。还有一些已经声明的对象可供程序员使用。它们被称为*隐式对象*。

out 用于将数据写入响应流的JspWriter
页面 servlet 本身。
pageContext 包含与整个页面相关联的数据的PageContext 实例。给定的 HTML 页面可以在多个 JSP 之间传递。
request 提供 HTTP 请求信息的HttpServletRequest 对象。
response 可用于将数据发送回客户端的HttpServletResponse 对象。
会话 可用于跟踪从一个请求到另一个请求的用户信息的HttpSession 对象。
config 提供 servlet 配置数据。
application 应用程序中所有 JSP 和 servlet 共享的数据。
exception 应用程序代码未捕获的异常。

JSP 操作

[编辑 | 编辑源代码]

JSP 操作是 XML 标记,用于调用内置的 Web 服务器功能。它们在运行时执行。有些是标准的,有些是自定义的(由 Java 开发人员开发)。以下是标准操作。

jsp:include

[编辑 | 编辑源代码]

与 include 指令类似,此标记将指定的 jsp 包含到返回的 HTML 页面中,但其工作原理不同。Java servlet 会暂时将请求和响应传递给指定的 JavaServer 页面。一旦另一个 JSP 完成,控制权将返回到当前 JSP。使用此方法,JSP 代码将在多个其他 JSP 之间共享,而不是重复。

<html>
  <head></head>
  <body>
    <jsp:include page="mycommon.jsp" >
      <jsp:param name="extraparam" value="myvalue" />
    </jsp:include>
    name:<%=request.getParameter("extraparam")%>
  </body>
</html>

jsp:param

[编辑 | 编辑源代码]

可以在jsp:includejsp:forwardjsp:params 块中使用。指定将添加到请求当前参数的参数。

jsp:forward

[编辑 | 编辑源代码]

用于将请求和响应传递给另一个 JSP 或 servlet。控制权永远不会返回到当前 JSP。

<jsp:forward page="subpage.jsp" >
  <jsp:param name="forwardedFrom" value="this.jsp" />
</jsp:forward>

在此转发示例中,请求将转发到subpage.jsp

jsp:plugin

[编辑 | 编辑源代码]

旧版本的 Netscape Navigator 和 Internet Explorer 使用不同的标记来嵌入小程序。此操作生成浏览器特定的标记,用于包含小程序。插件示例说明了在网页中嵌入小程序的 HTML 统一方式。在<OBJECT> 标记出现之前,没有通用的嵌入小程序的方式。

<jsp:plugin type=applet height="100%" width="100%"
   archive="myjarfile.jar, myotherjar.jar"
   codebase="/applets"
   code="com.foo.MyApplet" >
   <jsp:params>
     <jsp:param name="enableDebug" value="true" />
   </jsp:params>
   <jsp:fallback>
     Your browser does not support applets.
   </jsp:fallback>
</jsp:plugin>

目前,jsp:plugin 标记不允许动态调用小程序。例如,jsp:params 不能与需要将数据点作为参数传递的图表小程序一起使用,除非数据点的数量是恒定的。例如,您不能遍历 ResultSet 来创建jsp:param 标记。每个jsp:param 标记都必须手动编码。

jsp:fallback

[编辑 | 编辑源代码]

如果浏览器不支持小程序,则显示的内容。

jsp:getProperty

[编辑 | 编辑源代码]

从指定的 JavaBean 获取属性。

JSP 标记库

[编辑 | 编辑源代码]

除了预定义的 JSP 操作之外,开发人员还可以使用 JSP 标记扩展 API 添加自己的自定义*操作*。开发人员编写一个 Java 类,该类实现其中一个 Tag 接口,并提供一个标记库 XML 描述文件,该文件指定标记和实现这些标记的 java 类。

考虑以下 JSP。

<%@ taglib uri="mytaglib.tld" prefix="myprefix" %>
…
<myprefix:myaction> <%-- The start tag --%>
…
</myprefix:myaction> <%-- The end tag --%>
…

JSP 编译器将加载 mytaglib.tld XML 文件。

<taglib>
  <tlib-version>1.0</tlib-version>
  <jsp-version>2.0</jsp-version>
  <short-name>My taglib</short-name>
  <tag>
    <name>myaction</name>
    <tag-class>org.wikibooks.en.MyActionTag</tag-class>
    <body-content>empty</body-content>
  </tag>
</taglib>

JSP 编译器将看到标记myaction 由 java 类MyActionTag 实现。

public class MyActionTag extends TagSupport {
   public MyActionTag() {  }

     // Releases all instance variables.
   public void release() {  }

   // Called for the start tag
   public int doStartTag() {  }

   // Called at the end tag
   public int doEndTag() {  }
}

标记在文件中首次使用时,它将创建一个MyActionTag 实例。然后(以及每次使用该标记时),当它遇到开始标记时,它将调用方法doStartTag()。它查看开始标记的结果,并确定如何处理标记的主体。主体是开始标记和结束标记之间的文本。doStartTag() 方法可以返回以下值之一

SKIP_BODY 不会处理标记之间的主体。
EVAL_BODY_INCLUDE 评估标记的主体。
EVAL_BODY_TAG 评估标记的主体并将结果推送到流中(存储在标记的主体内容属性中)。


Clipboard

待办事项
添加主体标记描述。


注意:如果标记扩展了BodyTagSupport 类,则在调用doEndTag() 之前,将在主体处理完成后调用方法doAfterBody()。此方法用于实现循环结构。

当它遇到结束标记时,它将调用doEndTag() 方法。该方法可以返回以下两个值之一

EVAL_PAGE 这表示应处理 JSP 文件的其余部分。
SKIP_PAGE 这表示不应进行任何进一步的处理。控制权离开 JSP 页面。这就是用于转发操作的方法。

如果要迭代主体几次,则 Java 类(标记处理程序)必须实现IterationTag 接口。它返回EVAL_BODY_AGAIN - 表示再次调用主体。

JSP 标准标记库 (JSTL)

[编辑 | 编辑源代码]

JavaServer Pages 标准标记库 (JSTL) 是 Java EE Web 应用程序开发平台的一个组件。它通过添加 JSP 标记的标记库来扩展 JSP 规范,这些标记库用于常见任务,例如 XML 数据处理、条件执行、循环和国际化。

Struts 标记库

[编辑 | 编辑源代码]

Struts 项目包含Struts 标记库,其中大部分在独立于 Struts 架构的情况下很有用。

国际化

[编辑 | 编辑源代码]

JSP 中的国际化与普通 Java 应用程序中的国际化方式相同,即使用资源束。

表达式语言

[edit | edit source]

表达式语言 (EL) 是一个更快/更简单的显示参数值的方式。它自 JSP 2.0 起可用。例如,它允许开发者创建 Velocity 风格的模板。

Hello, ${param.visitor}

与以下代码相同

Hello, <%=request.getParameter("visitor")%>

它还提供了一种更清晰的方式来遍历嵌套 bean。考虑一些 bean

class Person {
   String name;
   // Person nests an organization bean.
   Organization organization;

   public String getName() { return this.name; }
   public Organization getOrganization() { return this.organization; }
}
class Organization {
   String name;
   public String getName() { return this.name; }
}

然后,如果一个 Person 实例要被放置到一个名为 "person" 的请求属性中,JSP 将有

 Hello, ${person.name}, of company ${person.organization.name}

与以下代码相同。

 Hello,
<% Person p = (Person) request.getAttribute("person");
   if (p != null) {
       out.print(p.getName());
   }
%>, of company
<% if ((p != null) && (p.getOrganization() != null)) {
       out.print(p.getOrganization().getName());
   }
%>

Java EE 5 平台中的 JSP 技术

[edit | edit source]

Java EE 5 的重点是通过使用 Java 语言注解来简化开发,这些注解是在 J2SE 5.0 中引入的。JSP 2.1 通过定义用于 JSP 标签处理程序和上下文监听器的依赖注入的注解来支持这个目标。

Java EE 5 规范的另一个关键关注点是使其 Web 层技术(即 JavaServer Pages (JSP)、JavaServer Faces (JSF) 和 JavaServer Pages Standard Tag Library (JSTL))保持一致。

这种对齐工作的成果是统一表达式语言 (EL),它集成了 JSP 2.0 和 JSF 1.1 定义的表达式语言。

统一 EL 中从对齐工作中产生的主要关键补充是:一个可插拔的 API,用于将变量引用解析为 Java 对象,以及用于解析应用于这些 Java 对象的属性;对延迟表达式的支持,这些表达式可以在需要时由标签处理程序进行评估,不同于它们的常规表达式对应物,这些表达式在页面执行和渲染时立即进行评估;对 lvalue 表达式的支持,这些表达式出现在赋值操作的左侧。当用作 lvalue 时,EL 表达式代表对数据结构(例如:JavaBeans 属性)的引用,该数据结构被分配了一些用户输入。统一 EL 在其自身的规范文档中定义,该文档与 JSP 2.1 规范一起提供。

由于统一 EL 的存在,JSTL 标签(例如 JSTL 迭代标签)可以以直观的方式与 JSF 组件一起使用。

JSP 2.1 利用 Servlet 2.5 规范来实现其 Web 语义。

模型-视图-控制器模式

[edit | edit source]
JSP 模型 2 架构。

模型-视图-控制器模式可以与 JSP 文件一起使用,以便将表示与请求处理和计算机数据存储分离。使用常规 servlet 或单独的 JSP 文件来处理请求。请求处理完成后,控制权将传递给仅用于创建输出的 JSP。有几个基于模型-视图-控制器模式的平台用于 Web 层(如 Barracuda、Apache Struts、Stripes 和 Spring MVC 框架)。

环境

[edit | edit source]

Java 服务器 (J2EE 规范) 和页面脚本和/或添加的扩展定制编程都通过 (在被加载的程序的运行时上下文中使用) 一个称为虚拟机的预安装基础程序与主机操作系统集成,这种类型是 Java 虚拟机 (JVM)。

因为无论是编译器-JVM 集 (称为 SDK 或 JDK) 还是独立的 JVM (称为 JRE,Java 运行时环境) 都是为大多数计算机平台操作系统而制作的,并且为 JVM 编译的程序被编译成 JVM 的特殊 Java 字节码文件,字节码文件 (编译的 Java 程序 .class 文件) 可以有效地在平台之间传输,无需重新编译,除非版本兼容性或特殊情况。这些 J2EE servlet 或 J2EE JSP 程序的源代码几乎总是与 J2EE JSP 材料和 J2EE Web 应用程序一起提供,因为服务器在加载它们时必须调用编译器。这些小的扩展程序 (自定义标签、servlet、bean、页面脚本) 是可变的,并且很可能在运行时之前或间歇性地更新或更改,但特别是在发送 JSP 页面请求时,它需要 JSP 服务器访问 Java 编译器 (SDK 或 JDK) 和所需的源代码 (不仅仅是 JVM JRE 和字节码类文件) 才能成功利用服务方法。

JSP 语法有两种基本形式,脚本标记,尽管从根本上说,页面是 HTML 或 XML 标记。脚本标记 (称为脚本元素) (分隔) 代码块 与标记不是有效的标记,并且允许任何与 java 服务器相关的 API (例如,运行的二进制文件本身或数据库连接 API 或 java 邮件 API) 或更专业的 JSP API 语言代码被嵌入到 HTML 或 XML 页面中,前提是 JSP 文件中使用了正确的声明,并且使用了页面的文件扩展名。脚本块不需要在块本身内完成,只需要块本身的最后一行在语法上正确地作为语句完成,它可以在后面的块中完成。这种分割的内联代码部分系统被称为跨越脚本,因为它可以通过跨越静态标记来进行包装。在运行时 (在客户端请求期间),代码被编译和评估,但代码的编译通常只发生在对文件代码进行更改时。


Clipboard

待办事项
添加代码和示例。

华夏公益教科书