JSP

JSP

JSP到底是什么

JSP(Java Server Pages)是一种动态网页开发技术。JSP 文件就是在传统的 HTML文件 中插入 Java代码JSP标签,后缀名为.jsp

JSP 与 PHP、ASP、ASP.NET 等语言类似,都运行在服务端。通常返回给客户端的就是一个 HTML 文件,因此只要有浏览器就能查看 JSP 页面。

JSP 使用 JSP 标签在 HTML 网页中插入 Java 代码,<%         //Java代码         %>,标签常以<%开头,%>结束。JSP 标签有多种功能,比如访问数据库和 JavaBean 组件等,还可以在不同的网页之间传递和共享信息。

JSP 是 Servlet 的扩展,我们可以在 JSP 中使用 Servlet 的所有功能。另外,JSP 还提供了一些其他功能,例如 EL 表达式、自定义标签等。

JSP 依赖于 Servlet,用户访问 JSP 页面时,JSP 代码会被翻译成 Servlet 代码,最终,以字符串的形式向外输出 HTML 代码。所以,JSP 只是在 Servlet 的基础上做了进一步封装

JSP 通过表单获取用户输入的数据访问数据库或其它数据源生成动态的 Web 内容。

JSP 具有以下特点:
JSP 具有 Servlet 的所有优点,例如 JSP 文件跨平台,即一次编写,处处运行。
JSP 比 CGI 性能更加优越,因为 JSP 可以直接在 HTML 中嵌入标签,而 CGI 需要单独引用 CGI 文件。
JSP 比 Servlet 更易于维护,JSP 将业务逻辑与网页设计分离,使其更加灵活。
使用 JSP,Web 开发人员可以更注重于网页设计,Java 开发人员可以更注重于逻辑处理。

JSP的由来
1997 年,Sun 公司推出了 Servlet。由于当时的 Java Web 层体系结构中只有 Servlet,所以接收用户请求、处理业务逻辑、生成 HTML 代码等都在 Servlet 中完成。Servlet 不能像 PHP、ASP 等语言镶嵌 HTML 代码,因此输出 HTML 比较困难,且部署过程复杂。

为了克服 Servlet 的这些弱点,1999 年初,Sun 公司推出了 JSP。在生成 HTML 代码方面 JSP 比 Servlet 方便许多,而且不需要特殊部署,只需要复制到服务器下面即可运行。简而言之,JSP 就是为了简化 Servlet 工作而出现的替代品。

JSP 包括很多技术,包括 Java Bean、自定义标签(Custom Tags)、EL 表达式(Expression Language)、JSTL 标准标签类库(Java Standard Tag Library)等。这些强大成熟的技术使得 JSP 在视图层(View)具有很大的优势。

Servlet与JSP异同点
相同点:与 Servlet 一样,JSP 也用于生成动态网页。

不同点如下:

序号 Servlet JSP
1 Servlet 在 Java 内添加 HTML 代码 JSP 在 HTML 内添加 Java 代码
2 Servlet 是一个 Java 程序,支持 HTML 标签 JSP 是一种 HTML 代码,支持 Java 语句
3 Servlet 一般用于开发程序的业务层(做一些复杂的逻辑处理) JSP 一般用于开发程序的表示层(显示数据)
4 Servlet 由 Java 开发人员创建和维护 JSP 常用于页面设计,由 Web 开发人员使用

JSP相对于Servlet的优点
1)易于维护
相对于 Servlet 来说,JSP 更易于管理。在 JSP 中,我们可以轻松地将业务逻辑与网页设计分开,而在 Servlet 技术中,它们是混合在一起的。
2)快速开发:无需重新编译和部署
JSP 页面被修改后,不需要重新编译和部署项目。而 Servlet 被修改后,需要重新编译和部署。
3)代码简洁
在 JSP 中,我们可以使用 EL表达式、JSTL标准标签类库、自定义标签、隐式对象等,能够有效的减少代码。

JSP生命周期

JSP 生命周期定义了 JSP 从创建到销毁的整个过程。这类似于 Servlet 生命周期,不同的是,JSP 需要先被编译成 Servlet。

JSP 生命周期阶段如下:
1.编译
2.初始化(容器调用 jspInit() 方法)
3.执行(容器调用 _jspService() 方法)
4.销毁(容器调用 jspDestroy() 方法)

1. JSP编译
当浏览器请求 JSP 时,JSP 容器会首先检查是否需要编译页面。如果该页面从未被编译过,或者自上次编译以来对其进行了修改,则编译该页面。
JSP 本质是 Servlet,因此 JSP 容器就是 Servlet 容器。

    编译过程包括 3 个步骤:

  • 解析 JSP:JSP 容器解析 JSP 文件,查看是否有语法错误
  • 翻译 JSP:JSP 容器把 JSP 文件翻译为 Servlet 类
  • 编译 Servlet

2. JSP初始化
当容器加载 JSP 时,它将在处理任何请求之前调用 jspInit() 方法。如果您需要自定义 JSP 的初始化内容,可以重写 jspInit() 方法。一般情况下,程序只初始化一次。与 Servlet init 方法一样,我们通常在 jspInit() 方法中初始化数据库连接、打开文件。

public void jspInit(){
    ...初始化数据库连接、打开文件
}

3. JSP执行
此阶段表示了 JSP 生命周期中所有与请求相关的交互行为,直到被销毁。JSP 页面完成初始化后,JSP 将会调用 _jspService() 方法。JSP 的 _jspService() 方法根据 request 来生成相应的 response,并且它还负责生成对 HTTP 方法的响应,即 GET、POST、DELETE 等。_jspService() 以 HttpServletRequest 对象和 HttpServletResponse 对象作为方法参数,如下所示:

void _jspService(HttpServletRequest request, HttpServletResponse response) {
    ...根据 request 来生成相应的 response
}

当用户通过浏览器访问Tomcat上的JSP页面时,Web应用服务器使用JSP引擎对该页面进行翻译(Java文件)、编译(.class文件)和执行,然后将执行结果(静态页面代码)返回给客户端浏览器,最后浏览器解释执行返回的结果呈现给用户。 如下如所示:

  • 翻译阶段:当Web应用服务器第一次接受到该JSP页面请求时,首先会对这个JSP页面进行翻译,将页面中的jsp代码翻译成Java源代码。
  • 编译阶段:我们知道Java源代码是不可运行的,所以Web应用服务器会将Java源文件编译成可执行的字节码文件(也就是.class文件),并加载到内存中。
  • 执行阶段:Web应用服务器会执行编译后的Java字节码文件,得到请求处理的结果,并把生成的结果反馈给客户端浏览器。
  • 注意:当某个JSP页面第一次被访问时,它会被翻译、编译与执行;当再次访问该JSP页面时,如果该JSP内容未经过修改,则无需再经过翻译和编译的过程,直接执行即可。这样可减少请求到接受到反馈页面的响应时间,提高用户体验。当一个JSP页面首次被访问时,它的响应速度是比较慢。JSP的翻译、编译和执行操作都是在服务器端进行的,返回给客户端浏览器的是服务器端执行JSP对应字节码文件后的HTML代码,所以客户端只要有浏览器就可以浏览JSP页面

      一个jsp文件的执行过程可以分为七个执行步骤:

  1. 启动Tomcat
  2. 浏览器发送HTTP请求给服务器
  3. Tomcat找到jsp文件,识别出这是一个jsp网页请求,并将该请求传递给jsp引擎,通过使用URL或.jsp文件来完成
  4. jsp引擎从磁盘载入jsp文件,并将所有的jsp文件转换成相应的java文件
  5. java文件编译得到字节码文件(.class文件)
  6. 服务器执行字节码文件
  7. 把结果嵌入到静态代码返回到浏览器中

4. JSP销毁
JSP 生命周期的销毁阶段表示从容器中删除 JSP。jspDestroy() 方法等效于 Servlet 中的 destroy() 方法。如果您需要自定义 JSP 的销毁内容,可以重写 jspDestroy() 方法。

public void jspDestroy() {
   ...释放数据库连接、关闭打开的文件
}

第一个JSP程序

jdk安装:jdk
Eclipse安装:eclipse
Tomcat 9安装:Windows10上Tomcat 9安装与配置

新建文件,编写以下代码,并将该文件存为 index.jsp,并放在文件夹 jspDemo 中,然后将该文件夹粘贴到 tomcat 的 webapps 目录下,运行 JSP 页面。

1. 编写代码
index.jsp 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>T_T</title>
</head>
<body>
    <%out.print("Hello,编程帮!网址是:http://www.biancheng.net");%>
</body>
</html>

2. 运行
     运行步骤:

  1. 把 JSP 文件放在文件夹内,部署到服务器中
  2. 启动服务器
  3. 通过 URL 访问,例如:http://localhost:8080/jspDemo/index.jsp

访问结果如下:

3. 代码解释
<% @    page    %>  用来指定该页面的配置信息,contentType="text/html; charset=UTF-8" 指定该网页类型为 html,网页字符编码为 UTF-8;language="java" 指定该网页编程语言为 Java。

<head>、<title> 和 <body> 都是 HTML 标签,用于静态网页,他们包含的内容都将直接传递给客户端,也就是 Web 浏览器。

<%   out.print("Hello,编程帮");   %> 是 JSP 中的输出语句,<% %> 中的代码是 Java 语句,因此以 ; 结尾,out.print("Hello,编程帮") 输出信息为“Hello,编程帮”。 


Eclipse创建JSP项目

     使用 Eclipse 创建 JSP 文件分为 3 个步骤:

  1. 创建动态 Web 项目
  2. 创建 JSP 文件
  3. 启动 Tomcat 服务器,部署项目

1. 创建动态Web项目
打开 Eclipse,单击 File -> New -> Project -> Dynamic Web Project,如下图所示:

输入项目名称 -> 配置服务器 -> 完成。

2. 创建JSP文件
在项目的 WebContent 目录单击鼠标右键,按顺序依次单击 New -> Other -> JSP File -> Next –> 输入文件名 index.jsp–>Finish


<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>

</body>
</html>

修改后 D:\eclipsecode\jspDemo\src\main\webapp\index.jsp 代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java"%>
<html>
<head>
    <title>T_T</title>
</head>
<body>
    <%out.print("Hello,编程帮");%>
</body>
</html>

3. 启动服务器并部署项目
运行 JSP:在您的 JSP 页面中单击鼠标右键–>单击 Run As–>Run on Server

Manually define a new server手动定义新服务器–>Apache–>Tomcat 服务器:Tomcat v9.0 Server–>Next–>选中项目–>Add(也可双击项目直接部署到Tomcat上)–>Finish。


启动 Tomcat 服务器,在浏览器输入相应的 URL,例如:http://localhost:8080/jspDemo/index.jsp ,页面如下:


JSP脚本 <% %>

http://c.biancheng.net/jsp2/script.html

在 JSP 中,可以使用 JSP 脚本写入 Java 代码

JSP 脚本可以包含任意数量的 Java 语句变量方法表达式 。JSP 脚本会把包含的内容插入到 Servlet 的 service() 方法中。

任何文本HTML 标签JSP 元素(声明,表达式等)都必须在脚本程序之外

JSP 脚本语法如下:

<%   Java语句   %>

等效于

<jsp:scriptlet>
    Java语句
</jsp:scriptlet>

示例:下面创建 login.html 和 welcome.jsp 两个文件,login.html 提供表单供用户输入用户名,welcome.jsp 获取用户输入用户名并显示欢迎消息。

login.html 代码如下:

<!DOCTYPE html>
<html>
<body>
    <form action="welcome.jsp" method="post">
        <input type="text" name="uname"><br/>
        <input type="text" name="url"><br/>
        <input type="submit" value="go">
    </form>
</body>
</html>

welcome.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
    <%
        String name = request.getParameter("uname");
        String url= request.getParameter("url");
        out.print("欢迎" + name + ",我们的网址是:" + url);
    %>
</body>
</html>

运行结果如下:
login.html运行页面

welcome.jsp运行页面


JSP声明语句 <%! %>

JSP 声明语句用于声明一个或多个变量、方法,以供后面的 Java 代码使用。您必须先对变量和方法进行声明,才能使用它们。

JSP 声明语法如下:

<%! 声明语句 %>

等效于

<jsp:declaration>
    声明语句
</jsp:declaration>

示例:

<%! int num = 0; %>
<%! Circle a = new Circle(2.0); %>
<%!
    public void show () {
    }
%>

JSP脚本和声明的区别:
JSP 脚本只能声明变量,不能声明方法。JSP 声明语句可以声明变量方法

JSP 脚本会把包含的内容转译插入到 Servlet 的 service() 方法中,也就是 <% %> 中定义的变量是局部变量。这也是 JSP 脚本不能声明方法的原因,因为 Java 不允许方法中嵌套方法。

JSP 声明会把包含的内容添加到 Servlet 类中(在任何方法之外),也就是 <%! %> 中定义的变量是成员变量,方法是成员方法。

JSP表达式 <%= %>

JSP 表达式可以把变量或者表达式输出到网页上,不需要 out.print() 就能输出数据。通常用于打印变量和方法的值。

可以将 <%=表达式 %> 理解为 <% out.println(表达式) %> 的简写方式。这里需要注意,JSP 表达式不能以分号结尾。

JSP 表达式语法如下:

<%= 表达式 %>

等效于

<jsp:expression>
    表达式
</jsp:expression>

例 1、使用 JSP 表达式显示欢迎消息,代码如下:

<%@ page language="java" pageEncoding="UTF-8"%>

<html> 
<body> 
    <%="欢迎来到编程帮(http://www.biancheng.net)!"%> 
</body> 
</html>  

浏览器输出信息为:欢迎来到编程帮(http://www.biancheng.net)!

例 2、下面使用 JSP 表达式显示当前时间,代码如下:

<%@ page language="java" pageEncoding="UTF-8"%>

<html>
<body>
    当前时间: <%= java.util.Calendar.getInstance().getTime() %> 
</body>
</html>

浏览器输出信息为:当前时间: Mon Dec 07 15:11:33 CST 2020


JSP注释(4种)

说到注释,相信大家肯定都不陌生,它是对程序代码的解释和说明。注释可以提高代码的可读性,让他人能够更加轻松地了解代码,从而提高团队合作开发的效率。

在 JSP 中可以使用以下 4 种注释:

  • HTML 注释
  • 带有 JSP 表达式的注释
  • 隐藏注释
  • 脚本程序(Scriptlet)中的注释

在 JSP 规范中,它们都属于 JSP 中的注释,且语法规则和运行效果都各不相同。本节我们将对 JSP 中的各种注释进行介绍。

HTML 注释 &lt;&excl; &hyphen;&hyphen; &hyphen;&hyphen;&gt;

由于 JSP 文件中可以包含 HTML 标记,所以 HTML 中的注释同样可以在 JSP 文件中使用。HTML 注释中的内容是不会在客户端浏览器中显示的,但可以通过 HTML 源代码看到这些注释内容。HTML 注释语法如下:

<!-- 注释内容 -->

示例 1、HTML 中的注释:

<%@ page language="java" pageEncoding="UTF-8"%>

<!-- 欢迎提示信息 -->
<h1>欢迎访问</h1>

访问该 JSP 页面,客户端浏览器中会输出以下内容:欢迎访问

查看 HTML 源代码,您将看到以下内容:

<!-- 欢迎提示信息 -->
<h1>欢迎访问</h1>


带JSP表达式注释&lt;&excl;&hyphen;&hyphen; <%= %> &hyphen;&hyphen;&gt;

在 HTML 注释中还可以嵌入 JSP 表达式,格式如下:

<!--HTML 注释内容<%=JSP 表达式%>-->

包含该注释语句的 JSP 页面被请求后,服务器能够自动识别并执行注释中的JSP 表达式,对于注释中的其他内容则不做任何操作。当服务器将执行结果返回给客户端浏览器后,注释的内容也不会在浏览器中显示。当我们查看 HTML 源代码时,只能查看到 JSP 表达式执行后的结果,并不能看到原来的 JSP 表达式。
示例 2、带有 JSP 表达式的注释:

<%@ page language="java" pageEncoding="UTF-8"%>

<%
    String name = "admin";
%>
<!--当前登录用户为:<%=name%>-->
<h1>欢迎您的到来,<%=name %></h1>

访问该 JSP 页面,客户端浏览器中会输出以下内容:欢迎您的到来,admin

查看 HTML 源代码,您将看到以下内容:

<!--当前登录用户为:admin-->
<h1>欢迎您的到来,admin</h1>


隐藏注释 &lt;&percnt;&hyphen;&hyphen; &hyphen;&hyphen;&percnt;&gt;

无论是普通的 HTML 注释还是带有 JSP 表达式的注释,虽然都不能在客户端浏览器中显示,但是它们却都存在于 HTML 源代码中,客户端可以通过 HTML 源代码看到被注释的内容,所以严格来说,这两种注释其实并不安全。下面我们介绍的隐藏注释,就可以解决这个问题,隐藏注释的内容,不会显示在客户端的任何位置(包括 HTML 源代码),安全性较高,其注释格式如下:

<%--注释内容--%>

示例 3、隐藏注释代码如下:

<%@ page language="java" pageEncoding="UTF-8"%>
<jsp:directive.page import="java.text.SimpleDateFormat"/>
<jsp:directive.page import="java.util.Date"/>

<%
    Date date = new Date();
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    String nowTime = dateFormat.format(date);
%>
<%--获取当前时间 --%>
<h1>当前时间为:<%=nowTime %></h1>

访问该 JSP 页面test.jsp,客户端浏览器中会输出以下内容:当前时间为:2021-06-17 10:46:29

查看 HTML 源代码,您将看到以下内容:

<h1>当前时间为:2021-06-17 10:51:17</h1>


脚本程序(Scriptlet)中的注释 //         /* */         /** */

脚本程序中包含的是一段 Java 代码,所以在脚本程序中的注释与在 Java 中的注释是相同的。

脚本程序中包括下面 3 种注释方法。

  • 单行注释
  • 多行注释
  • 文档注释

单行注释
单行注释的格式如下:

//注释内容

符号“//”后面的内容为注释的内容,服务器对该内容不进行任何操作。由于脚本程序的代码在客户端通过查看源代码是不可见的,因此在脚本程序中通过该方法注释的内容也是不可见的。

示例 4、JSP 文件中包含以下代码:

<%@ page language="java" pageEncoding="UTF-8"%>

<%
    int count=6; //定义一个计数变量
%>
计数变量 count:<%=count%>

访问该 JSP 页面,客户端浏览器中会输出以下内容:计数变量 count:6

查看 HTML 源代码,您将看到以下内容:

计数变量 count:6

示例 5、由于服务器不会对注释的内容进行处理,因此可以通过该注释暂时删除某一行代码。例如:

<%@ page language="java" pageEncoding="UTF-8"%>

<%
    String name = "admin";
    //name="bianchengbang";
%>
<h1>欢迎您:<%=name %></h1>

包含上述代码的 JSP 文件被执行后,会输出如下结果:欢迎您:admin

源代码:

多行注释
多行注释是通过“/”与“/”符号进行标记的,它们必须成对出现,在它们之间的注释内容可以换行。注释格式如下:

/*
注释内容1
注释内容2
*/

与单行注释一样,在“/”与“/”之间注释的所有内容,服务器都不会做任何处理,即使被注释的内容是 JSP 表达式或其他脚本程序也不例外。多行注释的开始标记和结束标记可以不在同一个脚本程序中同时出现

示例 6、JSP 文件中包含以下代码:

<%@ page language="java" pageEncoding="UTF-8"%>

<%
    String name = "www.biancheng.net";
    /*if ("www.biancheng.net".equals(name)) {%>
<h1>编程帮</h1>
<% }
    */%>
<h1><%=name %></h1>

访问该 JSP 页面,客户端浏览器中会输出以下内容:www.biancheng.net

源代码:

<%@ page language="java" pageEncoding="UTF-8"%>

<%
    String name = "www.biancheng.net";
    if ("www.biancheng.net".equals(name)) {%>
<h1>编程帮</h1>
<% }
    %>
<h1><%=name %></h1>

若将 JSP 文件中的多行注释去掉,再次访问该 JSP 页面,客户端浏览器输出内容如下:

编程帮

www.biancheng.net


源代码:


文档注释
文档注释的内容会被 Javadoc 文档工具在生成文档时读取,文档是对代码结构和功能的描述。注释格式如下:

/**
提示信息1
提示信息2
*/

文档注释与多行注释很相似,但它是以“/**”符号作为其开始标记的,而不是“/*”。与多行注释一样,被注释的所有内容,服务器不会做任何处理。

示例 7、在 Eclipse 中创建的一个 JSP 文件,该文件包含以下代码:

<%@ page language="java" pageEncoding="UTF-8"%>

<%!
    /**
     @作者:编程帮
     @网址:www.biancheng.net
    */
    public String sayHello(String who) {
        return "您好:" + who;
    }%>
<h1><%out.println(sayHello("编程帮")); %></h1>

访问该 JSP 页面,客户端浏览器中会输出以下内容:您好:编程帮

源代码:

将鼠标指针移动到调用 sayHello() 方法的位置,Eclipse 会自动读取并展示文档注释的内容。

String test.sayHello(String who)


@作者
编程帮
@网址
www.biancheng.net

JSP指令  &lt;%@ 指令 %&gt;

JSP 指令(directive)用来告诉 Web 服务器如何处理 JSP 页面的请求和响应。

服务器会根据 JSP 指令来编译 JSP,生成 Java 文件。JSP 指令不产生任何可见输出,在生成的 Java 文件中,不存在 JSP 指令。

SP 指令以<%@开始,以%>结束,语法如下:

<%@ directive attribute = "value" [attribute2 = "value2" ...]%>

注意:[ ] 中的内容是可选的,@ 符号和指令名称之间,以及最后一个属性和结束%>之间的空格是可选的。

指令分为以下 3 种类型。

指  令说  明
<%@ page ... %>定义与页面相关的属性,例如脚本语言、错误页面和缓冲要求
<%@ include ... %>引入其它 JSP 文件
<%@ taglib ... %>声明并导入标签库

在以下章节中,我们详细介绍了这几种指令。

  • JSP page指令
  • JSP include指令
  • JSP taglib指令

JSP page指令<%@ page 属性="" %>

JSP page 指令用来定义当前页面的相关属性。page 指令可以在 JSP 页面的任意位置编写,通常放在 JSP 页面的顶部。

page 指令的语法如下:

<%@ page attribute = "value" %>

下表是与 page 指令相关的属性。以下属性除了 import 可以声明多个外,其它属性都只能出现一次

page 指令常用属性
属  性取  值说  明举  例
buffernone、缓冲区大小(默认值为 8kb)指定输出流是否有缓冲区<%@ page buffer="16kb" %> 
autoFlushtrue(默认值)、false指定缓冲区是否自动清除<%@ page autoFlush="true" %> 
contentTypetext/html; charset = ISO-8859-1、
text/xml;charset = UTF-8 等
指定 MIME 类型和字符编码<%@ page contentType="text/html;charset=UTF-8" %>
errorpage页面路径指定当前 JSP 页面发生异常时,需要重定向的错误页面<%@ page errorpage="myerrorpage.jsp" %>

注意:myerrorpage.jsp 的 isErrorpage 值必须为 true
isErrorpagetrue、false(默认值)指定当前页面为错误页面<%@ page isErrorpage="true" %>  
extends包名.类名指定当前页面继承的父类,一般很少使用<%@ page extends="mypackage.SampleClass"%>
import类名、接口名、包名导入类、接口、包,类似于 Java 的 import 关键字<%@ page import = " java.util.Date" %>
<%@ page import="java.io.*, java.lang.*"%>
info页面的描述信息定义 JSP 页面的描述信息,可以使用 getServletInfo() 方法获取<%@ page info="这里是编程帮的页面信息"%>
isThreadSafetrue(默认值)、false是否允许多线程使用<%@ page isThreadSafe="false" %>
language脚本语言指定页面中使用的脚本语言<%@ page language= "java" %>
sessiontrue(默认值)、false指定页面是否使用 session<%@ page session="false" %>
isELIgnoredtrue(默认值)、false指定页面是否忽略 JSP 中的 EL<%@ page isELIgnored="false" %>

例 1、下面通过 page 指令的 import 属性导入 java.util.Date 类,显示欢迎信息和用户登录的日期时间。login.jsp 代码如下:

<%@ page import="java.util.Date" language="java" contentType="text/html;charset=utf-8"%>

<html>
<body>
    您好,欢迎登录编程帮!
    <br /> 您登录的时间是<%=new Date()%>
</body>
</html>

运行结果如下:

例 2、下面通过 page 指令的 errorpage 和 isErrorPage 属性定义以及重定向到错误页面。index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
    <%@page errorPage="errorpage.jsp"%>
    <%=100/0 %>
</body>
</html>

注意:exception 只能在错误页面使用。errorpage.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
    <%@page isErrorPage="true"%>
    抱歉,发生了异常!
    <br /> 异常为<%= exception%>
</body>
</html>

访问 index.jsp 页面,结果如下:


JSP include指令<%@ include file="" %>

include 指令用于在 JSP 页面引入其它内容,可以是 JSP 文件、html 文件和文本文件等,相当于把文件的内容复制到 JSP 页面。引入的文件和 JSP 页面同时编译运行。

使用 include 指令有以下优点:

  • 增加代码的可重用性
  • 使 JSP 页面的代码结构清晰易懂
  • 维护简单

可以在页面的任何位置编写 include 指令。其中,file 指定需要引入文件的相对路径。include 的语法如下:

<%@ include file="URL" %>  

例 1、在 index.jsp 页面使用 include 指令引入 head.jsp。head.jsp 代码如下:

<%@ page language="java" pageEncoding="UTF-8"%>

<p>header内容​</p>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
    <%@ include file="head.jsp" %>
    <p>编程帮(http://www.biancheng.net)</p>
</body>
</html>

此示例中,head.jsp 和 index.jsp 在同一目录中,如果不在同一目录,引入 head.jsp 文件时需要指定完整路径。运行结果如下:

源代码:


JSP taglib指令 <%@ taglib uri="" prefix="" %>

在 JSP 中,我们可以使用 taglib 指令声明并引入标签库。Java API 允许在页面中自定义标签,标签库就是自定义标签的集合。

其中,uri 指定自定义标签库的存放位置;prefix 指定标签库的前缀。为了区分不同的标签库,在页面中使用标签库以对应的 prefix 开头。taglib 指令的语法如下:

<%@ taglib uri="tagliburl" prefix="tagPre" %>

例 1、下面我们使用 currentDate 标签,首先使用 taglib 指令指定标签库存放位置和使用前缀。示例代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.biancheng.net/tags" prefix="mytag"%>
<!DOCTYPE html>
<html>
<body>
    <mytag:currentDate />
</body>
</html>

以上代码会出现找不到标签库路径错误,这里只演示 taglib 指令的用法,具体应用会在教程后面进行详细说明。


JSP动作  &lt;jsp:动作名 属性="" /&gt;

JSP 动作使用 XML 语法格式的标签来控制服务器的行为。利用 JSP 动作可以动态地插入文件重用 JavaBean 组件、把用户重定向到另一个页面、为 Java 插件生成 HTML 代码等。

JSP 动作与 JSP 指令的不同之处如下:
JSP 指令在翻译阶段执行,从而设置整个 JSP 页面的属性。JSP 页面被执行时首先进入翻译阶段,程序会先查找页面中的 JSP 指令,并将它们转换成 Servlet。所以,JSP 指令是在页面转换时期被编译执行的,且编译一次。
JSP 动作在请求处理阶段执行,它们只有执行时才实现自己的功能。通常用户每请求一次,动作标识就会执行一次。

JSP 动作语法如下:

<jsp:action_name attribute = "value" />

<jsp:action_name attribute="value"></jsp:action_name>

action_name 表示 JSP 动作名称,attribute 表示相应 JSP 动作的属性名称。常用的 JSP 动作如下表所示:

动作说明
jsp:include页面被请求时引入一个文件
jsp:userBean实例化JavaBean
jsp:setProperty设置 JavaBean 的属性
jsp:getProperty获取 JavaBean 的属性
jsp:forward将请求转发到另一个页面
jsp:plugin根据浏览器类型替换成 <object> 或者 <embed>标签
jsp:element定义动态 XML 元素
jsp:attribute设置动态 XML 元素的属性
jsp:body设置动态 XML 元素的内容

在以下章节中,我们详细介绍了几种常用的动作:

  • JSP include动作
  • JSP forward动作
  • JSP param动作
  • JSP useBean动作
  • JSP setProperty和getProperty动作

JSP include动作 &lt;jsp:include page="URL | <%=表达式%>" flush="true" /&gt;

<jsp:include> 动作用来在页面中引入文件,文件可以是 HTMLJSP 页面文本文件等。通过 include 动作,我们可以多次使用同一个页面,增加了代码可重用性。例如,在页面中使用 include 动作引入头部和底部页面。

page 指定引入页面的路径,flush 表示在引入文件前是否刷新缓冲区,默认为 false。注意:“jsp”和“:”以及“include”三者之间没有空格。<jsp:include> 的语法如下:

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

**示例: **head.jsp 代码如下:

<%@ page language="java" pageEncoding="UTF-8"%>

<p>header内容​</p>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<body>
    <jsp:include page="head.jsp"></jsp:include>
    <p>编程帮(http://www.biancheng.net)</p>
</body>
</html>

运行结果如下:

源代码:


JSP forward动作 &lt;jsp:forward page="url" /&gt;

<jsp:forward> 动作用来将请求转发到另一个页面中,请求的参数数据会被一起转发到目标页面。

其中,page 指定需要转发文件的相对路径,且指定的文件只能是该 Web 应用中的文件。<jsp:forward> 的语法如下:

<jsp:forward page="url"/>

示例: 在 login.jsp 页面中使用 <jsp:forward> 跳转到 index.jsp 页面,login.jsp 页面代码如下:

<%@ page language="java" contentType="text/html;charset=utf-8"%>
<html>
<head>
<title>编程帮-jsp:forward</title>
</head>
<body>
    <jsp:forward page="index.jsp" />
</body>
</html>

index.jsp 页面代码如下:

<%@ page import="java.util.Date" language="java" contentType="text/html;charset=utf-8"%>
<html>
<head>
<title>编程帮-jsp:forward</title>
</head>
<body>
    您好,欢迎登录编程帮!
    <br /> 您登录的时间是<%=new Date()%>
</body>
</html>

访问 login.jsp可以看到,浏览器中的地址仍是跳转前的地址,<jsp:forward> 与 Servlet 中的转发相似,客户端不会显示转发后的页面地址,有较好的隐蔽性。访问 login.jsp 结果如下:

源代码:


JSP param动作 &lt;jsp:param name="" value="" /&gt;

<jsp:param> 动作用来传递参数信息,经常和其它动作一起使用,例如 <jsp:include> 和 <jsp:forward>。

name 指定参数名称,value 指定参数值。<jsp:param> 的语法如下:

<jsp: param name="param_name" value="param_value" />

示例: 在 login.jsp 中传递参数,并转发到 index.jsp 页面。login.jsp 代码如下:

<%@ page language="java" contentType="text/html;charset=utf-8"%>
<html>
<head>
<title>编程帮-jsp:param</title>
</head>
<body>
    <%
        // 设定传递参数的编码格式
        request.setCharacterEncoding("UTF-8");
    %>
    <jsp:forward page="index.jsp">
        <jsp:param name="sitename" value="编程帮" />
        <jsp:param name="url" value="http://www.biancheng.net" />
    </jsp:forward>
</body>
</html>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html;charset=utf-8"%>
<html>
<head>
<title>编程帮-jsp:param</title>
</head>
<body>
    您好,欢迎登录<%=request.getParameter("sitename")%>!
    <br /> 网址是<%=request.getParameter("url")%>
</body>
</html>

<jsp:param> 在 <jsp:forward> 和 <jsp:include> 中的用法基本一样,用户可以将上述示例中的 <jsp:forward> 替换成 <jsp:include> 动作,运行结果相同。访问 login.jsp 结果如下:

源代码:


JSP useBean动作 &lt;jsp:useBean id = "" class = "" scope= "" /&gt;

<jsp:useBean> 用于获取 Bean 对象。首先会在指定范围内查找 Bean 对象,若 Bean 对象不存在,则创建 Bean 对象。语法如下:

<jsp:useBean id = "name" class = "package.class" scope= "page | request | session | application" />

1)id:表示 Bean 实例化对象的变量名,可以在指定范围内使用该变量名。id表示定义的JavaBean的唯一标识。命名引用该Bean的变量。可以随意取名。定义了一个JavaBean就可以为其属性赋值了,可以使用jsp:setProperty动作为JavaBean的属性赋值,也可以使用jsp:getProperty动作来获取属性的值。
2)class:表示需要实例化 Bean 的类路径,指定的类必须包含 public 且无参数的构造方法。class表示定义的JavaBean的完整类名(即必须带有包名)。
3)scope:指定 Bean 的作用域,取值为:

  • page:只能在当前页面使用该 Bean 对象;
  • request:只能在一次请求范围内使用该 Bean 对象;
  • session:只能在一次会话范围内使用该 Bean 对象;
  • application:可以在整个应用范围内使用该 Bean 对象。

JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范。JavaBean 与其它 Java 类相比而言独一无二的特征:

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

一个 JavaBean 对象的属性应该是可访问的。这个属性可以是任意合法的 Java 数据类型,包括自定义 Java 类。一个 JavaBean 对象的属性可以是可读写,或只读,或只写。JavaBean 对象的属性通过 JavaBean 实现类中提供的两个方法来访问,一个只读的属性只提供 getPropertyName() 方法,一个只写的属性只提供 setPropertyName() 方法。

方法 描述
getPropertyName() 举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 getMyName() 来读取这个属性。这个方法也称为访问器。
setPropertyName() 举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 setMyName()来写入这个属性。这个方法也称为写入器。

示例:jsp之useBean示例一.zip,下面创建一个简单的 Test 类,代码如下:

package net.biancheng;

public class Test {
    public String concatStr(String str) {
        String str2 = str.concat("http://www.biancheng.net");
        return str2;
    }
}

编译Test.java生成Test.class:javac D:\eclipsecode\useBean\src\main\webapp\WEB-INF\classes\net\biancheng\Test.java

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="net.biancheng.Test"%>    <!-- 这里不要忘了导入net.biancheng.Test类 -->
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <!-- 这里不要忘了scope="application" -->
    <jsp:useBean id="test" class="net.biancheng.Test"  scope="application" />
    <%
        String str = "编程帮:";
        out.print(test.concatStr(str));
    %>
</body>
</html>

在eclipse中手动添加文件、目录,在eclipse界面D:\工作空间\useBean\src\main\webapp\WEB-INF\ 下依次手动创建 classes\net\biancheng 文件夹,把StudentBean.java放到 D:\工作空间\useBean\src\main\webapp\WEB-INF\classes\net\biancheng 下,并直接编译Test.java文件为Test.class,如下图所示:

运行结果如下:

源代码:

示例: jsp之useBean示例二.zip 下面创建一个简单的 com.runoob.StudentBean 类,不要忘了导入java.io.Serializable类,属性都是私有的,StudentBean.java代码如下:

package com.runoob;
import java.io.Serializable;

public class StudentBean implements Serializable {
	private String firstName = null;
	private String lastName = null;
	private int age = 0;

	public StudentBean() {
	}
	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;
	}
}

编译StudentBean.java生成StudentBean.class:javac D:\eclipsecode\useBean\src\main\webapp\WEB-INF\classes\com\runoob\StudentBean.java

index.jsp代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ page import="com.runoob.StudentBean"%>    <!-- 这里不要忘了导入com.runoob.StudentBean类 -->
<html>
<head>
<title>get 和 set 属性实例</title>
</head>
<body>  
<!-- 这里com.runoob.StudentBean拼写容易出错 -->
<jsp:useBean id="students"  class="com.runoob.StudentBean" scope="application" > 
   <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>

在eclipse中手动添加文件、目录,在eclipse界面D:\工作空间\useBean\src\main\webapp\WEB-INF\ 下依次手动创建 classes\com\runoob 文件夹,把StudentBean.java放到 D:\工作空间\useBean\src\main\webapp\WEB-INF\classes\com\runoob 下,并直接编译StudentBean.java文件为StudentBean.class,如下图所示:


运行结果:

源代码:

JSP setProperty和getProperty动作

<jsp:getProperty> 动作用于获取 Bean 的属性值,<jsp:setProperty>动作用于设置 Bean 的属性值。

<jsp:setProperty> 的语法如下:

<jsp:setProperty name = "beanName" property = "attributeName" value = "attributeValue"/>

name 指定需要修改属性的 Bean 的名称;property 指定 Bean 的属性,即 Bean 类中的属性名;value 是要设定的属性值。

<jsp:setProperty> 通常和 <jsp:useBean> 一起使用,分为两种情况。

1、在 <jsp:useBean> 标签外使用 <jsp:setProperty>,无论 <jsp:useBean> 是否实例化了 User 对象,<jsp:setProperty> 都会执行,例如:

<jsp:useBean id = "User" ... />
...
<jsp:setProperty name = "User" property = "attributeName" value = "attributeValue"/>

2、在 <jsp:useBean> 标签里使用 <jsp:setProperty>,只有 <jsp:useBean> 实例化了 User 对象,<jsp:setProperty> 才会执行,例如:

<jsp:useBean id = "User" ...>
    ...
    <jsp:setProperty name = "User" property = "attributeName" value = "attributeValue"/>
</jsp:useBean>

<jsp:getProperty> 的语法如下:

<jsp:getProperty name = "beanName" property = "attributeName">

name 指定需要获取属性的 Bean 的名称;property 指定 Bean 的属性,即 Bean 类中的属性名。

注意:<jsp:setProperty> 和 <jsp:getProperty> 动作都会按照 page、request、session 和 application 的顺序依次查找指定 Bean 的实例对象,直到第一个实例被找到。如果任何范围内都不存在这个 Bean 实例,则会拋出异常。

示例: 创建 Bean 类 Site.java,代码如下:

package net.biancheng;

public class Site {
    private String name;
    private String url;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
}

login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="post">
        网站名称: <input type="text" name="name" /> <br> <br>
        网址: <input type="text" name="url" /> <br> <br>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        request.setCharacterEncoding("UTF-8");
    %>
    <jsp:useBean id="site" class="net.biancheng.Site" />
    <jsp:setProperty property="*" name="site" />
    <jsp:getProperty property="name" name="site" />
    <jsp:getProperty property="url" name="site" />
</body>
</html>

注意:当 property 的取值为 * 时,要求 Bean 属性的名称与类型要与 request 请求中参数的名称及类型一致,以便用 Bean 中的属性来接收客户输入的数据,系统会根据名称来自动匹配

运行结果如下:


源代码:


JSP内置对象(九种)

为了简化页面的开发过程,JSP 提供了一些内置对象。JSP 内置对象又称为隐式对象,它们由容器实现和管理。在 JSP 页面中,这些内置对象不需要预先声明,也不需要进行实例化,我们可以直接在脚本和表达式中使用。
注意:JSP 内置对象只能在脚本表达式中使用,在声明中不能使用。

九大内置对象
JSP 中定义了 9 个内置对象,它们分别是:request、response、session、application、out、pagecontext、config、page 和 exception,这些对象在客户端和服务器端交互的过程中分别完成不同的功能。

对  象
类型
说  明
requestjavax.servlet.http.HttpServletRequest获取用户请求信息
responsejavax.servlet.http.HttpServletResponse响应客户端请求,并将处理信息返回到客户端
outjavax.servlet.jsp.JspWriter输出内容到 HTML 中
sessionjavax.servlet.http.HttpSession用来保存用户信息
applicationjavax.servlet.ServletContext所有用户共享信息
configjavax.servlet.ServletConfig这是一个 Servlet 配置对象,用于 Servlet 和页面的初始化参数
pageContextjavax.servlet.jsp.PageContextJSP 的页面容器,用于访问 page、request、application 和 session 的属性
pagejavax.servlet.jsp.HttpJspPage类似于 Java 类的 this 关键字,表示当前 JSP 页面
exceptionjava.lang.Throwable该对象用于处理 JSP 文件执行时发生的错误和异常;只有在 JSP 页面的 page 指令中指定 isErrorPage 的取值 true 时,才可以在本页面使用 exception 对象。

JSP 的内置对象主要有以下特点:

  • 由 JSP 规范提供,不用编写者实例化;
  • 通过 Web 容器实现和管理;
  • 所有 JSP 页面均可使用;
  • 只有在脚本元素的表达式或代码段中才能使用。

四大域对象
在 JSP 九大内置对象中,包含四个域对象,它们分别是:pageContext(page 域对象)、request(request 域对象)、session(session 域对象)、以及 application(application 域对象)。

JSP 中的 4 个域对象都能通过以下 3 个方法,对属性进行保存、获取和移除操作。

返回值类型
方法
作用
void
setAttribute(String name, Object o)
将属性保存到域对象中
Object
getAttribute(String name)
获取域对象中的属性值
void
removeAttribute(String name)
将属性从域对象中移除

JSP 中的 4 个域对象的作用域各不相同,如下表。

作用域
描述
作用范围
page如果把属性保存到 pageContext 中,则它的作用域是 page。该作用域中的属性只在当前 JSP 页面有效,跳转页面后失效。
request如果把属性保存到 request 中,则它的作用域是 request。该作用域中的属性只在当前请求范围内有效。
  • 服务器跳转页面后有效,例如<jsp:forward>;
  • 客户端跳转页面后无效,例如超链接。
session如果把属性保存到 session 中,则它的作用域是 session。该作用域中的属性只在当前会话范围内有效,网页关闭后失效。
application如果把属性保存到 application 中,则它的作用域是 application。该作用域中的属性在整个应用范围内有效,服务器重启后失效。

JSP request对象

JSP request 是 javax.servlet.http.HttpServletRequest 的实例对象,主要用来获取客户端提交的数据。request 对象提供了一系列方法,可以获取请求参数信息、表单数据、HTTP 头信息、cookie 和 HTTP 请求方法等。request 对象常用方法如下表所示。

request 对象常用方法
方法
说明
String getParameter(String name)获取请求参数 name 的值
Enumeration getParameterNames() 获取所有参数名称
String[] getParameterValues(String name)获取请求参数 name 的所有值
Object getAttribute(String name)获取 name 属性的值
Enumeration getAttributeNames() 返回所有属性的名称集合
void setAttribute(String key, Object value)给 key 对象赋 value 值
void removeAttribute(String name)删除指定的 name 属性
cookie[] getCookies()获取所有的 cookie 对象
HttpSession getSession()返回 request 对应的 session 对象,如果没有则创建一个 session 对象
HttpSession getSession(boolean create)返回 request 对应的 session 对象,若没有,且 create 值为 true,则创建一个 session 对象
Enumeration getHeaderNames()返回 HTTP 头信息的名称集合
String getHeader(String name)获取 name 指定的 HTTP 头信息
String getMethod()获取 HTTP 请求方法/客户提交信息方式

示例: 在 index.jsp 页面使用 getHeaderNames() 方法获取 HTTP 头信息,并遍历输出参数名称和对应值。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>获取HTTP请求头信息</h2>
    <table width="100%" border="0" align="center">
        <tr bgcolor="#CCBBFF">
            <th><font size="4" color="#000000">参数名称</font></th>
            <th><font size="4" color="#000000">参数值</font></th>
        </tr>
        <%
            Enumeration headerNames = request.getHeaderNames();
            while (headerNames.hasMoreElements()) {
                String paramName = (String) headerNames.nextElement();
                out.print("<tr style='background-color:#FAFAD2'><td><font  size='2' color='#FF00FF'>" + paramName + "</font></td>\n");
                String paramValue = request.getHeader(paramName);
                out.println("<td> <font  size='2' color='#008000'>" + paramValue + "</font></td></tr>\n");
            }
        %>
    </table>
</body>
</html>

运行结果如下:

源代码:

头信息介绍如下:

参数名称
说明
host表示服务器的主机名和端口号。
connection判断客户端是否可以持续性的连接 HTTP。
cache-control网页缓存控制,默认值为 no-cache,表明每次访问都从服务器中获取页面。
upgrade-insecure-requestsChrome(谷歌浏览器)会在 HTTP 请求中加入Upgrade-Insecure-Requests: 1 ,服务器收到请求后会返回Content-Security-Policy: upgrade-insecure-requests头来告诉浏览器,可以把本站的所有 http 连接升级为 https 连接。使用其它浏览器没有该参数。
user-agent客户端程序的相关信息,例如:浏览器版本、操作系统类型等。
accept客户端能接收的 MIME 类型。
sec-fetch-*网络请求的元数据描述,也是 Chrome 特有的参数。
accept-encoding指明客户端能够处理的编码类型有哪些。
accept-language浏览器的首选语言。
cookie会话信息,每个会话都有一个对应的会话 ID 或其他信息。

示例:在 index.jsp 页面使用 getParameterNames()和getParameter(String name)方法获取 GET 参数名称和对应值。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>获取HTTP请求头信息</h2>
    <table width="100%" border="1" align="center">
        <tr bgcolor="#949494">
            <th>参数名称</th>
            <th>参数值</th>
        </tr>
        <%
            Enumeration parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = (String) parameterNames.nextElement();
                out.print("<tr><td>" + paramName + "</td>\n");
                String paramValue = request.getParameter(paramName);
                out.println("<td> " + paramValue + "</td></tr>\n");
            }
        %>
    </table>
</body>
</html>

通过 HTML 表单将客户端数据提交到 index.jsp 中,login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="get">
        站点名: <input type="text" name="name"> <br />
        网址: <input type="text" name="url" />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

运行结果如下:


示例:在 index.jsp 页面使用 getParameterNames()和getParameter(String name)方法获取 POST 参数名称和对应值。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>获取HTTP请求头信息</h2>
    <table width="100%" border="1" align="center">
        <tr bgcolor="#949494">
            <th>参数名称</th>
            <th>参数值</th>
        </tr>
        <%
            Enumeration parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = (String) parameterNames.nextElement();
                out.print("<tr><td>" + paramName + "</td>\n");
                String paramValue = request.getParameter(paramName);
                out.println("<td> " + paramValue + "</td></tr>\n");
            }
        %>
    </table>
</body>
</html>

通过 HTML 表单将客户端数据提交到 index.jsp 中,login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="post">
        站点名: <input type="text" name="name"> <br />
        网址: <input type="text" name="url" />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

运行结果如下:


示例:在 index.jsp 页面使用 getParameterNames()、getParameter(String name)、getAttribute(String attributeName)方法获取 GET 参数名称和对应值。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>获取HTTP请求头信息</h2>
    <table width="100%" border="1" align="center">
        <tr bgcolor="#949494">
            <th>参数名称</th>
            <th>参数值</th>
        </tr>
        <%
            Enumeration parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = (String) parameterNames.nextElement();
                out.print("<tr><td>" + paramName + "</td>\n");
                String paramValue = request.getParameter(paramName);
                out.println("<td> " + paramValue + "</td></tr>\n");  
            }
                               
            Integer requestCount = (Integer) request.getAttribute("requestCount");
            if (requestCount == null) {
            	requestCount = 1;
            } else {
            	requestCount++;
            }
            request.setAttribute("requestCount", requestCount);                
            out.println("<tr><td> requestCount </td>\n");  
            out.println("<td> " + request.getAttribute("requestCount") + "</td></tr>\n");
            
            Integer applicationCount = (Integer) application.getAttribute("applicationCount");
            if (applicationCount == null) {
            	applicationCount = 1;
            } else {
            	applicationCount++;
            }
            application.setAttribute("applicationCount", applicationCount);                
            out.println("<tr><td> applicationCount </td>\n");                 
            out.println("<td> " + application.getAttribute("applicationCount") + "</td></tr>\n");                       
        %>
    </table>
</body>
</html>

通过 HTML 表单将客户端数据提交到 index.jsp 中,login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="get">
        站点名: <input type="text" name="name"> <br />
        网址: <input type="text" name="url" />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

运行结果如下:






换个浏览器:

post方式是一样的,只不过http://localhost:8080/jspDemo/login.jsp跳转的页面为http://localhost:8080/jspDemo/index.jsp,url没有参数,别的都一样:

示例:在 index.jsp 页面使用 getParameterNames()、getParameter(String name)、getAttribute(String attributeName)、getParameterValues(String name)方法获取 GET 参数名称和对应值。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>获取HTTP请求头信息</h2>
    <table width="100%" border="1" align="center">
        <tr bgcolor="#949494">
            <th>参数名称</th>
            <th>参数值</th>
        </tr>
        <%
            Enumeration parameterNames = request.getParameterNames();
            while (parameterNames.hasMoreElements()) {
                String paramName = (String) parameterNames.nextElement();
                out.print("<tr><td>" + paramName + "</td>\n");
                String paramValue = request.getParameter(paramName);
                out.println("<td> " + paramValue + "</td>\n");

                String[] paramValueArrays = request.getParameterValues(paramName);
                out.println("<td> "+paramValueArrays.toString() + "</td></tr>\n"); 
            }                               
            Integer requestCount = (Integer) request.getAttribute("requestCount");
            if (requestCount == null) {
            	requestCount = 1;
            } else {
            	requestCount++;
            }
            request.setAttribute("requestCount", requestCount);                
            out.println("<tr><td> requestCount </td>\n");  
            out.println("<td> " + request.getAttribute("requestCount") + "</td>\n");
            out.println("<td></td></tr>\n"); 
            
            Integer applicationCount = (Integer) application.getAttribute("applicationCount");
            if (applicationCount == null) {
            	applicationCount = 1;
            } else {
            	applicationCount++;
            }
            application.setAttribute("applicationCount", applicationCount);                
            out.println("<tr><td> applicationCount </td>\n");                 
            out.println("<td> " + application.getAttribute("applicationCount") + "</td>\n");
            out.println("<td></td></tr>\n"); 
        %>
    </table>
</body>
</html>

通过 HTML 表单将客户端数据提交到 index.jsp 中,login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="get">
        站点名: <input type="text" name="name"> <br />
        网址: <input type="text" name="url" />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

运行结果如下:


JSP response对象

JSP response 是 javax.servlet.http.HttpServletResponse 的实例对象。response 对象和 request 对象相对应,主要用于响应客户端请求,将处理信息返回到客户端。response 对象的常用方法如下:

方  法
说  明
void addHeader(String name, String value)添加头信息(参数名称和对应值)
void addCookie(Cookie cookie)添加 cookie 信息
void sendRedirect(String location)实现页面重定向
void setStatus(int sc)实现页面的响应状态代码
void setContentType(String type)设置页面的 MIME 类型和字符集
void setCharacterEncoding(String charset)设定页面响应的编码类型

示例: 下面在 login.jsp 新建表单,在 checkdetails.jsp 接收 login.jsp 提交的用户名和密码,与指定的用户名和密码相比,相同则登录成功,重定向到 success.jsp;反之登录失败,重定向到 failed.jsp。login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>用户登录</h2>
    <form action="checkdetails.jsp">
        用户名: <input type="text" name="username" /> <br> <br> 
        密码: <input type="text" name="pass" /> <br> <br> 
        <input type="submit" value="登录" />
    </form>
</body>
</html>

checkdetails.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        String username = request.getParameter("username");
        String password = request.getParameter("pass");
        if (username.equals("admin") && password.equals("123456")) {
            response.sendRedirect("success.jsp");
        } else {
            response.sendRedirect("failed.jsp");
        }
    %>
</body>
</html>

success.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>登录成功!</h2>
</body>
</html>

failed.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>登录失败,用户名或密码错误!</h2>
</body>
</html>

运行结果如下所示:

输入 结果

JSP out对象

JSP out 是 javax.servlet.jsp.JspWriter 的实例对象。out 对象包含了很多 IO 流中的方法和特性,最常用的就是输出内容到 HTML 中。out 对象的常用方法如下:

out 对象的常用方法
方法
说明
void print()将内容直接打印在 HTML 标签中
void println()类似于 print,唯一区别是 println 方法添加了换行符
void newLine()输出换行字符
void clear()清除页面缓冲区
boolean isAutoFlush()检查页面是否自动清除缓冲区

示例:out 对象的方法相对比较简单,一般情况下很少使用。下面我们使用 out 对象的 print、println 和 newLine 方法将内容输出到 HTML 中。index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        out.print("欢迎来到编程帮,");
        out.newLine();
        out.println("我们的网址是:");
        out.print("www.biancheng.net");
    %>
</body>
</html>

注意:这里的 println 与 newLine 换行指的是 HTML 代码换行,并不是页面显示的换行。页面显示换行需要在代码后面加
标签。运行结果如下:

源代码:


JSP session对象

JSP session 是 javax.servlet.http.HttpSession 的实例对象,主要用来访问用户数据,记录客户的连接信息。

HTTP 协议是一种无状态的协议(即不保存连接状态的协议)。每次用户向服务器发出请求,且服务器接收请求并返回响应后,该连接就被关闭了,服务器端与客户端的连接被断开。此时,服务器端不保留连接的有关信息,要想记住客户的连接信息,就用到了 session 对象。session 对象的常用方法如下:

session 对象的常用方法
方法
说明
void setAttribute(String name, Object value)将参数名和参数值存放在 session 对象中
Object getAttribute(String name)通过 name 返回获取相应的 value 值,如果 name 没有相应的 value 值,则返回 null
void removeAttribute(String name)删除指定的 name 参数
Enumeration getAttributeNames()获取 session 对象中存储的所有参数
long getCreationTime()返回 session 对象创建的时间
String getId()获取 session 对象的 ID 值
boolean isNew()用于检查 session 对象是不是新对象,如果客户端禁用了 cookie ,则 session.isNew() 始终返回 true
void invalidate()终止 session,即指定 session 对象失效
void setMaxInactiveInterval()设置 session 对象的有效时间,单位:秒
int getMaxInactiveInterval()获取 session 对象的有效时间,单位:秒
long getLastAccessedTime()获取上次访问 session 对象的时间

invalidate使无效       Inactive不活跃的       Interval间隔       Access通道

示例:在 login.jsp 页面登录,并跳转到 index.jsp。login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h2>用户登录</h2>
    <form action="index.jsp">
        用户名: <input type="text" name="username" /> <br> <br>
        密码: <input type="text" name="pass" /> <br> <br>
        <input type="submit" value="登录" />
    </form>
</body>
</html>

在 index.jsp 中,使用 session.setAttribute() 方法将用户名存储到 session 对象中,代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        String username = request.getParameter("username");
        out.print("欢迎" + username + "登录");
        session.setAttribute("sessname", username);
    %>
    <a href="success.jsp">跳转成功页面</a>
</body>
</html>

在 success.jsp 中,使用 session.getAttribute() 方法获取 session 对象中的用户名,并显示。success.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        String name = (String) session.getAttribute("sessname");
        out.print("您好,您的用户名为:" + name);
    %>
</body>
</html>

运行结果如下图所示:


拓展:如果使用的是 Tomcat 服务器,可以在 web.xml 文件中添加以下内容来设置 session 的有效时间:

<session-config>
    <session-timeout>15</session-timeout>
</session-config>

这里单位是分钟,即表示 session 对象的有效时间为 15 分钟,Tomcat 默认有效时间为 30 分钟。如果在 web.xml 中配置的是15分钟,则 getMaxInactiveInterval( ) 方法返回 900。


JSP application对象

JSP application 是 javax.servlet.ServletContext 的实例对象。在服务器部署应用和项目时,Web 容器仅创建一次 ServletContext 实例,也就是说 application 设置的任何属性和值可以用于整个应用(所有 JSP 页面)。可以将 application 对象看作 Web 应用的全局变量。一般用于保存应用程序的公用数据。
application 对象在 Web 应用运行时一直存在于服务器中,非常占用资源,因此在实际开发中不推荐使用,否则容易造成内存不足等情况。application 对象常用方法如下:

application 对象的常用方法
方  法
说  明
Object getAttribute(String attributeName)获取 attributeName(属性名称)对应的 object
void setAttribute(String attributeName, Object object)设置 attributeName 对应的属性值
Enumeration getAttributeNames()返回 application 对象中所有的 attributeName
void removeAttribute(String objectName)删除 application 对象中指定 attributeName 的属性
String getServerInfo()获取当前 Servlet 的版本信息
String getRealPath(String value)获取指定文件的实际路径

示例:可以使用 application 对象来保存 JSP 页面的访问人数,也就是我们常说的网站计数器,下面通过一个例子来演示。index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        Integer count = (Integer) application.getAttribute("count");
        if (count == null) {
            count = 1;
        } else {
            count++;
        }
        application.setAttribute("count", count);
    %>
    <h2>编程帮网站计数器</h2>
    欢迎访问编程帮,您是第<%=count%>位访问客户!
</body>
</html>

运行结果如下:


JSP config对象

JSP config 是 javax.servlet.ServletConfig 的实例对象,一般用于获取页面和 Servlet 的初始化参数。config 对象的常用方法如下:

config 对象的常用方法
方法
说明
String getInitParameter(String paramname) 获取指定的初始化参数值
Enumeration getInitParameterNames()获取当前页面所有的初始化参数值
ServletContext getServletContext()获取当前执行 Servlet 的 servletContext(Servlet 上下文)的值
String getServletName() 获取当前执行 Servlet 的名称

示例:在 web.xml 文件中定义 Servlet 名称和映射,然后使用 config 对象获取信息。注意: <servlet-mapping>...<url-pattern>/index.jsp</url-pattern></servlet-mapping>中/index.jsp指的是D:\eclipsecode\工程名\src\main\webapp\WEB-INF\web.xml 具体项目部署文件,WEB-INF是Java的WEB应用的安全目录,即客户端无法访问,只有服务端可以访问的目录。D:\apache-tomcat-9.0.79\conf\conf\web.xml是全局文件。具体项目部署文件web.xml 代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_2_5.xsd"
    version="2.5">
    <servlet>
        <servlet-name>BianChengBangServlet</servlet-name>
        <jsp-file>/index.jsp</jsp-file>
        <init-param>
            <param-name>url</param-name>
            <param-value>http://www.biancheng.net</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>BianChengBangServlet</servlet-name>
        <url-pattern>/index.jsp</url-pattern>
    </servlet-mapping>
</web-app>

在 index.jsp 页面获取 Servlet 名称以及初始化参数,代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        String sname = config.getServletName();
        String url = config.getInitParameter("url");
        out.print("Servlet名称为:" + sname + "<br>");
        out.print("初始化参数为:" + url + "<br>");
    %>
</body>
</html>

注意:因为 JSP 页面的实质是 Servlet,所以一般很少在 JSP 页面中使用 config 对象。运行结果如下:

源代码:


JSP pageContext对象

pageContext 是 javax.servlet.jsp.PageContext 的实例对象。pageContext 对象表示整个 JSP 页面,可以获取或删除以下对象的任意属性:

  • page
  • request
  • session
  • application

pageContext 常用的方法如下:

pageContext 常用的方法
方  法
说  明
Object findAttribute (String AttributeName)按 page、request、session、application 的顺序查找指定的属性,并返回对应的属性值。如果没有相应的属性,则返回 NULL
Object getAttribute (String AttributeName, int Scope)在指定范围内获取属性值。与 findAttribute 不同的是,getAttribute 需要指定查找范围
void removeAttribute(String AttributeName, int Scope)在指定范围内删除某属性
void setAttribute(String AttributeName, Object AttributeValue, int Scope)在指定范围内设置属性和属性值
Exception getException()返回当前页的 Exception 对象
ServletRequest getRequest()返回当前页的 request 对象
ServletResponse getResponse()返回当前页的 response 对象
ServletConfig getServletConfig()返回当前页的 ServletConfig 对象
HttpSession getSession()返回当前页的 session 对象
Object getPage()返回当前页的 page 对象
ServletContext getServletContext()返回当前页的 application 对象

示例:使用 PageContext 对象取得不同范围的属性值。index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        request.setAttribute("info", "request范围的值");
        session.setAttribute("info", "session范围的值");
        application.setAttribute("info", "application范围的值");
    %>
    利用 pageContext 取出以下范围内各值(方法一):
    <br> request 设定的值:<%=pageContext.getRequest().getAttribute("info")%>
    <br> session 设定的值:<%=pageContext.getSession().getAttribute("info")%>
    <br> application 设的值:<%=pageContext.getServletContext().getAttribute("info")%>
    <hr>
    利用pageContext取出以下范围内各值(方法二):
    <br> 范围1(page)内的值:<%=pageContext.getAttribute("info", 1)%>
    <br> 范围2(request)内的值:<%=pageContext.getAttribute("info", 2)%>
    <br> 范围3(session)内的值:<%=pageContext.getAttribute("info", 3)%>
    <br> 范围4(application)内的值:<%=pageContext.getAttribute("info", 4)%>
    <hr>
    利用 pageContext 修改或删除某个范围内的值:    
    <br> 修改 request 设定的值:
    <% pageContext.setAttribute("info", "修改request范围的值", 2);%>
    <%=pageContext.getRequest().getAttribute("info")%>

    <br> 删除 session 设定的值:
    <%pageContext.removeAttribute("info",3);%>       
    <%=session.getAttribute("info")%>
</body>
</html>

运行结果如下:

源代码:


JSP page对象

JSP page 的实质是 java.lang.Object 对象,相当于 Java 中的 this 关键字。page 对象是指当前的 JSP 页面本身,在实际开发中并不常用。page 对象的常用方法如下:

page 对象的常用方法
方  法
说  明
class getClass()返回当前页面所在类
int hashCode()返回当前页面的 hash 代码
String toString()将当前页面所在类转换成字符串
boolean equals(Object obj)比较对象和指定的对象是否相等
void copy (Object obj)把对象复制到指定的对象中
Object clone()复制对象

示例:下面通过一个简单的例子来演示 page 中的方法。index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        Object obj;
        obj = null;
    %>
    返回当前页面所在类:<%=page.getClass()%>
    <br> 返回当前页面的 hash 代码:<%=page.hashCode()%>
    <br> 转换成 String 类的对象:<%=page.toString()%>
    <br> page和obj比较:<%=page.equals(obj)%>
    <br> page和this比较:<%=page.equals(this)%>
</body>
</html>

运行结果如下:


JSP Cookie的使用

Cookie 不是 JSP 内置对象,而是由 Netscape 公司发明,用来跟踪用户会话(session)的方式。Cookie 由服务器生成并发送给浏览器(客户端),浏览器会将其以文本文件的形式存储在某个目录下。

例如,IE 浏览器把 cookie 信息保存在类似于 C://windows//cookies 的目录下,当用户再次访问某个网站时,服务器就会要求浏览器查找并返回之前发送的 cookie 信息,来识别此用户。识别用户通常有以下步骤:
1.服务器把 cookie 信息发送到浏览器,例如:用户 ID、用户名称等信息。
2.浏览器在本地存储这些信息。
3.浏览器再次向服务器发送请求时,它会同时将本地存储的 cookie 信息一同发送给服务器,然后服务器使用这些信息来识别用户或其它。

cookie 的作用表现在以下方面:

  • 对特定对象的追踪,如用户的访问次数、最后访问时间等。
  • 统计网页的浏览次数。
  • 在 cookie 有效期内,记录用户的登录信息,简化下一次的登录过程。
  • 实现各种个性化服务,如”最近浏览过的商品“。

注意:由于 cookie 会将用户的个人信息保存在客户端,如用户名、计算机名、以及浏览和登录的网站等。这些信息可能会包含一些比较敏感的内容,所以从安全角度来说,使用 cookie 存在着一定的风险,因此不建议在 cookie 中保存比较重要或隐私的内容。

cookie方法:

cookie 常用方法
方法
说明
public void setDomain(String pattern)设置 cookie 的域名,如 biancheng.net
public String getDomain()获取 cookie 的域名
public void setMaxAge(int expiry)设置 cookie 有效期,单位:秒,默认仅在当前会话中存在
public int getMaxAge()获取 cookie 有效期,单位:秒,默认为 -1,表示 cookie 保存到浏览器关闭为止
public String getName()返回 cookie 的名称,名称创建后将不能被修改
public void setValue(String newValue)设置 cookie 的值
public String getValue()获取 cookie 的值
public void setPath(String uri)设置 cookie 的路径,默认为当前页面目录以及子目录下的所有 URL 
public String getPath()获取 cookie 的路径
public void setSecure(boolean flag)设置 cookie 是否要加密传输
public void setComment(String purpose)设置 cookie 注释
public String getComment()返回 cookie 注释,如果 cookie 没有注释,则返回 null

JSP使用cookie步骤:
1)创建 cookie 对象:创建 cookie 对象,name 代表 cookie 的名称,value 表示该名称对应的值,语法如下:Cookie cookie = new Cookie(String name,String value); 注意:name 和 value 中不能包含空格和以下字符:[ ] ( ) = , " / ? @ : ;
2)写入 cookie:创建 cookie 对象后,调用 response.addCookie() 方法写入 cookie,代码如下:response.addCookie(cookie);
3)设置 cookie 有效期:调用 setMaxAge() 方法设置 cookie 的有效期(单位:秒),如将 cookie 有效期设置为 24 小时,代码如下:cookie.setMaxAge(60*60*24);
4)读取cookie:调用 request.getCookies() 方法读取 cookie,该方法返回 HTTP 请求中的 cookie 对象数组,需要通过遍历进行访问。

示例:通过 HTML 表单将客户端数据提交到 index.jsp 中,并设置 cookie。login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <form action="index.jsp" method="get">
        站点名: <input type="text" name="name"> <br />
        网址: <input type="text" name="url" />
        <input type="submit" value="提交" />
    </form>
</body>
</html>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*"%>
<%
    // 解决中文乱码  
    String str = URLEncoder.encode(request.getParameter("name"), "utf-8");
    // 创建cookie对象
    Cookie name = new Cookie("name", str);
    Cookie url = new Cookie("url", request.getParameter("url"));
    // 设置cookie有效期为24小时。
    name.setMaxAge(60 * 60 * 24);
    url.setMaxAge(60 * 60 * 24);
    // 在响应头部添加cookie
    response.addCookie(name);
    response.addCookie(url);
%>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <b>网站名:</b>
    <%=request.getParameter("name")%><br>
    <b>网址:</b>
    <%=request.getParameter("url")%>
</body>
</html>

运行结果如下:

读取cookie:调用 request.getcookies() 方法,在 cookie.jsp 页面中读取 cookie,cookie.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        Cookie cookie = null; //创建cookie对象
        Cookie[] cookies = null;
        // 获取 cookie 的数据
        cookies = request.getCookies();
        if (cookies != null) {
            out.println("<h2> 获取cookie名称与对应值</h2>");
            for (int i = 0; i < cookies.length; i++) {
                cookie = cookies[i];
                out.print("参数名 : " + cookie.getName());
                out.print("<br>");
                out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") + " <br>");
                out.print("------------------------------------<br>");
            }
        } else {
            out.println("<h2>cookie为空</h2>");
        }
    %>
</body>
</html>

运行结果如下:

删除cookie步骤:

  • 获取 cookie
  • 将要删除的 cookie 有效期设置为 0
  • 调用 response.addCookie() 方法重新写入 cookie

删除参数名为“name”的 cookie,cookie.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        Cookie cookie = null; //创建cookie对象
        Cookie[] cookies = null;
        // 获取 cookie 的数据
        cookies = request.getCookies();
        if (cookies != null) {
            out.println("<h2> 获取cookie名称与对应值</h2>");
            for (int i = 0; i < cookies.length; i++) {
                cookie = cookies[i];
                //删除参数名为name的cookie
                //compareTo() 方法用于两个相同数据类型的比较,如果指定的数与参数相等返回 0。如果指定的数小于参数返回 -1。如果指定的数大于参数返回 1。
                if ((cookie.getName()).compareTo("name") == 0) {
                    cookie.setMaxAge(0);
                    response.addCookie(cookie);
                    out.print("删除 cookie: " + cookie.getName() + "<br/>");
                }
                out.print("参数名 : " + cookie.getName());
                out.print("<br>");
                out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") + " <br>");
                out.print("------------------------------------<br>");
            }
        } else {
            out.println("<h2>cookie为空</h2>");
        }
    %>
</body>
</html>

刷新 cookie.jsp 页面,运行结果如下:

注:也可以手动在浏览器中删除 cookie。

session和cookie的区别

session 和 cookie 的区别
session
cookie
将信息保存在服务器将信息保存在客户端
保存的值是 Object 类型保存的值是 String 类型
session 存储的数据随会话的结束而结束cookie 可以长期保存在客户端
安全性高,可以保存重要的信息安全性低,通常用于保存不重要的信息

实际开发中,需要根据不同的业务需求来选择采用哪种技术,例如,用户的用户名和密码等敏感信息不能使用 cookie 存储,淘宝购物的”最近浏览过的商品“,则可以使用 cookie 存储在客户端。

EL表达式   ${EL表达式}

之前的 JSP 页面中,我们经常使用 JSP 表达式来输出变量或者页面之间传递的参数,大大降低了页面的可读性。为了简化 JSP 页面,JSP 2.0 新增了 EL(Expression Language)表达式语言。EL 提供了更为简洁、方便的形式来访问变量和参数,不仅可以简化 JSP 页面代码,还会使开发者的逻辑变得更加清晰 。

EL表达式的语法:EL 表达式语法以${开头,以}结束,中间为合法的表达式。EL 表达式语法如下:

${EL表达式}

示例:${param.name} 表示获取参数 name 的值,它等同于 <%=request.getParameter('name') %>。从形式和语法上可以看出,EL 表达式简化了 JSP 原有的表达式。在实际开发中,EL 表达式也是经常使用的方式。

EL 表达式定义了许多运算符,如算术运算符、比较运算符、逻辑运算符等,使用这些运算符,可以使 JSP 页面更加简洁。

EL算术运算符:算术运算符用来进行加、减、乘、除和求余,EL 表达式算术运算符如下:

EL算术运算符
EL算术运算符说明范例结果
+${5+2}7
-${5-2}3
*${5*2}10
/ 或 div ${5/2}2
% 或 mod求余${5%2}1
注意:EL 的+运算符与 Java 的+运算符不一样,它无法实现两个字符串的连接运算。如果该运算符连接的两个值不能转换为数值型的字符串,则会拋出异常;反之,EL 会自动将这两个字符转换为数值型数据,再进行运算。

EL 表达式中还可以使用 ( ) 改变优先级,例如:${2+3*2} 等于 8,${(2+3)*2} 等于 10。

EL比较运算符:比较运算符用来实现两个表达式的比较,进行比较的表达式可以是数值型或字符串。EL 表达式比较运算符如下:

EL比较运算符
EL比较运算符说明范例结果
== 或 eq等于${6==6} 或 ${6 eq 6}
${"A"="a"} 或 ${"A" eq "a"}
true
false
!= 或 ne不等于${6!=6} 或 ${6 ne 6}
${“A"!=“a”} 或 ${“A” ne “a”}
false
true
< 或 lt小于${3<8} 或 ${3 lt 8}
${"A"<"a"} 或 ${"A" lt "a"}
true
true
> 或 gt大于${3>8} 或 ${3 gt 8}
${"A">"a"} 或 ${"A" gt "a"}
false
false
<= 或 le小于等于${3<=8} 或 ${3 le 8}
${"A"<="a"} 或 ${"A" le "a"
true
true
>= 或 ge大于等于${3>=8} 或 ${3 ge 8}
${"A">="a"} 或 ${"A" ge "a"}
false
false

EL逻辑运算符:逻辑运算符两边的表达式必须是布尔型(Boolean)变量,其返回结果也是布尔型(Boolean)。EL 表达式逻辑运算符如下:

EL逻辑运算符
EL逻辑运算符说明范例结果
&& 或 and${2>1&&3<4 } 或 ${2>1and3<4 }true
|| 或 or${2<1||3>4} 或 ${2<1or3>4}false
! 或 not${!(2>4)} 或 ${not (2>4)}true

EL其它运算符
1). 和 [ ]:.和[ ]是 EL 中最常用的运算符,用来访问 JavaBean 中的属性和隐式对象的数据。一般情况下,.用来访问 JavaBean 属性或 Map 类型的值,[ ]用来访问数组或者列表的元素。
2)empty:empty 用来判断 EL 表达式中的对象或者变量是否为空。若为空或者 null,返回 true,否则返回 false。
3)条件运算符:EL 表达式中,条件运算符的语法和 Java 的完全一致,其中条件表达式用于指定一个判定条件,该表达式的结果为 boolean 类型。如果该表达式的运算结果为 true,则返回表达式 1 的值;反之,返回表达式 2 的值。如下:${条件表达式?表达式1:表达式2}

示例:运算符演示如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="net.biancheng.*,java.util.*"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h4>.运算符</h4>
    <%
        Site site = new Site();
        site.setName("编程帮");
        site.setUrl("www.biancheng.net");
        session.setAttribute("site", site);
    %>
    欢迎来到${site.name},我们的网址是:${site.url}
    <h4>[]运算符</h4>
    <%
        List tutorials = new ArrayList();
        tutorials.add("Java");
        tutorials.add("Python");
        session.setAttribute("tutorials", tutorials);
        HashMap siteMap = new HashMap();
        siteMap.put("one", "编程帮");
        siteMap.put("two", "C语言中文网");
        session.setAttribute("site", siteMap);
    %>
    tutorials 中的内容:${tutorials[0]},${tutorials[1]}
    <br> siteMap 中的内容:${site.one},${site.two}
    <h4>empty和条件运算符</h4>
    <!-- 当 cart 变量为空时,输出购物车为空,否则输出cart -->
    <% String cart = null;%>
    ${empty cart?"购物车为空":cart}
</body>
</html>

在eclipse界面D:\eclipsecode\jspDemo\src\main\webapp\WEB-INF\ 下依次手动创建 classes\net\biancheng\Site.java 文件

package net.biancheng;

public class Site {
    private String name;
    private String url;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
}

javac D:\eclipsecode\jspDemo\src\main\webapp\WEB-INF\classes\net\biancheng\Site.java后生成Site.class文件,运行结果如下:

EL运算符优先级:在 EL 表达式中,多种运算符混合运算时,优先级如下表所示(由高至低,由左至右):

EL运算符优先级
序号优先级
1[] .
2()
3-(负)、not、! 、empty
4* 、 / 、 div 、% 、mod
5+、-(减)
6<、>、<=、>=、lt、gt、le、ge
7==、!-、eq、ne
8&&、and
9||、or
10${A?B:C}

EL保留字:保留字是系统预留的名称。在为变量命名时,应该避开这些预留的名称,以免程序编译时发生错误。EL 表达式的保留字如下表所示:

EL保留字
ltlegtge
eqnetruefalse
andornotinstanceof
divmodemptynull

禁用EL表达式:如果不想使用 EL 表达式,可以禁用 EL 表达式。禁用 EL 表达式有以下 3 种方法:
1)禁用单个EL表达式:在 EL 表达式前加 \,例如:\${2+3},页面输出:${2+3}

2)禁用当前页面的EL表达式:将 page 指令中的 isELIgnored 属性设置为 true:<%@ page isELIgnored="true" %>

3)禁用整个Web应用的EL表达式:在 web.xml 中配置 元素,如下:

<jsp-property-group>
    <url-pattern>*jsp</url-pattern>
    <el-ignored>false</el-ignored>
</jsp-propery-group>

EL内置对象
为了显示方便,EL 表达式语言提供了许多内置对象,可以通过不同的内置对象来输出不同的内容。EL 表达式内置对象如下:

EL表达式内置对象
内置对象说明
pageScope获取 page 范围的变量
requestScope获取 request 范围的变量
sessionScope获取 session 范围的变量
applicationScope获取 application 范围的变量
param相当于 request.getParameter(String name),获取单个参数的值
paramValues相当于 request.getParameterValues(String name),获取参数集合中的变量值
header相当于 request.getHeader(String name),获取 HTTP 请求头信息
headerValues相当于 request.getHeaders(String name),获取 HTTP 请求头数组信息
initParam相当于 application.getInitParameter(String name),获取 web.xml 文件中的参数值
cookie相当于 request.getCookies(),获取 cookie 中的值
pageContext表示当前 JSP 页面的 pageContext 对象

从以上方法可以看出,EL 表达式可以输出 4 种属性范围的内容。如果在不同的属性范围中设置了同一个属性名称,则按照 page、request、session、application 的顺序依次查找。我们也可以指定要取出哪一个范围的变量,例如:${pagesScope.name},表示取出 page 范围的 name 变量。
例 1:EL 表达式获取 4 种不同范围的属性,代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        pageContext.setAttribute("info", "page属性范围");
        request.setAttribute("info", "request属性范围");
        session.setAttribute("info", "session属性范围");
        application.setAttribute("info", "application属性范围");
    %>
    <h2>不同属性范围的值</h2>
    <hr />
    不指定范围:${info}
    <br> page 属性内容:${pageScope.info}
    <br> request 属性内容:${requestScope.info}
    <br>session 属性内容:${sessionScope.info}
    <br>application 属性内容:${applicationScope.info}
</body>
</html>

运行结果如下:

EL表达式读取不同范围的属性内容
例 2:下面演示 param、paramValues 的使用方法,login.jsp 代码如下:

表单的 action 属性也是一个 EL 表达式。${pageContext.request.contextPath} 等价于 <%=request.getContextPath()%>,意思就是取出部署的应用程序名,或者是当前的项目名称。本例中项目名称是 jspDemo,因此 ${pageContext.request.contextPath} 或 <%=request.getContextPath()%> 就等于 /jspDemo。实际项目中应该这样写:${pageContext.request.contextPath}/login.jsp。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%
        request.setCharacterEncoding("UTF-8");
    %>
    <form action="${pageContext.request.contextPath}/index.jsp" method="post">
        网站名称: <input type="text" name="name" value="编程帮" /> <br>
        <input type="text" name="name" value="C语言中文网" /> <br>
        <input type="text" name="name" value="百度" /> <br>
       
        网址: <input type="text" name="url" value="www.biancheng.net" /> <br>
        <input type="submit" value="提交" />
    </form>
</body>
</html>

index.jsp 页面代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <% request.setCharacterEncoding("UTF-8"); %>
    请求参数值:${param.url}
    <br> ${paramValues.name[0]}
</body>
</html>

运行结果如下:

例 3:下面演示 cookie 的使用方法。login.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <% response.addCookie(new Cookie("name", "编程帮")); %>
    <a href="index.jsp">跳转到index.jsp页面</a>
</body>
</html>

index.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    ${cookie.name.value }
</body>
</html>

运行结果如下所示:


JSP JavaBean的使用   类.set属性名("值")   类.get属性名()

JavaBean 是可以重复使用的一个组件,是遵守 JavaBean API 规范的 Java 类。一般情况下,JavaBean 要满足以下要求:

  • JavaBean 是一个公共类,并提供无参公有的构造方法
  • 需要被序列化并且实现了 Serializable 接口
  • 属性私有化
  • 具有公共的访问属性的 getter 和 setter 方法

Java 之父 James Gosling 在设计 Java 语言,为 Java 组件中封装数据的 Java 类进行命名时,看到了桌子上的咖啡豆,于是灵机一动,就把它命名为“JavaBean”,bean 翻译为“豆、豆子”。

在程序中,开发人员所要处理的无非是业务逻辑和数据,而这两种操作都可以封装成 JavaBean,一个应用程序中会使用很多的 JavaBean。由此可见,JavaBean 组件是应用程序的重要组成部分。

JavaBean示例:下面是一个 JavaBean 的简单示例,User.java如下:

package entity;
import java.io.Serializable;
public class User implements Serializable {
   
    private static final long serialVersionUID = 1L;
   
    private int id;
    private String name;

    public User() {
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getId() {
        return id;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

JavaBean属性
一个 JavaBean 可以有多个属性,属性应该是可以访问的,属性类型可以是任何合法的 Java 数据类型,包括自定义的类。JavaBean 的每个属性通常都具有相应的 setter 和 getter 方法,setter 方法称为属性修改器,getter 方法称为属性访问器,如下:
1)getPropertyName( )
属性访问器以小写的 get 作为前缀,后跟属性名,且属性名的第一个字母也要改为大写。例如:属性名称为 name,则方法名称为 getName( ),用来读取该属性值。
2)setPropertyName( )
属性修改器以小写的 set 作为前缀,后跟属性名,且属性名的第一个字母也要改为大写。例如:属性名称为 name,则方法名称为 setName( ),用来设置该属性值。

JavaBean 的某个属性也可以只有 set 方法或 get 方法,这样的属性称之为只写或只读属性。

通过 getter 和 setter 方法访问 JavaBean,如下:

public static void main(String[] args) {
    User u = new User();// 创建对象
    u.setName("编程帮");// 设置对象的值
    System.out.println(u.getName());// 输出“编程帮”
}

JSP自定义标签   &lt;前缀:标签名&gt;

自定义标签就是用户(开发者)自己定义的标签。自定义标签可以让 JSP 页面中不含有 Java 代码,只含有 HTML 代码和部分标签,就能实现业务逻辑的调用。具体参考:JSP标准标签库(JSTL)个人使用指南

自定义标签的优点如下:

  • 减少 JSP 页面对脚本的需求和依赖性
  • 将 JSP 页面和业务逻辑分开,增加了程序的可维护性
  • 可重复调用相同的业务逻辑,增加了程序的可重用性

使用自定义标签步骤如下:

  1. 自定义标签实现类
  2. 编写 tld 标签库描述文件
  3. 在 JSP 页面中使用自定义标签

自定义标签的语法
使用 taglib 指令指定 tld 文件的路径。其中:prefix 指定自定义标签的前缀,uri 指定 tld 文件的路径。

<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>

prefix 表示自定义标签的前缀,tagname 表示自定义标签的名称,attr 表示自定义标签的属性,value 表示自定义标签的属性值。使用自定义标签有以下 2 种格式:

<prefix:tagname attr1=value1....attrn=valuen />

<prefix:tagname attr1=value1....attrn=valuen > 
    标签体
</prefix:tagname>  

简单示例
下面创建一个简单的自定义标签 <bc:Hello>。
1. 创建处理标签的Java类
创建处理标签的 HelloTag 类,HelloTag.java代码如下:

package net.biancheng;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport {
    public void doTag() throws JspException, IOException {
        JspWriter out = getJspContext().getOut();
        out.println("欢迎来到编程帮,我们的网址是:www.biancheng.net");
    }
}

以上代码重写了doTag() 方法,getJspContext() 方法用来获取 JspContext 对象,并将 out.println 中的内容传递给 JspWriter 对象。

注意:SimpleTagSupport 实现了 SimpleTag 接口,该接口是 JSP 2.X 标签库新增的自定义标签接口类。该接口极其简单,提供了 doTag() 方法去处理自定义标签中的逻辑以及标签体。相对来说,JSP 1.X 比较复杂,处理标签的 Java 类需要继承 TagSupport 类,重写 doStartTag()、doAfterBody()、doEndTag() 等方法。

2. 创建tld标签库描述文件
tld 文件采用 XML 文件格式进行描述,后缀名为 .tld ,用来将写好的类映射成 JSP 标签。tld 文件保存在 WEB-INF 目录下,为了管理方便,可以在目录下创建 tlds 文件夹。tld 文件中只能有一对 taglib 标签,taglib 标签下可以有多个 tag 标签,每个 tag 标签代表一个自定义标签。文章后面会对 tld 文件中的各个标签进行说明。custom.tld 代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib 
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
    "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <!-- 标签库版本号 -->
    <tlib-version>1.0</tlib-version>
    <!-- JSP版本号 --> 
    <jsp-version>2.0</jsp-version>
    <!-- 当前标签库的前缀 -->
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>net.biancheng.HelloTag</tag-class>
        <!-- 正文内容正文内容,没有则用 empty 表示 -->
        <body-content>empty</body-content>
        <!-- 自定义标签的功能描述 -->
        <description>输出内容</description>
    </tag>
</taglib>  

3. 使用自定义标签
使用自定义标签和使用 JSTL 标签是一样的, JSP 文件中使用 Hello 标签代码如下。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="bc" uri="WEB-INF/tlds/custom.tld"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <bc:Hello />
</body>
</html>


运行结果如下:

tld标签库描述文件
tld 文件中常用的标签有 taglib、tag、attribute 和 variable。下面以 custom.tld 文件为例介绍其含义。
1、<taglib>标签:<taglib> 标签用来设置整个标签库信息,其说明如下表所示。

属  性说  明
tlib-version标签库版本号
jsp-versionJSP版本号
short-name当前标签库的前缀
uri页面引用的自定义标签的 uri 地址
name自定义标签名称
tag-class自定义标签实现类路径
description自定义标签的功能描述
attribute自定义标签的指定属性,可以有多个

2、<tag>标签:<tag> 标签用来定义标签具体的内容,其说明如下表所示。

属  性说  明
name自定义标签名称
tag-class自定义标签实现类
body-content有 3 个值:empty(表示没有标签体)、JSP(表示标签体可以加入 JSP 程序代码)、tagdependent(表示标签体中的内容由标签自己处理)
description自定义标签的功能描述
attribute自定义标签功能的指定属性,可以有多个
variable自定义标签的变量属性

3、<attribute>标签:<attribute> 标签用来定义 <tag> 标签中的属性,其说明如下表所示。

属  性说  明
name属性名称
description属性描述
required指定属性是否是必须的,默认值:false
rtexprvalue属性值是否支持 JSP 表达式
type定义该属性的 Java 类型,默认值:String
fragment如果声明了该属性,属性值将被视为一个 JspFragment

4、<variable> 标签:<variable> 标签用来定义 <tag> 标签中的变量属性,其说明如下表所示。

属  性说  明
declare变量声明
description变量描述
name-from-attribute指定的属性名称,其值为变量,在调用 JSP 页面时可以使用的名字
name-given变量名(标签使用时的变量名)
scope变量的作用范围,有 3 个值:NESTED 开始和结束标签之间、AT_BEGIN 从开始标签到页面结束、AT_END 从结束标签之后到页面结束
variable-class变量的 Java 类型,默认值:String

自定义标签属性
在自定义标签中设置属性,自定义标签类中必须有相应的 setter 方法。为 bc:Hello 标签添加 message 属性,HelloTag.java 类代码如下:

package net.biancheng;

import java.io.IOException;
import java.io.StringWriter;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport {
    private String message;

    public void setMessage(String message) {
        this.message = message;
    }

    StringWriter sw = new StringWriter();

    public void doTag() throws JspException, IOException {
        if (message != null) {
            // 从属性中使用 message
            JspWriter out = getJspContext().getOut();
            out.println(message);
        } else {
            // 从内容中使用 message
            getJspBody().invoke(sw);
            getJspContext().getOut().println(sw.toString());
        }
    }
}

可以如下手动编译,也可以不手动编译,eclipse程序会自己自动编译的:
javac -encoding utf-8 D:\eclipsecode\jspDemo\src\main\webapp\WEB-INF\classes\net\biancheng\HelloTag.java

**javac编译的时候报错“错误: **程序包javax.servlet不存在”,由于servlet和JSP不是Java平台JavaSE(标准版)的一部分,而是Java EE(企业版)的一部分,因此,必须告知编译器servlet的位置。将环境变量CLASSPATH的值设置添加:D:\apache-tomcat-9.0.79\lib\servlet-api.jar

  1. 搜索servlet-api.jar所在文件夹:D:\apache-tomcat-9.0.79\lib
  2. 将环境变量CLASSPATH的值添加:D:\apache-tomcat-9.0.79\lib\servlet-api.jar
  3. 除了设置classpath以及servlet-api.jar外,还有一点:把lib下的servlet-api.jar拷贝到 C:\Program Files\Java\jre-1.8\lib\ext下。(jre下建议也复制个相同的lib/ext)
  4. classpath下添加 %JAVA_HOME%\lib\j2ee.jar

下面使用 <attribute> 标签为 <bc:Hello> 标签添加 message 属性,custom.tld 代码如下:,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib 
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
    "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>net.biancheng.HelloTag</tag-class>
        <!-- 正文内容类型,若没有正文内容,则用 empty 表示 -->
        <body-content>tagdependent</body-content>
        <attribute>
            <name>message</name>
        </attribute>
    </tag>
</taglib>  

这时就可以在 JSP 页面中使用 message 属性了,iindex.jsp如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="bc" uri="WEB-INF/tlds/custom.tld"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <bc:Hello message="欢迎来到编程帮2,我们的网址是:www.biancheng.net"/>
</body>
</html>

运行结果如下:

自定义标签的标签体
可以像 JSTL 标签库一样在标签中包含消息内容,如在 Hello 标签中包含内容,JSP 使用格式如下:

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

<bc:Hello>
   欢迎来到编程帮3,我们的网址是:www.biancheng.net
</bc:Hello>

修改 HelloTag 类,如下:

package net.biancheng;

import java.io.IOException;
import java.io.StringWriter;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport {
    StringWriter sw = new StringWriter();
    public void doTag() throws JspException, IOException {
        getJspBody().invoke(sw);
        getJspContext().getOut().println(sw.toString());
    }
}

修改 custom.tld 文件,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib 
        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" 
    "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>2.0</jsp-version>
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>net.biancheng.HelloTag</tag-class>
        <!-- 正文内容类型,若没有正文内容,则用 empty 表示 -->
        <body-content>tagdependent</body-content>
    </tag>
</taglib> 

运行结果如下:



JSP文件上传   &lt;input type="file"&gt;

文件上传(Commons-FileUpload)就是对文件进行读写,需要编写大量的代码来实现,而且容易发生异常。幸运的是,有很多实用的工具,可以帮助我们实现文件上传的功能,其中应用比较多的是 Commons-FileUpload 组件。本节我们在 JSP 中使用 Commons-FileUpload 组件来实现文件上传。Commons-FileUpload 组件具有以下特点:

  • 使用简单:Commons-FileUpload 可以内嵌到 JSP 页面中,所以只需要编写少量的代码就可以完成文件的上传功能。
  • 能够全程控制上传内容:使用 Commons-FileUpload 组件提供的对象及操作方法,可以获得上传文件的信息,即文件名称、类型和大小等。
  • 能够控制上传文件的大小和类型:为了避免在上传过程中出现异常数据,Commons-FileUpload 组件提供了相应的方法来控制上传文件。

Commons-FileUpload 组件依赖于 FileUpload 和 Commons,需要 commons-fileupload-xx.jarcommons-io-xx.jar 组件API文件。

Commons-FileUpload组件API:
下面我们介绍一下 Commons-FileUpload 组件的接口和实现类。
1、ServletFileUpload类:ServletFileUpload 类用于实现文件上传操作,常用方法如下:

方  法说  明
public void setSizeMax(long sizeMax)设置上传文件总量的最大值 (包含文件和表单数据)
public List parseRequest(HttpServletRequest req)解析 form 表单提交的数据,返回一个 FileItem 实例的集合
public static final boolean isMultipartContent(HttpServletRequest req)判断请求信息中的内容是否是”multipart/form-data“类型,是则返回 true,否则返回 false。
public void setHeaderEncoding(String encoding)设置转换时所使用的字符集编码

2、FileItem接口:FileItem 接口用于封装单个表单字段元素的数据,一个表单字段对应一个 FileItem 实例,本节示例中使用的是其实现类 DiskFileItem。FileItem 接口提供的常用方法如下:

方  法说  明
public boolean isFormField()用于判断 FileItem 类对象封装的数据是一个普通文本表单字段,还是一个文件表单字段,如果是普通表单字段则返回 true,否则返回 false。因此,可以使用该方法判断是否为普通表单域,还是文件上表单域。
public String getName()获取文件上传的文件名
public String getFieldName()返回表单字段元素的 name 属性值
public long getSize()获取上传文件的大小
public String getString()将 FileItem 对象中保存的主体内容以一个字符串返回。其重载方法 public String getString(String encoding) 中的参数用指定的字符集编码方式
public void write()将 FileItem 对象中保存的主体内容保存到指定的文件中。

3、FileItemFactory接口与实现类:创建 ServletFileUpload 实例需要依赖 FileItemFactory 工厂接口。DiskFileItemFactory 是 FileItemFactory 接口的实现类,该类的常用方法如下。

方  法说  明
public void setSizeThreshold(int sizeThreshold)设置内存缓冲区的大小
public void setRepository(String path)设置临时文件存放的目录

JSP上传文件
JSP 和 HTML Form 标签一起使用,来允许用户把文件上传到服务器。上传的文件可以是文本文件、图像文件或其它任何文档。创建上传文件表单时,需要注意以下几点:

  • 表单的 method 属性必须设置为 POST 方法,不能使用 GET 方法。
  • 表单 enctype 属性应设置为 multipart/form-data。
  • 表单 action 属性应设置为对应的 Servlet,用来处理文件上传的逻辑代码,下面示例中使用 FileUploadServlet 处理逻辑。
  • 使用 <input.../> 标签上传单个文件,属性 type="file"。上传多个文件需添加多个 <input .../> 标签。

拓展:表单的 enctype 属性有以下 3 个值
application/x-www-form-urlencoded:默认值,用于处理少量文本数据的传递。向服务器发送大量的文件或二进制数据时,效率很低。
multipart/form-data:上传二进制数据,只有使用了 multipart/form-data 才能完整的传递文件数据,进行上传操作。
text/plain:用于向服务器传递大量文本数据,适用于电子邮件的应用。

示例:在 fileupload.jsp 页面创建上传文件的表单,代码如下。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <h3>上传文件</h3>
    <form action="FileuploadServlet" method="post" enctype="multipart/form-data">
        <input type="file" name="file" size="50" /> <br /> <br />
        <input type="submit" value="上传" />
    </form>
</body>
</html>

在 FileuploadServlet.java 中处理文件上传逻辑,代码如下:

package net.biancheng;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/FileuploadServlet")
public class FileuploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    // 上传文件存储目录
    private static final String UPLOAD_DIRECTORY = "upload";
    // 设置缓冲区大小
    private static final int MEMORY_THRESHOLD = 1024 * 1024 * 3; // 3MB
    // 设置上传单个文件的最大值
    private static final int MAX_FILE_SIZE = 1024 * 1024 * 40; // 40MB
    private static final int MAX_REQUEST_SIZE = 1024 * 1024 * 50; // 50MB
    /**
     * 文件上传判断逻辑
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // 判断是否为多媒体上传
        if (!ServletFileUpload.isMultipartContent(request)) {
            // 如果不是则停止
            PrintWriter writer = response.getWriter();
            writer.println("Error: 表单必须包含 enctype=multipart/form-data");
            writer.flush();
            return;
        }
        // 配置fileItem工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置缓冲区大小
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        // 创建文件上传处理器
        ServletFileUpload upload = new ServletFileUpload(factory);
        // 设置上传文件大小的最大值
        upload.setFileSizeMax(MAX_FILE_SIZE);
        // 设置上传文件总量的最大值 (包含文件和表单数据)
        upload.setSizeMax(MAX_REQUEST_SIZE);
        // 防止上传文件名称乱码
        upload.setHeaderEncoding("UTF-8");
        // 构造临时路径来存储上传的文件
        String uploadPath = getServletContext().getRealPath("/") + File.separator + UPLOAD_DIRECTORY;
        // 如果目录不存在则创建
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
        try {
            // 解析请求的内容提取文件数据
            @SuppressWarnings("unchecked")
            List<FileItem> formItems = upload.parseRequest(request);
            if (formItems != null && formItems.size() > 0) {
                // 迭代表单数据
                for (FileItem item : formItems) {
                    // 处理表单中不存在的字段
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);
                        // 在控制台输出文件的上传路径
                        System.out.println(filePath);
                        // 保存文件到硬盘
                        item.write(storeFile);
                        request.setAttribute("message", "文件上传成功!");
                    }
                }
            }
        } catch (Exception ex) {
            request.setAttribute("message", "错误信息: " + ex.getMessage());
        }
        // 跳转到 message.jsp
        getServletContext().getRequestDispatcher("/message.jsp").forward(request, response);
    }
}

关于getServletContext().getRealPath("/") + File.separator + UPLOAD_DIRECTORY:
1、ServletContext.getRealPath() 是从当前servlet 在tomcat 中的存放文件夹开始计算起的。ServletContext.getRealPath("/") 返回 "D:\apache-tomcat-9.0.79\webapps\MavenDemo"。ServletContext.getRealPath("/") + "xxx" 返回 "D:\apache-tomcat-9.0.79\webapps\MavenDemo\xxx"。
2、File.separator在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 ''。
3、UPLOAD_DIRECTORY为upload。
即为D:\apache-tomcat-9.0.79\webapps\MavenDemo\upload,此处需要手动创建upload文件夹。

关于getServletContext().getRequestDispatcher("/message.jsp").forward( request, response);服务器跳转浏览器中的地址不会改变,你放到attribute里面的字段不会丢失,用户根本不知道你后面跳转到哪个页面去了。response.sendRedirect()客户端跳转,俗称重定向,浏览器的地址会改变的,参数不能放到attribute里面,只能放到response.sendRedirect("/xxx.do?a=1&b=2")类似这样传递参数,用户是可以看到地址栏改变的。js中的window.location.href属于重定向,是客户端跳转的范畴

在 message.jsp 页面显示是否上传成功,代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <% out.println(request.getAttribute("message")); %>	
</body>
</html>

pom.xml代码如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>net.biancheng</groupId>
  <artifactId>MavenDemo</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>MavenDemo Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
	<dependency>
		<groupId>javax.servlet.jsp</groupId>
		<artifactId>jsp-api</artifactId>
		<version>2.2</version>
		<scope>provided</scope>
	</dependency>
	<dependency>
		<groupId>com.liferay</groupId>
		<artifactId>org.apache.commons.fileupload</artifactId>
		<version>1.2.2.LIFERAY-PATCHED-1</version>
	</dependency>
	<dependency>
		<groupId>com.fujieid.jap.http</groupId>
		<artifactId>jap-http</artifactId>
		<version>1.0.0</version>
	</dependency>	
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>javax.servlet-api</artifactId>
		<version>4.0.1</version>
		<scope>provided</scope>
	</dependency>	
  </dependencies>
  <build>
    <finalName>MavenDemo</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <resources>
		<resource>
			<directory>src/main/resources</directory>
		</resource>
	</resources>
  </build>
</project>

拷贝 D:\apache-tomcat-9.0.79\lib\servlet-api.jar、commons-fileupload-1.5.jarcommons-io-2.13.0.jar 到 “C:\Program Files\Java\jdk-1.8\jre\lib\ext” 目录
选择需要上传的文件,点击上传。运行结果如下:



打开控制台可以看到文件的上传路径,根据路径可以找到上传的文件。该示例控制台输出内容如下:
D:\apache-tomcat-9.0.79\webapps\MavenDemo\\upload\葵花宝典.txt

message.jsp 页面修改为:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<!DOCTYPE html>
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    ${message}
</body>
</html>


servlet使用的2.3版本默认不支持EL表达式,更改版本2.4支持EL,D:\eclipsecode\MavenDemo\src\main\webapp\WEB-INF\web.xml修改为:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>

选择需要上传的文件,点击上传。运行结果如下:


JSP JDBC访问数据库

动态网页的主要特点就是能及时更新数据,这些更新的数据来源于数据库。学习本节内容需要您了解 JDBC,可以使用 JDBC 连接 MySQL 数据库。本节数据库使用 MySQL 商业版 8.0.34,你可以点击 MySQL官方网站 下载相应的 jar 包。

注意:MySQL 8.0 及之后的版本与之前的数据库连接有所不同:
首先MySQL6开始驱动 com.mysql.jdbc.Driver 更换为 com.mysql.cj.jdbc.Driver。
MySQL 8.0 及以上版本需要显式关闭 SSL 连接和指定 CST。

MySQL 8.0 及以上版本加载驱动与连接数据库方式如下:

<sql:setDataSource var="snapshot" driver="com.mysql.cj.jdbc.Driver"
     url="jdbc:mysql://localhost:3306/test?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
     user="root"  password="123456"/>

<sql:setDataSource>标签用来配置数据源或者将数据源信息存储在某作用域的变量中,用来作为其它JSTL数据库操作的数据源。语法格式

<sql:setDataSource
  var="<string>"
  scope="<string>"
  dataSource="<string>"
  driver="<string>"
  url="<string>"
  user="<string>"
  password="<string>"/>
属性描述是否必要默认值
driver要注册的JDBC驱动
url数据库连接的JDBC URL
user数据库用户名
password数据库密码
dataSource事先准备好的数据库
var代表数据库的变量默认设置
scopevar属性的作用域Page

将website.sql文件导入到mysql中

mysql -u root -p123456
show databases;
create database website;
show databases;
use website;
source D:/website.sql  //(注意:如果文件路径是复制来的,需要将""全部换成“/”,否则会报错)
show tables;
select * from website;


直接下载JSTL两个jar包,tomcat9的1.1版本为D:\apache-tomcat-9.0.79\webapps\MavenDemo\WEB-INF\lib\jstl.jar,standard.jar,tomcat9的1.2版本为D:\apache-tomcat-9.0.79\webapps\examples\WEB-INF\lib\taglibs-standard-impl-1.2.5.jar、taglibs-standard-spec-1.2.5.jar,放在 项目的WEB-IF下的lib下,并buildPath导入这两个包。

修改pom文件,官网下载驱动仓库下载驱动 mysql-connector-j-8.0.33.jar ,然后buildpath后,修改pom文件如下:

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.33</version>
	</dependency>

下面使用 JSTL 对数据表中的数据进行增删改查。

示例:创建 website 数据表,并插入数据。点击下载 SQL 文件(下载后需要解压)。crud.jsp 代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<%@ page isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>编程帮(www.biancheng.net)</title>
</head>
<body>
    <%-- JDBC驱动名、数据库 URL、数据库的用户名与密码,useUnicode=true&characterEncoding=utf-8 防止中文乱码 --%>
    <sql:setDataSource var="snapshot" driver="com.mysql.jdbc.Driver"
        url="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8"
        user="root" password="123456" />

    <%-- 插入数据
    <sql:update dataSource="${snapshot}" var="result">
        INSERT INTO website (NAME,url,age,country) VALUES ('京东', 'https://www.jd.com/', 15, 'CN');
    </sql:update> --%>

    <%-- 删除website表中id为9数据
    <c:set var="empid" value="9" />
    <sql:update dataSource="${snapshot}" var="count">
         DELETE FROM website WHERE id = ?
         <sql:param value="${empid}" />
    </sql:update>--%>

    <%-- 修改website表中id为8数据
    <c:set var="newid" value="8" />
    <sql:update dataSource="${snapshot}" var="count">
         UPDATE website SET name="zc" WHERE id = ?
         <sql:param value="${newid}" />
    </sql:update>--%>

    <%-- 查询数据 --%>
    <sql:query dataSource="${snapshot}" var="result">
        SELECT * from website;
    </sql:query>
    <table border="1" width="100%">
        <tr><th>id</th><th>name</th><th>url</th><th>age</th><th>country</th></tr>
        <c:forEach var="row" items="${result.rows}">
            <tr>
                <td><c:out value="${row.id}" /></td>
                <td><c:out value="${row.name}" /></td>
                <td><c:out value="${row.url}" /></td>
                <td><c:out value="${row.age}" /></td>
                <td><c:out value="${row.country}" /></td>
            </tr>
        </c:forEach>
    </table>
</body>
</html>

D:\eclipsecode\MavenDemo\src\main\webapp\WEB-INF\web.xml改为:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>

查询数据

插入数据

删除website表中id为9数据

修改website表中id为8数据


JSP 分页功能

当数据有几万、几十万甚至上百万时,用户必须要拖动页面才能浏览更多的数据,很大程度的影响了用户体验。这时可以使用分页来显示数据,能够使数据更加清晰直观,且不受数量的限制。

分页的方式主要分为两种:

  • 将查询结果以集合等形式保存在内存中,翻页时从中取出一页数据显示。该方法可能导致用户浏览到的是过期数据,且如果数据量非常大,查询一次数据就会耗费很长时间,存储的数据也会占用大量的内存开销。
  • 每次翻页时只从数据库中检索出本页需要的数据。虽然每次翻页都需要查询数据库,但查询的记录数相对较少,总体开销不大,再配以连接池等技术进行优化,可以达到比较高的效率。

实现分页显示的步骤:
1)确定每页显示的数据数量:确定每页显示的记录条数,即每次从数据库中需要查询出多少条记录用于页面显示。通常这个数量可以在开发时定义好,也可以由用户来选择。
2)计算显示的总页数:根据每页显示的记录条数计算总页数,即会产生多少页数据。显示的记录数量是已知的,而数据库中符合条件的记录总数是未知的,因此想要得到总页数,需要先得到符合条件的记录总数。
3)编写SQL语句:使用相应的 SQL 语句实现分页需求。例如,MySQL 中的 LIMIT 子句,SqlServer 中的 TOP、Oracle 中的 ROWNUM 等。

注意:本节使用的是 MySQL 数据库。不同的数据库厂商实现分页的 SQL 语句会存在差异,因此在实际应用中根据数据库的不同,需要修改相应的 SQL 语句。

示例:

实际使用:

mysql -u root -p123456
show databases;
create database zc;
show databases;
use zc;
show tables;
drop table websites;
select * from websites;

CREATE TABLE `websites` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '站点名称',
  `url` varchar(255) NOT NULL DEFAULT '',
  `alexa` int(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名',
  `country` char(10) NOT NULL DEFAULT '' COMMENT '国家',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;


INSERT INTO `websites` VALUES
	( '1', 'Google', 'https://www.google.cm/', '1', 'USA' ),
	( '2', '淘宝', 'https://www.taobao.com/', '13', 'CN' ),
	( '3', 'zc自学教程', 'http://www.zc.com', '5892', 'CN' ),
	( '4', '微博', 'http://weibo.com/', '20', 'CN' ),
	( '5', '百度', 'https://www.baidu.com/', '5', 'CN' ),
	( '6', 'Facebook', 'https://www.facebook.com/', '3', 'USA' ),
	( '7', 'youtube', 'https://www.youtube.com/', '2', 'USA' ),
	( '8', 'QQ', 'http://qq.com/', '4', 'CN' ),
	( '9', '360', 'https://www.360.cn', '9', 'CN' ),
	( '10', '新浪', 'https://www.sina.com.cn', '15', 'CN' ),
	( '11', '京东', 'https://www.jd.com', '23', 'CN' );

select * from websites;

Website.java 类的代码如下:

package z.c.entity;

public class Website {
    private int id;
    private String name;
    private String url;
    private int alexa;
    private String country;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public int getAlexa() {
        return alexa;
    }
    public void setAlexa(int alexa) {
        this.alexa = alexa;
    }
    public String getCountry() {
        return country;
    }
    public void setCountry(String country) {
        this.country = country;
    }
}

基于方便代码管理的考虑,将有关分页的数据封装到一个 Page 类中,其中包括每页显示的数据数量、数据的总数量、显示的总页数、当前页码、每页显示的数据集合。Page.java 代码如下:

package z.c.utils;

import java.util.List;
import z.c.entity.Website;

public class Page {
    // 总页数
    private int totalPageCount = 0;
    // 页面大小,即每页显示记录数
    private int pageSize = 3;
    // 记录总数
    private int totalCount;
    // 当前页码
    private int currPageNo = 1;
    // 每页网站集合
    private List<Website> websiteList;
    public int getTotalPageCount() {
        return totalPageCount;
    }
    public void setTotalPageCount(int totalPageCount) {
        this.totalPageCount = totalPageCount;
    }
    public int getPageSize() {
        return pageSize;
    }
    public void setPageSize(int pageSize) {
        if (pageSize > 0)
            this.pageSize = pageSize;
    }
    public int getTotalCount() {
        return totalCount;
    }
    public void setTotalCount(int totalCount) {
    	System.out.println(totalCount);
        if (totalCount > 0) {
            this.totalCount = totalCount;
            // 计算总页数
            totalPageCount = this.totalCount % pageSize == 0 ? (this.totalCount / pageSize) : (this.totalCount / pageSize + 1);
        }
    }
    public int getCurrPageNo() {
        if (currPageNo == 0)
            return 0;
        return currPageNo;
    }
    public void setCurrPageNo(int currPageNo) {
        if (currPageNo > 0)
            this.currPageNo = currPageNo;
    }
    public List<Website> getWebsiteList() {
        return websiteList;
    }
    public void setWebsiteList(List<Website> websiteList) {
        this.websiteList = websiteList;
    }
}

在 setTotalCount() 方法中,根据记录总数和每页显示记录数计算出总页数,使用三元运算符?:进行处理。如果记录总数能被每页显示记录数整除,则总页数为两者的商,如果不能被整除,则余出的记录数单独列为一页,总页数为两者的商再加 1。

WebsiteDao.java 代码如下:

package z.c.dao;

import java.util.List;
import z.c.entity.Website;

public interface WebsiteDao {
    // 查询总条数
    public int getTotcalCount();
    //根据起始下标和每页显示数量查询数据
    public List<Website> getPageWebsiteList(int pageNo, int pagesize);
}

WebsiteDaoImpl.java 代码如下:

package z.c.dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import z.c.entity.Website;

public class WebsiteDaoImpl implements WebsiteDao {
    /**
     * 连接数据库
     */
    public static Connection getConnection() {
        Connection con = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/zc?characterEncoding=utf-8&useSSL=false", "root", "123456");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return con;
    }
    /**
     * 查询总条数
     */
    @Override
    public int getTotcalCount() {
        int count = 0;
        try {
            Connection con = getConnection();
            String sql = "SELECT COUNT(id) FROM websites";
            PreparedStatement ps = con.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            rs.next();
            count = rs.getInt(1);  //rs.getInt(1)表示从结果集的第一行第一列里拿值,这个值应是个数值类型的所以用int来取,就是getInt()方法内。
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return count;
    }
    @Override
    public List<Website> getPageWebsiteList(int pageNo, int pagesize) {
        PreparedStatement ps;
        ResultSet rs;
        List<Website> list = new ArrayList<Website>();
        try {
            Connection con = getConnection();
            String sql = "SELECT * FROM websites LIMIT " + (pageNo - 1) * pagesize + "," + pagesize;
            ps = con.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()) {
                Website website = new Website();
                website.setId(rs.getInt(1));
                website.setName(rs.getString(2));  //rs.getInt(int index) 和 rs.getInt(String columName)可以通过索引或者列名来获得查询结果集中的某一列的值。
                website.setUrl(rs.getString(3));
                website.setAlexa(rs.getInt(4));
                website.setCountry(rs.getString(5));
                list.add(website);
            }
            con.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }
}

view.jsp 页面代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.*,z.c.dao.*,z.c.entity.*,z.c.utils.*"%>


<html>
<head>
<title>JSP 分页</title>
</head>
<body>
    <%
        WebsiteDao websiteDao = new WebsiteDaoImpl();
        String pageNo = request.getParameter("page") != null ? request.getParameter("page") : "1";
        int currPageNo = Integer.parseInt(pageNo);
        Page p = new Page();
        p.setCurrPageNo(currPageNo);
        p.setPageSize(3);
        
        p.setTotalCount(websiteDao.getTotcalCount());
        
        List<Website> websiteList = websiteDao.getPageWebsiteList(p.getCurrPageNo(), p.getPageSize());
        p.setWebsiteList(websiteList);
        out.print("<h1>当前页面: " + currPageNo + "</h1>");
        out.print("<table border='1' cellpadding='4' width='60%'>");
        out.print("<tr><th>id</th><th>name</th><th>url</th><th>alexa</th><th>country</th></tr>");
        for (Website e : websiteList) {
            out.print("<tr><td>" + e.getId() + "</td><td>" + e.getName() + "</td><td>" + e.getUrl() + "</td><td>" + e.getAlexa() + "</td><td>" + e.getCountry() + "</td></tr>");
        }
        out.print("</table>");
        
        for (int i = 1; i <= p.getTotalPageCount(); i++) {
    %>
    		<a href="view.jsp?page=<%=i%>"><%=i%></a>
    <%
        }
    %>
</body>
</html>

运行结果如下:


Eclipse 创建Servlet

动态网站项目中创建一个servlet:右键点击项目名称(jspDemo)或项目中的src,在弹出的菜单中选择 "New -> Other… -> Web -> Servlet",如果找不到Servlet 选项,选择"File-->New->Other-->Web->Servlet":

jspDemo->New->Other
Web->Servlet
Servlet类名称->包名称->Next->Next->finish
Servlet类


ZcEclipseServlet.java如下:

package z.c;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ZcEclipseServlet
 */
@WebServlet("/ZcEclipseServlet")
public class ZcEclipseServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ZcEclipseServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		// 使用 UTF-8 防止中文乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
       //response.getWriter().append("Served at: ").append(request.getContextPath());
        response.getWriter().write("zc自学教程:http://www.zc.com");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

创建 /jspDemo/webapp/WEB-INF/web.xml 文件(如果没有),代码如下所示:

<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="2.5"   
    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_2_5.xsd">  
  <servlet>  
     <!-- 类名 -->  
    <servlet-name>ZcEclipseServlet</servlet-name>
    <!-- 所在的包 -->  
    <servlet-class>z.c.ZcEclipseServlet</servlet-class>  
  </servlet>  
  <servlet-mapping>  
    <servlet-name>ZcEclipseServlet</servlet-name>  
    <!-- 访问的网址 -->  
    <url-pattern>/jspDemo/ZcEclipseServlet</url-pattern>  
    </servlet-mapping>  
</web-app>  

运行结果如下:


JSP 结构

网络服务器需要一个 JSP 引擎,也就是一个容器来处理 JSP 页面。容器负责截获对 JSP 页面的请求。本教程使用内嵌 JSP 容器的 Apache 来支持 JSP 开发。

JSP 容器与 Web 服务器协同合作,为JSP的正常运行提供必要的运行环境和其他服务,并且能够正确识别专属于 JSP 网页的特殊元素。

JSP 处理

以下步骤表明了 Web 服务器是如何使用JSP来创建网页的:

  • 就像其他普通的网页一样,您的浏览器发送一个 HTTP 请求给服务器。
  • Web 服务器识别出这是一个对 JSP 网页的请求,并且将该请求传递给 JSP 引擎。通过使用 URL或者 .jsp 文件来完成。
  • JSP 引擎从磁盘中载入 JSP 文件,然后将它们转化为 Servlet。这种转化只是简单地将所有模板文本改用 println() 语句,并且将所有的 JSP 元素转化成 Java 代码。
  • JSP 引擎将 Servlet 编译成可执行类,并且将原始请求传递给 Servlet 引擎。
  • Web 服务器的某组件将会调用 Servlet 引擎,然后载入并执行 Servlet 类。在执行过程中,Servlet 产生 HTML 格式的输出并将其内嵌于 HTTP response 中上交给 Web 服务器。
  • Web 服务器以静态 HTML 网页的形式将 HTTP response 返回到您的浏览器中。
  • 最终,Web 浏览器处理 HTTP response 中动态产生的HTML网页,就好像在处理静态网页一样。

以上提及到的步骤可以用下图来表示:

一般情况下,JSP 引擎会检查 JSP 文件对应的 Servlet 是否已经存在,并且检查 JSP 文件的修改日期是否早于 Servlet。如果 JSP 文件的修改日期早于对应的 Servlet,那么容器就可以确定 JSP 文件没有被修改过并且 Servlet 有效。这使得整个流程与其他脚本语言(比如 PHP)相比要高效快捷一些。总的来说,JSP 网页就是用另一种方式来编写 Servlet 而不用成为 Java 编程高手。除了解释阶段外,JSP 网页几乎可以被当成一个普通的 Servlet 来对待。


JSP 生命周期

理解JSP底层功能的关键就是去理解它们所遵守的生命周期。JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命周期还包括将JSP文件编译成servlet。以下是JSP生命周期中所走过的几个阶段:

  • 编译阶段:servlet容器编译servlet源文件,生成servlet类
  • 初始化阶段:加载与JSP对应的servlet类,创建其实例,并调用它的初始化方法
  • 执行阶段:调用与JSP对应的servlet实例的服务方法
  • 销毁阶段:调用与JSP对应的servlet实例的销毁方法,然后销毁servlet实例

很明显,JSP生命周期的四个主要阶段和servlet生命周期非常相似,下面给出图示:

JSP编译
当浏览器请求JSP页面时,JSP引擎会首先去检查是否需要编译这个文件。如果这个文件没有被编译过,或者在上次编译后被更改过,则编译这个JSP文件。编译的过程包括三个步骤:

  • 解析JSP文件。
  • 将JSP文件转为servlet。
  • 编译servlet。

JSP初始化
容器载入JSP文件后,它会在为请求提供任何服务前调用jspInit()方法。如果您需要执行自定义的JSP初始化任务,复写jspInit()方法就行了,就像下面这样。一般来讲程序只初始化一次,servlet也是如此。通常情况下您可以在jspInit()方法中初始化数据库连接、打开文件和创建查询表。

public void jspInit(){
  // 初始化代码
}

JSP执行
这一阶段描述了JSP生命周期中一切与请求相关的交互行为,直到被销毁。当JSP网页完成初始化后,JSP引擎将会调用_jspService()方法。_jspService()方法需要一个HttpServletRequest对象和一个HttpServletResponse对象作为它的参数,就像下面这样。_jspService()方法在每个request中被调用一次并且负责产生与之相对应的response,并且它还负责产生所有7个HTTP方法的回应,比如GET、POST、DELETE等等。

void _jspService(HttpServletRequest request,
                 HttpServletResponse response)
{
   // 服务端处理代码
}

JSP清理
JSP生命周期的销毁阶段描述了当一个JSP网页从容器中被移除时所发生的一切。jspDestroy()方法在JSP中等价于servlet中的销毁方法。当您需要执行任何清理工作时复写jspDestroy()方法,比如释放数据库连接或者关闭文件夹等等。jspDestroy()方法的格式如下:

public void jspDestroy()
{
   // 清理代码
}

实例:JSP生命周期代码life.jsp如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>life.jsp</title>
</head>
<body>

<%! 
  private int initVar=0;
  private int serviceVar=0;
  private int destroyVar=0;
%>
  
<%!
  public void jspInit(){
    initVar++;
    System.out.println("jspInit(): JSP被初始化了"+initVar+"次");
  }
  public void jspDestroy(){
    destroyVar++;
    System.out.println("jspDestroy(): JSP被销毁了"+destroyVar+"次");
  }
%>

<%
  serviceVar++;
  System.out.println("_jspService(): JSP共响应了"+serviceVar+"次请求");

  String content1="初始化次数 : "+initVar;
  String content2="响应客户请求次数 : "+serviceVar;
  String content3="销毁次数 : "+destroyVar;
%>
<h1>zc自学教程 JSP 测试实例</h1>
<p><%=content1 %></p>
<p><%=content2 %></p>
<p><%=content3 %></p>
</body>
</html>

浏览器打开该页面,输出结果为:


JSP 语法

脚本程序
脚本程序可以包含任意量的Java语句、变量、方法或表达式,只要它们在脚本语言中是有效的。脚本程序的语法格式:<% 代码片段 %>或者,您也可以编写与其等价的XML语句:<jsp:scriptlet> 代码片段 </jsp:scriptlet>,任何文本、HTML标签、JSP元素必须写在脚本程序的外面。示例:hello.jsp

<html>
<head><title>Hello World</title></head>
<body>
Hello World!<br/>
<%
out.println("Your IP address is " + request.getRemoteAddr());
%>
</body>
</html>

注意:开启D:\apache-tomcat-9.0.79\bin\tomcat9w.exe,把hello.jsp放置在 D:\apache-tomcat-9.0.79\webapps\ROOT 目录下,打开浏览器并在地址栏中输入http://localhost:8080/hello.jsp。注:0:0:0:0:0:0:0:1是ipv6的表现形式,对应ipv4来说相当于127.0.0.1 。运行后得到以下结果:

中文编码问题
如果我们要在页面正常显示中文,我们需要在 JSP 文件头部添加代码:<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>接下来我们将以上程序修改为:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
Hello World!<br/>
<%
out.println("你的 IP 地址 " + request.getRemoteAddr());
%>
</body>
</html>

这样中文就可以正常显示了:

JSP声明
一个声明语句可以声明一个或多个变量、方法,供后面的Java代码使用。在JSP文件中,您必须先声明这些变量和方法然后才能使用它们。JSP声明的语法格式:<%! declaration; [ declaration; ]+ ... %> 或者,也可以编写与其等价的XML语句:<jsp:declaration> 代码片段 </jsp:declaration>程序示例:

<%! int i = 0; %> 
<%! int a, b, c; %> 
<%! Circle a = new Circle(2.0); %> 

JSP表达式
一个JSP表达式中包含的脚本语言表达式,先被转化成String,然后插入到表达式出现的地方。由于表达式的值会被转化成String,所以您可以在一个文本行中使用表达式而不用去管它是否是HTML标签。表达式元素中可以包含任何符合Java语言规范的表达式,但是不能使用分号来结束表达式。JSP表达式的语法格式:<%= 表达式 %> 同样,也可以编写与之等价的XML语句:<jsp:expression> 表达式 </jsp:expression>程序示例:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<p>
   今天的日期是: <%= (new java.util.Date()).toLocaleString()%>
</p>
</body> 
</html> 

运行后得到以下结果:

JSP注释
JSP注释主要有两个作用:为代码作注释以及将某段代码注释掉。JSP注释的语法格式:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<%-- 该部分注释在网页中不会被显示--%> 
<p>
   今天的日期是: <%= (new java.util.Date()).toLocaleString()%>
</p>
</body> 
</html> 

运行后得到以下结果:

不同情况下使用注释的语法规则:

语法描述
<%-- 注释 --%>JSP注释,注释内容不会被发送至浏览器甚至不会被编译
<!-- 注释 -->HTML注释,通过浏览器查看网页源代码时可以看见注释内容
<\%代表静态 <%常量
%\>代表静态 %> 常量
\'在属性中使用的单引号
\"在属性中使用的双引号

JSP指令
JSP指令用来设置与整个JSP页面相关的属性。JSP指令语法格式:<%@ directive attribute="value" %>这里有三种指令标签:

指令描述
<%@ page ... %>定义页面的依赖属性,比如脚本语言、error页面、缓存需求等等
<%@ include ... %>包含其他文件
<%@ taglib ... %>引入标签库的定义,可以是自定义标签

JSP行为
JSP行为标签使用XML语法结构来控制servlet引擎。它能够动态插入一个文件,重用JavaBean组件,引导用户去另一个页面,为Java插件产生相关的HTML等等。行为标签只有一种语法格式,它严格遵守XML标准:<jsp:action_name attribute="value" />行为标签基本上是一些预先就定义好的函数,下表罗列出了一些可用的JSP行为标签:

语法描述
jsp:include用于在当前页面中包含静态或动态资源
jsp:useBean寻找和初始化一个JavaBean组件
jsp:setProperty设置 JavaBean组件的值
jsp:getProperty将 JavaBean组件的值插入到 output中
jsp:forward从一个JSP文件向另一个文件传递一个包含用户请求的request对象
jsp:plugin用于在生成的HTML页面中包含Applet和JavaBean对象
jsp:element动态创建一个XML元素
jsp:attribute定义动态创建的XML元素的属性
jsp:body定义动态创建的XML元素的主体
jsp:text用于封装模板数据

JSP隐含对象
JSP支持九个自动定义的变量,江湖人称隐含对象。这九个隐含对象的简介见下表:

对象描述
requestHttpServletRequest类的实例
responseHttpServletResponse类的实例
outPrintWriter类的实例,用于把结果输出至网页上
sessionHttpSession类的实例
applicationServletContext类的实例,与应用上下文有关
configServletConfig类的实例
pageContextPageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
page类似于Java类中的this关键字
exceptionexception 类的对象,代表发生错误的 JSP 页面中对应的异常对象

控制流语句
JSP提供对Java语言的全面支持。您可以在JSP程序中使用Java API甚至建立Java代码块,包括判断语句和循环语句等等。

判断语句
if…else 块,请看下面这个例子:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%! int day = 3; %> 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<h3>IF...ELSE 实例</h3>
<% if (day == 1 || day == 7) { %>
      <p>今天是周末</p>
<% } else { %>
      <p>今天不是周末</p>
<% } %>
</body> 
</html> 

运行后得到以下结果:

现在来看switch…case块,与if…else块有很大的不同,它使用out.println(),且整个都装在脚本程序的标签中,就像下面这样:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%! int day = 1; %> 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<h3>SWITCH...CASE 实例</h3>
<% 
switch(day) {
case 0:
   out.println("星期天");
   break;
case 1:
   out.println("星期一");
   break;
case 2:
   out.println("星期二");
   break;
case 3:
   out.println("星期三");
   break;
case 4:
   out.println("星期四");
   break;
case 5:
   out.println("星期五");
   break;
default:
   out.println("星期六");
}
%>
</body> 
</html> 

浏览器访问,运行后得出以下结果:

循环语句 JSP For
在JSP程序中可以使用Java的三个基本循环类型:for,while,和 do…while。for循环为例,输出的不同字体大小的"zc教程":

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%! int fontSize; %> 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<h3>For 循环实例</h3>
<%for ( fontSize = 1; fontSize <= 3; fontSize++){ %>
   <font color="green" size="<%= fontSize %>">
    zc自学教程
   </font><br />
<%}%>
</body> 
</html> 

运行后得到以下结果:

循环语句 JSP While
将上例改用 while 循环来写:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%! int fontSize=0; %> 
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc自学教程(zc.com)</title>
</head>
<body>
<h3>For 循环实例</h3>
<%while ( fontSize <= 3){ %>
   <font color="green" size="<%= fontSize %>">
    zc自学教程
   </font><br />
   <%fontSize++;%>
<%}%>
</body> 
</html> 

浏览器访问,输出结果为(fontSize 初始化为0,所以多输出了一行):

JSP运算符

JSP支持所有Java逻辑和算术运算符。下表罗列出了JSP常见运算符,优先级从高到底:

类别操作符结合性
后缀() [] . (点运算符)左到右
一元++ - - ! ~右到左
可乘性* / % 左到右
可加性+ - 左到右
移位>> >>> <<  左到右 
关系> >= < <=  左到右
相等/不等== != 左到右
位与左到右
位异或左到右
位或左到右
逻辑与&& 左到右
逻辑或|| 左到右
条件判断?: 右到左
赋值= += -= *= /= %= >>= <<= &= ^= |= 右到左
逗号 左到右 

JSP 字面量
JSP语言定义了以下几个字面量:

  • 布尔值(boolean):true 和 false;
  • 整型(int):与 Java 中的一样;
  • 浮点型(float):与 Java 中的一样;
  • 字符串(string):以单引号或双引号开始和结束;
  • Null:null。

JSP 指令

JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言。语法格式:<%@ directive attribute="value" %>指令可以有很多个属性,它们以键值对的形式存在,并用逗号隔开。JSP中的三种指令标签:

指令描述
<%@ page ... %>定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
<%@ include ... %>包含其他文件
<%@ taglib ... %>引入标签库的定义

Page指令
Page指令为容器提供当前页面的使用说明。一个JSP页面可以包含多个page指令。Page指令的语法格式:<%@ page attribute="value" %>等价的XML格式:<jsp:directive.page attribute="value" />下表列出与Page指令相关的属性:

属性描述
buffer指定out对象使用缓冲区的大小
autoFlush控制out对象的 缓存区
contentType指定当前JSP页面的MIME类型和字符编码
errorPage指定当JSP页面发生异常时需要转向的错误处理页面
isErrorPage指定当前页面是否可以作为另一个JSP页面的错误处理页面
extends指定servlet从哪一个类继承
import导入要使用的Java类
info定义JSP页面的描述信息
isThreadSafe指定对JSP页面的访问是否为线程安全
language定义JSP页面所用的脚本语言,默认是Java
session指定JSP页面是否使用session
isELIgnored指定是否执行EL表达式
isScriptingEnabled确定脚本元素能否被使用

Include指令
JSP可以通过include指令来包含其他文件。被包含的文件可以是JSP文件、HTML文件或文本文件。包含的文件就好像是该JSP文件的一部分,会被同时编译执行。Include指令的语法格式:<%@ include file="文件相对 url 地址" %>include 指令中的文件名实际上是一个相对的 URL 地址。如果您没有给文件关联一个路径,JSP编译器默认在当前路径下寻找。等价的XML语法:<jsp:directive.include file="文件相对 url 地址" />

Taglib指令
JSP API允许用户自定义标签,一个自定义标签库就是自定义标签的集合。Taglib指令引入一个自定义标签集合的定义,包括库路径、自定义标签。Taglib指令的语法:<%@ taglib uri="uri" prefix="prefixOfTag" %>uri属性确定标签库的位置,prefix属性指定标签库的前缀。等价的XML语法:<jsp:directive.taglib uri="uri" prefix="prefixOfTag" />


JSP 动作元素

与JSP指令元素不同的是,JSP动作元素在请求处理阶段起作用。JSP动作元素是用XML语法写成的。利用JSP动作可以动态地插入文件、重用JavaBean组件、把用户重定向到另外的页面、为Java插件生成HTML代码。动作元素只有一种语法,它符合XML标准:<jsp:action_name attribute="value" />动作元素基本上都是预定义的函数,JSP规范定义了一系列的标准动作,它用JSP作为前缀,可用的标准动作元素如下:

语法 描述
jsp:include在页面被请求的时候引入一个文件。
jsp:useBean寻找或者实例化一个JavaBean。
jsp:setProperty设置JavaBean的属性。
jsp:getProperty输出某个JavaBean的属性。
jsp:forward把请求转到一个新的页面。
jsp:plugin根据浏览器类型为Java插件生成OBJECT或EMBED标记。
jsp:element定义动态XML元素
jsp:attribute设置动态定义的XML元素属性。
jsp:body设置动态定义的XML元素内容。
jsp:text在JSP页面和文档中使用写入文本的模板

常见的属性
所有的动作要素都有两个属性:id属性和scope属性。

  • id属性:id属性是动作元素的唯一标识,可以在JSP页面中引用。动作元素创建的id值可以通过PageContext来调用。
  • scope属性:该属性用于识别动作元素的生命周期。 id属性和scope属性有直接关系,scope属性定义了相关联id对象的寿命。 scope属性有四个可能的值: (a) page, (b)request, (c)session, 和 (d) application。

<jsp:include>动作元素
jsp:include动作元素用来包含静态和动态的文件。该动作把指定文件插入正在生成的页面。语法格式如下:<jsp:include page="相对 URL 地址" flush="true" />前面已经介绍过include指令,它是在JSP文件被转换成Servlet的时候引入文件,而这里的jsp:include动作不同,插入文件的时间是在页面被请求的时候。以下是include动作相关的属性列表。

属性 描述
page包含在页面中的相对URL地址。
flush布尔属性,定义在包含资源前是否刷新缓存区。

以下我们定义了两个文件 date.jsp 和 main.jsp,其中D:\apache-tomcat-9.0.79\webapps\ROOT\date.jsp文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<p>
   今天的日期是: <%= (new java.util.Date()).toLocaleString()%>
</p>

main.jsp文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc教程(zc.com)</title>
</head>
<body>

<h2>include 动作实例</h2>
<jsp:include page="date.jsp" flush="true" />

</body>
</html>

现在将以上两个文件放在服务器的根目录下,访问main.jsp文件。显示结果如下:


<jsp:useBean>动作元素
jsp:useBean 动作用来加载一个将在JSP页面中使用的JavaBean。这个功能非常有用,因为它使得我们可以发挥 Java 组件复用的优势。jsp:useBean动作最简单的语法为:<jsp:useBean id="name" class="package.class" />在类载入后,我们既可以通过 jsp:setProperty 和 jsp:getProperty 动作来修改和检索bean的属性。以下是useBean动作相关的属性列表。

属性 描述
class指定Bean的完整包名。
type指定将引用该对象变量的类型。
beanName通过 java.beans.Beans 的 instantiate() 方法指定Bean的名字。

<jsp:setProperty>动作元素
jsp:setProperty用来设置已经实例化的Bean对象的属性,有两种用法。首先,你可以在jsp:useBean元素的外面(后面)使用jsp:setProperty,如下所示,此时,不管jsp:useBean是找到了一个现有的Bean,还是新创建了一个Bean实例,jsp:setProperty都会执行:<jsp:useBean id="myName" ... />...<jsp:setProperty name="myName" property="someProperty" .../> 第二种用法是把jsp:setProperty放入jsp:useBean元素的内部,如下所示,此时,jsp:setProperty只有在新建Bean实例时才会执行,如果是使用现有实例则不执行jsp:setProperty:<jsp:useBean id="myName" ... >...<jsp:setProperty name="myName" property="someProperty" .../></jsp:useBean>jsp:setProperty动作有下面四个属性,如下表:

属性 描述
namename属性是必需的。它表示要设置属性的是哪个Bean。
propertyproperty属性是必需的。它表示要设置哪个属性。有一个特殊用法:如果property的值是"*",表示所有名字和Bean属性名字匹配的请求参数都将被传递给相应的属性set方法。
value value 属性是可选的。该属性用来指定Bean属性的值。字符串数据会在目标类中通过标准的valueOf方法自动转换成数字、boolean、Boolean、 byte、Byte、char、Character。例如,boolean和Boolean类型的属性值(比如"true")通过 Boolean.valueOf转换,int和Integer类型的属性值(比如"42")通过Integer.valueOf转换。value和param不能同时使用,但可以使用其中任意一个。
param param 是可选的。它指定用哪个请求参数作为Bean属性的值。如果当前请求没有参数,则什么事情也不做,系统不会把null传递给Bean属性的set方法。因此,你可以让Bean自己提供默认属性值,只有当请求参数明确指定了新值时才修改默认属性值。

<jsp:getProperty>动作元素
jsp:getProperty动作提取指定Bean属性的值,转换成字符串,然后输出。语法格式如下:<jsp:useBean id="myName" ... />...<jsp:getProperty name="myName" property="someProperty" .../>下表是与getProperty相关联的属性:

属性 描述
name要检索的Bean属性名称。Bean必须已定义。
property表示要提取Bean属性的值

以下实例我们使用了Bean,脚本TestBean.java:

package z.c.main;

public class TestBean {
	   private String message = "zc自学教程";
	 
	   public String getMessage() {
	      return(message);
	   }
	   public void setMessage(String message) {
	      this.message = message;
	   }
}

下面是一个很简单的例子,它的功能是装载一个Bean,然后设置/读取它的message属性。现在让我们在main.jsp文件中调用该Bean.java:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 教程 (zc.com)</title>
</head>
<body>

<h2>Jsp 使用 JavaBean 实例</h2>
<jsp:useBean id="test" class="z.c.main.TestBean" />
 
<jsp:setProperty name="test"  property="message" value="zc自学教程..." />
 
输出信息....<br>
 
<jsp:getProperty name="test" property="message" />

</body>
</html>

文件结构如下:

浏览器访问 http://192.168.230.193:8080/jspDemo/main.jsp ,执行以上文件,输出如下所示:


<jsp:forward> 动作元素
jsp:forward动作把请求转到另外的页面。jsp:forward标记只有一个属性page。语法格式如下所示:<jsp:forward page="相对 URL 地址" />以下是forward相关联的属性:

属性 描述
pagepage属性包含的是一个相对URL。page的值既可以直接给出,也可以在请求的时候动态计算,可以是一个JSP页面或者一个 Java Servlet.

以下实例我们使用了两个文件,分别是: date.jsp 和 main.jsp。其中date.jsp 文件代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<p>
   今天的日期是: <%= (new java.util.Date()).toLocaleString()%>
</p>

main.jsp文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 教程 (zc.com)</title>
</head>
<body>

<h2>forward 动作实例</h2>
<jsp:forward page="date.jsp" />
</body>
</html>

访问main.jsp文件。显示结果如下:


<jsp:plugin>动作元素
jsp:plugin动作用来根据浏览器的类型,插入通过Java插件 运行Java Applet所必需的OBJECT或EMBED元素。如果需要的插件不存在,它会下载插件,然后执行Java组件。 Java组件可以是一个applet或一个JavaBean。plugin动作有多个对应HTML元素的属性用于格式化Java 组件。param元素可用于向Applet 或 Bean 传递参数。以下是使用plugin 动作元素的典型实例:

<jsp:plugin type="applet" codebase="dirname" code="MyApplet.class" width="60" height="80">
   <jsp:param name="fontcolor" value="red" />
   <jsp:param name="background" value="black" />
 
   <jsp:fallback> Unable to initialize Java Plugin </jsp:fallback>
 
</jsp:plugin>

如果你有兴趣可以尝试使用applet来测试jsp:plugin动作元素,元素是一个新元素,在组件出现故障的错误时发送给用户错误信息。


<jsp:element> 、 <jsp:attribute>、 <jsp:body>动作元素
<jsp:element> 、 <jsp:attribute>、 <jsp:body>动作元素动态定义XML元素。动态是非常重要的,这就意味着XML元素在编译时是动态生成的而非静态。以下实例动态定义了XML元素:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 教程(zc.com)</title>
</head>
<body>
<jsp:element name="xmlElement">
<jsp:attribute name="xmlElementAttr">   属性值   </jsp:attribute>
<jsp:body>   XML 元素的主体   </jsp:body>
</jsp:element>
</body>
</html>

浏览器访问以下页面,输出结果如下所示:


<jsp:text>动作元素
<jsp:text>动作元素允许在JSP页面和文档中使用写入文本的模板,语法格式如下:<jsp:text>模板数据</jsp:text>文本模板不能包含重复元素,只能包含文本和EL表达式(注:EL表达式将在后续章节中介绍)。请注意,在XML文件中,您不能使用表达式如 ${whatever > 0},因为>符号是非法的。 你可以使用 ${whatever gt 0}表达式或者嵌入在一个CDATA部分的值。<jsp:text><![CDATA[<br>]]></jsp:text>如果你需要在 XHTML 中声明 DOCTYPE,必须使用到<jsp:text>动作元素,实例如下:

<jsp:text><![CDATA[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">]]></jsp:text>
<head><title>jsp:text action</title></head>
<body>

<jsp:text>      Welcome to JSP Programming      </jsp:text>

</body>
</html>

你可以对以上实例尝试使用jsp:text及不使用该动作元素执行结果的区别。


JSP 隐式对象

JSP隐式对象是JSP容器为每个页面提供的Java对象,开发者可以直接使用它们而不用显式声明。JSP隐式对象也被称为预定义变量。JSP所支持的九大隐式对象:

对象描述
requestHttpServletRequest接口的实例
responseHttpServletResponse接口的实例
outJspWriter类的实例,用于把结果输出至网页上
sessionHttpSession类的实例
applicationServletContext类的实例,与应用上下文有关
configServletConfig类的实例
pageContextPageContext类的实例,提供对JSP页面所有对象以及命名空间的访问
page类似于Java类中的this关键字
ExceptionException类的对象,代表发生错误的JSP页面中对应的异常对象

request对象
request对象是javax.servlet.http.HttpServletRequest 类的实例。每当客户端请求一个JSP页面时,JSP引擎就会制造一个新的request对象来代表这个请求。request对象提供了一系列方法来获取HTTP头信息,cookies,HTTP方法等等。

response对象
response对象是javax.servlet.http.HttpServletResponse类的实例。当服务器创建request对象时会同时创建用于响应这个客户端的response对象。response对象也定义了处理HTTP头模块的接口。通过这个对象,开发者们可以添加新的cookies,时间戳,HTTP状态码等等。

out对象
out对象是 javax.servlet.jsp.JspWriter 类的实例,用来在response对象中写入内容。最初的JspWriter类对象根据页面是否有缓存来进行不同的实例化操作。可以在page指令中使用buffered='false'属性来轻松关闭缓存。JspWriter类包含了大部分java.io.PrintWriter类中的方法。不过,JspWriter新增了一些专为处理缓存而设计的方法。还有就是,JspWriter类会抛出IOExceptions异常,而PrintWriter不会。下表列出了我们将会用来输出boolean,char,int,double,String,object等类型数据的重要方法:

方法描述
out.print(dataTypedt)输出Type类型的值
out.println(dataTypedt)输出Type类型的值然后换行
out.flush()刷新输出流

session对象
session对象是 javax.servlet.http.HttpSession 类的实例。和Java Servlets中的session对象有一样的行为。session对象用来跟踪在各个客户端请求间的会话。

application对象
application对象直接包装了servlet的ServletContext类的对象,是javax.servlet.ServletContext 类的实例。这个对象在JSP页面的整个生命周期中都代表着这个JSP页面。这个对象在JSP页面初始化时被创建,随着jspDestroy()方法的调用而被移除。通过向application中添加属性,则所有组成您web应用的JSP文件都能访问到这些属性。

config对象
config对象是 javax.servlet.ServletConfig 类的实例,直接包装了servlet的ServletConfig类的对象。这个对象允许开发者访问Servlet或者JSP引擎的初始化参数,比如文件路径等。以下是config对象的使用方法,不是很重要,所以不常用:config.getServletName();它返回包含在元素中的servlet名字,注意,元素在 WEB-INF\web.xml 文件中定义。

pageContext 对象
pageContext对象是javax.servlet.jsp.PageContext 类的实例,用来代表整个JSP页面。这个对象主要用来访问页面信息,同时过滤掉大部分实现细节。这个对象存储了request对象和response对象的引用。application对象,config对象,session对象,out对象可以通过访问这个对象的属性来导出。pageContext对象也包含了传给JSP页面的指令信息,包括缓存信息,ErrorPage URL,页面scope等。PageContext类定义了一些字段,包括PAGE_SCOPE,REQUEST_SCOPE,SESSION_SCOPE, APPLICATION_SCOPE。它也提供了40余种方法,有一半继承自javax.servlet.jsp.JspContext 类。其中一个重要的方法就是 removeAttribute(),它可接受一个或两个参数。比如,pageContext.removeAttribute("attrName") 移除四个scope中相关属性,但是下面这种方法只移除特定 scope 中的相关属性:pageContext.removeAttribute("attrName", PAGE_SCOPE);

page 对象
这个对象就是页面实例的引用。它可以被看做是整个JSP页面的代表。page 对象就是this对象的同义词。

exception 对象
exception 对象包装了从先前页面中抛出的异常信息。它通常被用来产生对出错条件的适当响应。


JSP 客户端请求

当浏览器请求一个网页时,它会向网络服务器发送一系列不能被直接读取的信息,因为这些信息是作为HTTP信息头的一部分来传送的。您可以查阅HTTP协议来获得更多的信息。下表列出了浏览器端信息头的一些重要内容,在以后的网络编程中将会经常见到这些信息:

信息描述
Accept指定浏览器或其他客户端可以处理的MIME类型。它的值通常为 image/png 或 image/jpeg
Accept-Charset指定浏览器要使用的字符集。比如 ISO-8859-1
Accept-Encoding指定编码类型。它的值通常为 gzip 或compress
Accept-Language指定客户端首选语言,servlet会优先返回以当前语言构成的结果集,如果servlet支持这种语言的话。比如 en,en-us,ru等等
Authorization在访问受密码保护的网页时识别不同的用户
Connection表明客户端是否可以处理HTTP持久连接。持久连接允许客户端或浏览器在一个请求中获取多个文件。Keep-Alive 表示启用持久连接
Content-Length仅适用于POST请求,表示 POST 数据的字节数
Cookie返回先前发送给浏览器的cookies至服务器
Host指出原始URL中的主机名和端口号
If-Modified-Since表明只有当网页在指定的日期被修改后客户端才需要这个网页。 服务器发送304码给客户端,表示没有更新的资源
If-Unmodified-Since与If-Modified-Since相反, 只有文档在指定日期后仍未被修改过,操作才会成功
Referer标志着所引用页面的URL。比如,如果你在页面1,然后点了个链接至页面2,那么页面1的URL就会包含在浏览器请求页面2的信息头中
User-Agent用来区分不同浏览器或客户端发送的请求,并对不同类型的浏览器返回不同的内容

HttpServletRequest类
request对象是javax.servlet.http.HttpServletRequest类的实例。每当客户端请求一个页面时,JSP引擎就会产生一个新的对象来代表这个请求。request对象提供了一系列方法来获取HTTP信息头,包括表单数据,cookies,HTTP方法等等。接下来将会介绍一些在JSP编程中常用的获取HTTP信息头的方法。详细内容请见下表:

序号方法& 描述
1Cookie[] getCookies()

返回客户端所有的Cookie的数组
2Enumeration getAttributeNames()

返回request对象的所有属性名称的集合
3Enumeration getHeaderNames()

返回所有HTTP头的名称集合
4Enumeration getParameterNames()

返回请求中所有参数的集合
5HttpSession getSession()

返回request对应的session对象,如果没有,则创建一个
6HttpSession getSession(boolean create)

返回request对应的session对象,如果没有并且参数create为true,则返回一个新的session对象
7Locale getLocale()

返回当前页的Locale对象,可以在response中设置
8Object getAttribute(String name)

返回名称为name的属性值,如果不存在则返回null。
9ServletInputStream getInputStream()

返回请求的输入流
10String getAuthType()

返回认证方案的名称,用来保护servlet,比如 "BASIC" 或者 "SSL" 或 null 如果 JSP没设置保护措施
11String getCharacterEncoding()

返回request的字符编码集名称
12String getContentType()

返回request主体的MIME类型,若未知则返回null
13String getContextPath()

返回request URI中指明的上下文路径
14String getHeader(String name)

返回name指定的信息头
15String getMethod()

返回此request中的HTTP方法,比如 GET,,POST,或PUT
16String getParameter(String name)

返回此request中name指定的参数,若不存在则返回null
17String getPathInfo()

返回任何额外的与此request URL相关的路径
18String getProtocol()

返回此request所使用的协议名和版本
19String getQueryString()

返回此 request URL包含的查询字符串
20String getRemoteAddr()

返回客户端的IP地址
21String getRemoteHost()

返回客户端的完整名称
22String getRemoteUser()

返回客户端通过登录认证的用户,若用户未认证则返回null
23String getRequestURI()

返回request的URI
24String getRequestedSessionId()

返回request指定的session ID
25String getServletPath()

返回所请求的servlet路径
26String[] getParameterValues(String name)

返回指定名称的参数的所有值,若不存在则返回null
27boolean isSecure()

返回request是否使用了加密通道,比如HTTPS
28int getContentLength()

返回request主体所包含的字节数,若未知的返回-1
29int getIntHeader(String name)

返回指定名称的request信息头的值
30int getServerPort()

返回服务器端口号

HTTP信息头示例
在这个例子中,我们会使用HttpServletRequest类的getHeaderNames()方法来读取HTTP信息头。这个方法以枚举的形式返回当前HTTP请求的头信息。获取Enumeration对象后,用标准的方式来遍历Enumeration对象,用hasMoreElements()方法来确定什么时候停止,用nextElement()方法来获得每个参数的名字。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!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>zc 教程 (zc.com)</title>
</head>
<body>
<h2>HTTP 头部请求实例</h2>
<table width="100%" border="1" align="center">
<tr bgcolor="#949494">
<th>Header Name</th><th>Header Value(s)</th>
</tr>
<%
   Enumeration<String> headerNames = request.getHeaderNames();
   while(headerNames.hasMoreElements()) {
      String paramName = (String)headerNames.nextElement();
      out.print("<tr><td>" + paramName + "</td>\n");
      String paramValue = request.getHeader(paramName);
      out.println("<td> " + paramValue + "</td></tr>\n");
   }
%>
</table>
</body>
</html>

访问main.jsp,将会得到以下结果:


JSP 服务器响应

Response响应对象主要将JSP容器处理后的结果传回到客户端。可以通过response变量设置HTTP的状态和向客户端发送数据,如Cookie、HTTP文件头信息等。一个典型的响应看起来就像下面这样,状态行包含HTTP版本信息,比如HTTP/1.1,一个状态码,比如200,还有一个非常短的信息对应着状态码,比如OK。:

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
  (空行)
<!doctype ...>
<html>
<head>...</head>
<body>
...
</body>
</html>

下表摘要出了HTTP1.1响应头中最有用的部分,在网络编程中您将会经常见到它们:

响应头描述
Allow指定服务器支持的request方法(GET,POST等等)
Cache-Control指定响应文档能够被安全缓存的情况。通常取值为 publicprivate 或no-cache 等等。 Public意味着文档可缓存,Private意味着文档只为单用户服务并且只能使用私有缓存。No-cache 意味着文档不被缓存。
Connection命令浏览器是否要使用持久的HTTP连接。close 命令浏览器不使用持久HTTP连接,而keep-alive 意味着使用持久化连接。
Content-Disposition让浏览器要求用户将响应以给定的名称存储在磁盘中
Content-Encoding指定传输时页面的编码规则
Content-Language表述文档所使用的语言,比如en, en-us,,ru等等
Content-Length表明响应的字节数。只有在浏览器使用持久化 (keep-alive) HTTP 连接时才有用
Content-Type表明文档使用的MIME类型
Expires指明啥时候过期并从缓存中移除
Last-Modified指明文档最后修改时间。客户端可以 缓存文档并且在后续的请求中提供一个 If-Modified-Since请求头
Location在300秒内,包含所有的有一个状态码的响应地址,浏览器会自动重连然后检索新文档
Refresh指明浏览器每隔多久请求更新一次页面。
Retry-After与503 (Service Unavailable)一起使用来告诉用户多久后请求将会得到响应
Set-Cookie指明当前页面对应的cookie

HttpServletResponse类
response 对象是 javax.servlet.http.HttpServletResponse 类的一个实例。就像服务器会创建request对象一样,它也会创建一个客户端响应。response对象定义了处理创建HTTP信息头的接口。通过使用这个对象,开发者们可以添加新的cookie或时间戳,还有HTTP状态码等等。下表列出了用来设置HTTP响应头的方法,这些方法由HttpServletResponse 类提供:

方法描述
String encodeRedirectURL(String url)对sendRedirect()方法使用的URL进行编码
String encodeURL(String url)将URL编码,回传包含Session ID的URL
boolean containsHeader(String name)返回指定的响应头是否存在
boolean isCommitted()返回响应是否已经提交到客户端
void addCookie(Cookie cookie)添加指定的cookie至响应中
void addDateHeader(String name, long date)添加指定名称的响应头和日期值
void addHeader(String name, String value)添加指定名称的响应头和值
void addIntHeader(String name, int value)添加指定名称的响应头和int值
void flushBuffer()将任何缓存中的内容写入客户端
void reset()清除任何缓存中的任何数据,包括状态码和各种响应头
void resetBuffer()清除基本的缓存数据,不包括响应头和状态码
void sendError(int sc)使用指定的状态码向客户端发送一个出错响应,然后清除缓存
void sendError(int sc, String msg)使用指定的状态码和消息向客户端发送一个出错响应
void sendRedirect(String location)使用指定的URL向客户端发送一个临时的间接响应
void setBufferSize(int size)设置响应体的缓存区大小
void setCharacterEncoding(String charset)指定响应的编码集(MIME字符集),例如UTF-8
void setContentLength(int len)指定HTTP servlets中响应的内容的长度,此方法用来设置 HTTP Content-Length 信息头
void setContentType(String type)设置响应的内容的类型,如果响应还未被提交的话
void setDateHeader(String name, long date)使用指定名称和日期设置响应头的名称和日期
void setHeader(String name, String value)使用指定名称和值设置响应头的名称和内容
void setIntHeader(String name, int value)指定 int 类型的值到 name 标头
void setLocale(Locale loc)设置响应的语言环境,如果响应尚未被提交的话
void setStatus(int sc)设置响应的状态码

接下来的例子使用setIntHeader()方法和setRefreshHeader()方法来模拟一个数字时钟:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 教程 (zc.com)</title>
</head>
<body>
<h2>自动刷新实例</h2>
<%
   // 设置每隔5秒自动刷新
   response.setIntHeader("Refresh", 5);
   // 获取当前时间
   Calendar calendar = new GregorianCalendar();
   String am_pm;
   int hour = calendar.get(Calendar.HOUR);
   int minute = calendar.get(Calendar.MINUTE);
   int second = calendar.get(Calendar.SECOND);
   if(calendar.get(Calendar.AM_PM) == 0)
      am_pm = "AM";
   else
      am_pm = "PM";
   String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
   out.println("当前时间: " + CT + "\n");
%>
</body>
</html>

将以上代码保存为main.jsp,然后通过浏览器访问它。它将会每隔5秒显示一下系统当前时间。我们可以看下以下 Gif 演示图:


JSP HTTP 状态码

HTTP请求与HTTP响应的格式相近,都有着如下结构:

  • 以状态行+CRLF(回车换行)开始
  • 零行或多行头模块+CRLF
  • 一个空行,比如CRLF
  • 可选的消息体比如文件,查询数据,查询输出

举例来说,一个服务器响应头看起来就像下面这样,状态行包含HTTP版本,一个状态码,和状态码相对应的短消息:

HTTP/1.1 200 OK
Content-Type: text/html
Header2: ...
...
HeaderN: ...
  (Blank Line)
<!doctype ...>
<html>
<head>...</head>
<body>
...
</body>
</html>

下表列出了可能会从服务器返回的HTTP状态码和与之关联的消息:

状态码消息描述
100Continue只有一部分请求被服务器接收,但只要没被服务器拒绝,客户端就会延续这个请求
101Switching Protocols服务器交换机协议
200OK请求被确认
201Created请求时完整的,新的资源被创建
202Accepted请求被接受,但未处理完
203Non-authoritative Information 
204No Content 
205Reset Content 
206Partial Content 
300Multiple Choices一个超链接表,用户可以选择一个超链接并访问,最大支持5个超链接
301Moved Permanently被请求的页面已经移动到了新的URL下
302Found被请求的页面暂时性地移动到了新的URL下
303See Other被请求的页面可以在一个不同的URL下找到
304Not Modified 
305Use Proxy 
306Unused已经不再使用此状态码,但状态码被保留
307Temporary Redirect被请求的页面暂时性地移动到了新的URL下
400Bad Request服务器无法识别请求
401Unauthorized被请求的页面需要用户名和密码
402Payment Required目前还不能使用此状态码
403Forbidden禁止访问所请求的页面
404Not Found服务器无法找到所请求的页面
405Method Not Allowed请求中所指定的方法不被允许
406Not Acceptable服务器只能创建一个客户端无法接受的响应
407Proxy Authentication Required在请求被服务前必须认证一个代理服务器
408Request Timeout请求时间超过了服务器所能等待的时间,连接被断开
409Conflict请求有矛盾的地方
410Gone被请求的页面不再可用
411Length Required"Content-Length"没有被定义,服务器拒绝接受请求
412Precondition Failed请求的前提条件被服务器评估为false
413Request Entity Too Large因为请求的实体太大,服务器拒绝接受请求
414Request-url Too Long服务器拒绝接受请求,因为URL太长。多出现在把"POST"请求转换为"GET"请求时所附带的大量查询信息
415Unsupported Media Type服务器拒绝接受请求,因为媒体类型不被支持
417Expectation Failed 
500Internal Server Error请求不完整,服务器遇见了出乎意料的状况
501Not Implemented请求不完整,服务器不提供所需要的功能
502Bad Gateway请求不完整,服务器从上游服务器接受了一个无效的响应
503Service Unavailable请求不完整,服务器暂时重启或关闭
504Gateway Timeout网关超时
505HTTP Version Not Supported服务器不支持所指定的HTTP版本

设置HTTP状态码的方法
下表列出了HttpServletResponse 类中用来设置状态码的方法:

方法描述
public void setStatus ( int statusCode )此方法可以设置任意的状态码。如果您的响应包含一个特殊的状态码和一个文档,请确保在用PrintWriter返回任何内容前调用setStatus方法
public void sendRedirect(String url)此方法产生302响应,同时产生一个 Location 头告诉URL 一个新的文档
public void sendError(int code, String message)此方法将一个状态码(通常为 404)和一个短消息,自动插入HTML文档中并发回给客户端

接下来的例子将会发送407错误码给浏览器,然后浏览器将会告诉您"Need authentication!!!"。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Setting HTTP Status Code - zc 教程 (zc.com)</title>
</head>
<body>
<%
   // 设置错误代码,并说明原因
   response.sendError(407, "Need authentication!!!" );
%>
</body>
</html>

访问以上JSP页面,将会得到以下结果:


JSP 表单处理

我们在浏览网页的时候,经常需要向服务器提交信息,并让后台程序处理。浏览器中使用 GET 和 POST 方法向服务器提交数据。

GET 方法
GET方法将请求的编码信息添加在网址后面,网址与编码信息通过"?"号分隔:http://www.runoops.com/hello?key1=value1&key2=value2GET方法是浏览器默认传递参数的方法,一些敏感信息,如密码等建议不使用GET方法。用get时,传输数据的大小有限制 (注意不是参数的个数有限制),最大为1024字节。

POST 方法
一些敏感信息,如密码等我们可以通过POST方法传递,POST提交数据是隐式的。POST提交数据是不可见的,GET是通过在url里面传递的(可以看一下你浏览器的地址栏)。JSP使用getParameter()来获得传递的参数,getInputStream()方法用来处理客户端的二进制数据流的请求。

JSP 读取表单数据

  • getParameter(): 使用 request.getParameter() 方法来获取表单参数的值。
  • getParameterValues(): 获得如checkbox类(名字相同,但值有多个)的数据。 接收数组变量 ,如checkbox类型
  • getParameterNames():该方法可以取得所有变量的名称,该方法返回一个 Enumeration。
  • getInputStream():调用此方法来读取来自客户端的二进制数据流。

使用URL的 GET 方法实例
以下是一个简单的URL,并使用GET方法来传递URL中的参数:http://localhost:8080/jspDemo/test.jsp?name=zc自学教程&url=http://www.zc.comjspDemo 为项目地址。以下是 getData.jsp 文件的JSP程序用于处理客户端提交的表单数据,我们使用getParameter()方法来获取提交的数据:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>
<h1>使用 GET 方法读取数据</h1>
<ul>
<li><p><b>站点名:</b>
   <%= request.getParameter("name")%>
</p></li>
<li><p><b>网址:</b>
   <%= request.getParameter("url")%>
</p></li>
</ul>
</body>
</html>

接下来我们通过浏览器访问 http://localhost:8080/jspDemo/getData.jsp?name=zc自学教程&url=http://www.zc.com 输出结果如下所示:

使用表单的 GET 方法实例
以下是一个简单的 HTML 表单,将HTML代码保存到getForm.html 文件中。 将该文件放置于当前jsp项目的 webapp 目录下(与 getData.jsp 同一个目录)。该表单通过GET方法将客户端数据提交 到 getData.jsp 文件中:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>

<form action="getData.jsp" method="GET">
站点名: <input type="text" name="name">
<br />
网址: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>

</body>
</html>

通过访问 http://localhost:8080/jspDemo/getForm.html 提交表单数据到 getData.jsp 文件,在 "站点名" 与 "网址" 两个表单中填入信息,并点击 "提交" 按钮,它将输出结果。演示 Gif 图如下所示:

使用表单的 POST 方法实例
接下来让我们使用POST方法来传递表单数据,代码中我们使用 new String((request.getParameter("name")).getBytes("ISO-8859-1"),"UTF-8")来转换编码,防止中文乱码的发生。如下所示postData.jsp 文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>
<h1>使用 POST 方法读取数据</h1>
<ul>
<li><p><b>站点名:</b>
<% // 解决中文乱码的问题
String name = new String((request.getParameter("name")).getBytes("ISO-8859-1"),"UTF-8");
%>
   <%=name%>
</p></li>
<li><p><b>网址:</b>
   <%= request.getParameter("url")%>
</p></li>
</ul>
</body>
</html>

以下是postForm.html 的代码:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>

<form action="postData.jsp" method="POST">
站点名: <input type="text" name="name">
<br />
网址: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>

</body>
</html>

通过访问 http://localhost:8080/jspDemo/postForm.html 提交表单数据到 postData.jsp 文件,演示 Gif 图如下所示:

传递 Checkbox 数据到JSP程序
复选框 checkbox 可以传递一个甚至多个数据。以下是一个简单的HTML代码,并将代码保存在ckbox.html文件中:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>

<form action="ckboxPost.jsp" method="POST" target="_blank">
<input type="checkbox" name="google" checked="checked" /> Google
<input type="checkbox" name="zc"  /> zc自学教程
<input type="checkbox" name="taobao" checked="checked" />  淘宝
<input type="submit" value="选择网站" />
</form>

</body>
</html>

以下为ckboxPost.jsp文件代码,用于处理复选框数据:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>
<h1>从复选框中读取数据</h1>
<ul>
<li>Google 是否选中:
   <%= request.getParameter("google")%><br>
</li>
<li>zc自学教程是否选中:
   <%= request.getParameter("zc")%><br>
</li>
<li>淘宝是否选中:
   <%= request.getParameter("taobao")%><br>
</li>
</ul>
</body>
</html>

通过访问 http://localhost:8080/jspDemo/ckbox.html 提交表单数据到 ckboxPost.jsp 文件,演示 Gif 图如下所示:

读取所有表单参数
以下我们将使用 HttpServletRequest 的 getParameterNames() 来读取所有表单参数,该方法可以取得所有变量的名称,该方法返回一个枚举。一旦我们有了一个 Enumeration(枚举),我们就可以调用 hasMoreElements() 方法来确定是否还有元素,以及使用nextElement()方法来获得每个参数的名称。getParams.jsp 文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!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>zc 自学教程 (zc.com)</title>
</head>
<body>
<h1>读取所有表单参数</h1>
<table width="100%" border="1" align="center">
<tr bgcolor="#949494">
<th>参数名</th><th>参数值</th>
</tr>
<%
   Enumeration<String> paramNames = request.getParameterNames();

   while(paramNames.hasMoreElements()) {
      String paramName = (String)paramNames.nextElement();
      out.print("<tr><td>" + paramName + "</td>\n");
      String paramValue = request.getParameter(paramName);
      out.println("<td> " + paramValue + "</td></tr>\n");
   }
%>
</table>
</body>
</html>

以下是param.html 文件的内容:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程 (zc.com)</title>
</head>
<body>

<form action="getParams.jsp" method="POST" target="_blank">
<input type="checkbox" name="google" checked="checked" /> Google
<input type="checkbox" name="zc"  /> zc 自学教程
<input type="checkbox" name="taobao" checked="checked" /> 淘宝
<input type="submit" value="选择网站" />
</form>

</body>
</html>

现在我们通过访问 http://localhost:8080/jspDemo/param.html 提交表单数据到 getParams.jsp 文件,演示 Gif 图如下所示:

可以尝试使用以上的JSP代码读取其它对象,如文本框,单选按钮或下拉框等等其他形式的数据。


JSP 过滤器

JSP 和 Servlet 中的过滤器都是 Java 类。过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。可以将一个或多个过滤器附加到一个 Servlet 或一组 Servlet。过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。过滤器是可用于 Servlet 编程的 Java 类,可以实现以下目的:

  • 在客户端的请求访问后端资源之前,拦截这些请求。
  • 在服务器的响应发送回客户端之前,处理这些响应。

根据规范建议的各种类型的过滤器:

  • 身份验证过滤器(Authentication Filters)。
  • 数据压缩过滤器(Data compression Filters)。
  • 加密过滤器(Encryption Filters)。
  • 触发资源访问事件过滤器。
  • 图像转换过滤器(Image Conversion Filters)。
  • 日志记录和审核过滤器(Logging and Auditing Filters)。
  • MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
  • 标记化过滤器(Tokenizing Filters)。
  • XSL/T 过滤器(XSL/T Filters),转换 XML 内容。

过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。当 Web 容器启动 Web 应用程序时,它会为您在部署描述符中声明的每一个过滤器创建一个实例。Filter 的执行顺序与在 web.xml 配置文件中的配置顺序一致,一般把 Filter 配置在所有的 Servlet 之前。

Servlet 过滤器方法
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:

序号方法 & 描述
1public void init(FilterConfig filterConfig)
web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
2public void doFilter (ServletRequest, ServletResponse, FilterChain)
该方法完成实际的过滤操作,当客户端的请求与过滤器设置的 URL 匹配时,Servlet 容器将先调用过滤器的 doFilter 方法。FilterChain 用于访问后续过滤器。
3public void destroy()
Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。

FilterConfig 使用
Filter 的 init 方法中提供了一个 FilterConfig 对象。如 web.xml 文件配置如下,在 init 方法使用 FilterConfig 对象获取参数:

public void  init(FilterConfig filterConfig) throws ServletException {
    // 获取初始化参数
    String site = filterConfig.getInitParameter("Site"); 
    // 输出初始化参数
    System.out.println("网站名称: " + site); 
}

JSP 过滤器实例
以下是 Servlet 过滤器的实例,将输出网站名称和地址。本实例让您对 Servlet 过滤器有基本的了解,您可以使用相同的概念编写更复杂的过滤器应用程序LogFilter.java 文件代码如下:

package z.c.xx;

//导入必需的 java 库
import javax.servlet.*;

//实现 Filter 类
public class LogFilter implements Filter  {
  public void  init(FilterConfig filterConfig) throws ServletException {
      // 获取初始化参数
      String site = filterConfig.getInitParameter("Site"); 

      // 输出初始化参数
      System.out.println("网站名称: " + site); 
  }
  public void  doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
      // 输出站点名称
      System.out.println("站点网址:https://www.zc.com");

      // 把请求传回过滤链
      chain.doFilter(request,response);
  }
  public void destroy( ){
      /* 在 Filter 实例被 Web 容器从服务移除之前调用 */
  }
}

DisplayHeader.java 文件代码如下:

package z.c.xx;

//导入必需的 java 库
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class DisplayHeader
 */
@WebServlet("/DisplayHeader")
public class DisplayHeader extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public DisplayHeader() {
        super();
        // TODO Auto-generated constructor stub
    }

    // 处理 GET 方法请求的方法
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
    {
        // 设置响应内容类型
        response.setContentType("text/html;charset=UTF-8");

        PrintWriter out = response.getWriter();
        String title = "HTTP Header 请求实例 - zc 自学教程实例";
        String docType = "<!DOCTYPE html> \n";
        out.println(docType +"<html>\n" +
            "<head><meta charset=\"utf-8\"><title>" + title + "</title></head>\n"+
            "<body bgcolor=\"#f0f0f0\">\n" +
            "<h1 align=\"center\">" + title + "</h1>\n" +
            "<table width=\"100%\" border=\"1\" align=\"center\">\n" +
            "<tr bgcolor=\"#949494\">\n" +
            "<th>Header 名称</th><th>Header 值</th>\n"+
            "</tr>\n");

        Enumeration<String> headerNames = request.getHeaderNames();

        while(headerNames.hasMoreElements()) {
            String paramName = (String)headerNames.nextElement();
            out.print("<tr><td>" + paramName + "</td>\n");
            String paramValue = request.getHeader(paramName);
            out.println("<td> " + paramValue + "</td></tr>\n");
        }
        out.println("</table>\n</body></html>");
    }

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

动态网站项目中创建一个servlet:右键点击项目名称(jspDemo)或项目中的src,在弹出的菜单中选择 "New -> Other… -> Web -> Servlet",如果找不到Servlet 选项,选择"File-->New->Other-->Web->Servlet":

jspDemo->New->Other
Web->Servlet
Servlet类名称->包名称->Next->Next->finish
Servlet类


ZcEclipseServlet.java如下:

package z.c.xx;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ZcEclipseServlet
 */
@WebServlet("/ZcEclipseServlet")
public class ZcEclipseServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public ZcEclipseServlet() {
        super();
        // TODO Auto-generated constructor stub
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		// 使用 UTF-8 防止中文乱码
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.getWriter().append("Served at: ").append(request.getContextPath());
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping)
定义过滤器,然后映射到一个 URL 或 Servlet,这与定义 Servlet,然后映射到一个 URL 模式方式大致相同。在部署描述符文件 web.xml 中为 filter 标签创建下面的条目:

<?xml version="1.0" encoding="UTF-8"?>  
<web-app version="2.5"   
    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_2_5.xsd">
    
    <servlet>
        <!-- 类名 -->  
        <servlet-name>ZcEclipseServlet</servlet-name>
        <!-- 所在的包 -->  
        <servlet-class>z.c.xx.ZcEclipseServlet</servlet-class>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>ZcEclipseServlet</servlet-name>  
        <!-- 访问的网址 -->  
        <url-pattern>/jspDemo/ZcEclipseServlet</url-pattern>  
    </servlet-mapping>  
    
 	<servlet>
		<!-- 类名 -->
		<servlet-name>DisplayHeader</servlet-name>
		<!-- 所在的包 -->
		<servlet-class>z.c.xx.DisplayHeader</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>DisplayHeader</servlet-name>
		<!-- 访问的网址 -->
		<url-pattern>/jspDemo/DisplayHeader</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>LogFilter</filter-name>
		<filter-class>z.c.xx.LogFilter</filter-class>
		<init-param>
			<param-name>Site</param-name>
			<param-value>zc 自学教程</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>LogFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>   
  
</web-app>

上述过滤器适用于所有的 Servlet,因为我们在配置中指定 /* 。如果您只想在少数的 Servlet 上应用过滤器,您可以指定一个特定的 Servlet 路径。现在试着以常用的方式调用任何 Servlet,您将会看到在 Web 服务器中生成的日志。您也可以使用 Log4J 记录器来把上面的日志记录到一个单独的文件中。接下来我们访问这个实例地址 http://localhost:8080/jspDemo/DisplayHeader, 然后在控制台看下输出内容,eclipse的console不见了:eclipse—>window—>show view—>other—>General—>console—>open,如下Gif演示(高清地址):

使用多个过滤器
Web 应用程序可以根据特定的目的定义若干个不同的过滤器。假设您定义了两个过滤器 AuthenFilter 和 LogFilter。您需要创建一个如下所述的不同的映射,其余的处理与上述所讲解的大致相同:

<filter>
   <filter-name>LogFilter</filter-name>
   <filter-class>z.c.xx.LogFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter>
   <filter-name>AuthenFilter</filter-name>
   <filter-class>z.c.xx.AuthenFilter</filter-class>
   <init-param>
      <param-name>test-param</param-name>
      <param-value>Initialization Paramter</param-value>
   </init-param>
</filter>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器的应用顺序
web.xml 中的 filter-mapping 元素的顺序决定了 Web 容器应用过滤器到 Servlet 的顺序。若要反转过滤器的顺序,您只需要在 web.xml 文件中反转 filter-mapping 元素即可。例如,上面的实例将先应用 LogFilter,然后再应用 AuthenFilter,但是下面的实例将颠倒这个顺序:

<filter-mapping>
   <filter-name>AuthenFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
   <filter-name>LogFilter</filter-name>
   <url-pattern>/*</url-pattern>
</filter-mapping>

web.xml配置各节点说明

  • <filter>指定一个过滤器。
    • <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
    • <filter-class>元素用于指定过滤器的完整的限定类名。
    • <init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。
    • 在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
  • <filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
    • <filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
    • <url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
  • <servlet-name>指定过滤器所拦截的Servlet名称。
  • <dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARDERROR之一,默认REQUEST。用户可以设置多个<dispatcher>子元素用来指定 Filter 对资源的多种调用方式进行拦截。
  • <dispatcher>子元素可以设置的值及其意义
    • REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
    • INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
    • FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
    • ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。

Filter 的基本工作原理

  1.   Filter 程序是一个实现了特殊接口的 Java 类,与 Servlet 类似,也是由 Servlet 容器进行调用和执行的。
  2.  当在 web.xml 注册了一个 Filter 来对某个 Servlet 程序进行拦截处理时,它可以决定是否将请求继续传递给 Servlet 程序,以及对请求和响应消息是否进行修改。
  3.  当 Servlet 容器开始调用某个 Servlet 程序时,如果发现已经注册了一个 Filter 程序来对该 Servlet 进行拦截,那么容器不再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,再由 doFilter 方法决定是否去激活 service 方法。
  4.  但在 Filter.doFilter 方法中不能直接调用 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活目标 Servlet 的 service 方法,FilterChain 对象时通过 Filter.doFilter 方法的参数传递进来的。
  5.  只要在 Filter.doFilter 方法中调用 FilterChain.doFilter 方法的语句前后增加某些程序代码,这样就可以在 Servlet 进行响应前后实现某些特殊功能。
  6.  如果在 Filter.doFilter 方法中没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法不会被执行,这样通过 Filter 就可以阻止某些非法的访问请求。

    更多内容可以参考:Filter、FilterChain、FilterConfig 介绍

Filter 链
1、在一个 Web 应用程序中可以注册多个 Filter 程序,每个 Filter 程序都可以对一个或一组 Servlet 程序进行拦截。如果有多个 Filter 程序都可以对某个 Servlet 程序的访问过程进行拦截,当针对该 Servlet 的访问请求到达时,Web 容器将把这多个 Filter 程序组合成一个 Filter 链(也叫过滤器链)。
2、Filter 链中的各个 Filter 的拦截顺序与它们在 web.xml 文件中的映射顺序一致,上一个 Filter.doFilter 方法中调用 FilterChain.doFilter 方法将激活下一个 Filter的doFilter 方法,最后一个 Filter.doFilter 方法中调用的 FilterChain.doFilter 方法将激活目标 Servlet的service 方法。
3、只要 Filter 链中任意一个 Filter 没有调用 FilterChain.doFilter 方法,则目标 Servlet 的 service 方法都不会被执行。

Filter 接口
一个 Filter 程序就是一个 Java 类,这个类必须实现 Filter 接口。javax.servlet.Filter 接口中定义了三个方法:init、doFilter、destory。

1、init 方法
(1)在 Web 应用程序启动时,Web 服务器(Web 容器)将根据其 web.xml 文件的配置信息来创建每个注册的 Filter 的实例对象,并将其保存在内存中。
(2)Web 容器创建 Filter 的实例对象后,将立即调用该 Filter 对象的 init 方法。init 方法在 Filter 生命周期中仅被执行一次,Web 容器在调用 init 方法时,会传递一个包含 Filter 的配置和运行环境信息的 FilterConfig 对象。public void init(FilterConfig filterConfig) throws ServletException
(3)开发人员可以在 init 方法中完成与构造方法类似的初始化功能,要注意的是:如果初始化代码要使用到 FilterConfig 对象,这些代码只能在 init 方法中编写,而不能在构造方法中编写(尚未调用 init 方法,即并没有创建 FilterConfig 对象,要使用它则必然出错)。

2、doFilter 方法
当一个 Filter 对象能够拦截访问请求时,Servlet 容器将调用 Filter 对象的 doFilter 方法。public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException.ServletException其中,参数 request 和 response 为 Web 容器或 Filter 链中上一个 Filter 传递过来的请求和响应对象;参数 chain 为代表当前 Filter 链的对象。

3、destroy 方法
该方法在 Web 容器卸载 Filter 对象之前被调用,也仅执行一次。可以完成与 init 方法相反的功能,释放被该 Filter 对象打开的资源,例如:关闭数据库连接和 IO 流。

FilterChain 接口
该接口用于定义一个 Filter 链的对象应该对外提供的方法,这个接口只定义了一个 doFilter 方法。public void doFilter(ServletRequest request, ServletResponse response) throws java.io.IOException.ServletExceptionFilterChain 接口的 doFilter 方法用于通知 Web 容器把请求交给 Filter 链中的下一个 Filter 去处理,如果当前调用此方法的 Filter 对象是Filter 链中的最后一个 Filter,那么将把请求交给目标 Servlet 程序去处理。

FilterConfig 接口
1、与普通的 Servlet 程序一样,Filter 程序也很可能需要访问 Servlet 容器。Servlet 规范将代表 ServletContext 对象和 Filter 的配置参数信息都封装到一个称为 FilterConfig 的对象中。
2、FilterConfig 接口则用于定义 FilterConfig 对象应该对外提供的方法,以便在 Filter 程序中可以调用这些方法来获取 ServletContext 对象,以及获取在 web.xml 文件中为 Filter 设置的友好名称和初始化参数。
3、FilterConfig接口定义的各个方法:

  • getFilterName 方法,返回 元素的设置值。
  • getServletContext 方法,返回 FilterConfig 对象中所包装的 ServletContext 对象的引用。
  • getInitParameter 方法,用于返回在 web.xml 文件中为 Filter 所设置的某个名称的初始化的参数值。
  • getInitParameterNames 方法,返回一个 Enumeration 集合对象。

Filter 的注册与映射
1、注册 Filter
一个 <filter> 元素用于注册一个 Filter。其中,<filter-name> 元素是必需的,<filter-class> 元素也是必需的,<init-param> 元素是可选的,可以有多个 < init-param> 元素。

<filter>
    <filter-name>FirstFilter</filter-name>
    <filter-class>z.c.xx.FirstFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF8</param-value>
    </init-param>
</filter>

2、映射 Filter
<filter-mapping> 元素用于设置一个 Filter 所负责拦截的资源。一个 Filter 拦截的资源可以通过两种方式来指定:资源的访问请求路径和 Servlet 名称。

第一种:指定资源的访问路径

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<url-pattern> 元素中的访问路径的设置方式遵循 Servlet 的 URL 映射规范。

  • /*:表示拦截所有的访问请求。
  • /filter/*:表示拦截 filter 目录下的所有访问请求,如:http://localhost:8089/jspDemo/filter/xxxxxx 。
  • /test.html:表示拦截根目录下以 test.html 为资源名的访问请求,访问链接只会是:http://localhost:8080/test.html。

第二种:指定 Servlet 的名称

<filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <servlet-name>default</servlet-name>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

(1)<servlet-name> 元素与 <url-pattern> 元素是二选一的关系,其值是某个 Servlet 在 web.xml 文件中的注册名称。

(2)<dispatcher> 元素的设置值有 4 种:REQUEST、INCLUDE、FORWARD、ERROR,分别对应 Servlet 容器调用资源的 4 种方式:

  • 通过正常的访问请求调用;
  • 通过 RequestDispatcher.include 方法调用;
  • 通过 RequestDispatcher.forward 方法调用;
  • 作为错误响应资源调用。

如果没有设置 <dispatcher> 子元素,则等效于 REQUEST 的情况。也可以设置多个 <dispatcher> 子元素,用于指定 Filter 对资源的多种调用方式都进行拦截。

我们使用过滤器肯定要导入相应的jar包才行,Filter就在servlet-api.jar中,我们将该jar包放到WEB-INF下的lib目录下面,然后加入项目。


FirstFilter.java

package z.c.xx;

import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
 
public class FirstFilter implements Filter {
    private FilterConfig filterConfig = null;
    String paramValue = null;
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        paramValue = filterConfig.getInitParameter("encoding");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("begin headers-------------------");
        Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();
        
        while(headerNames.hasMoreElements()) {
            String headerName = (String)headerNames.nextElement();
            System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
        }
        System.out.println("end headers-------------------");
        
        //在调用目标前写入响应内容
        response.setContentType("text/html; charset=gb2312");
        System.out.println("IP地址为:" + request.getRemoteHost() + "<br>");
        System.out.println("FirstFilter执行前!");
 
        chain.doFilter(request, response);
        
        //在目标返回后写入响应内容
        System.out.println("FirstFilter执行后!");
        System.out.println("<br>名称为encoding的初始化参数的值为:" + paramValue);
        System.out.println("<br>当前Web程序的真实路径为:" + filterConfig.getServletContext().getRealPath("/"));
        
        //System.out.println("<br>修改了test.html文件!");
    }
    
    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <filter>
    <display-name>FirstFilter</display-name>
    <filter-name>FirstFilter</filter-name>
    <filter-class>z.c.xx.FirstFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>

test.html(位于webapp或WebContent路径的filter目录中)

<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
这就是test.html页面的原始内容!
</body>
</html>

访问:http://192.168.230.217:8080/MavenDemo/filter/111111111111

访问:http://192.168.230.217:8080/MavenDemo/filter/test.html

添加listener:省略

z.c.xx ListenerDemo



JSP Cookie 处理

Cookie 是存储在客户机的文本文件,它们保存了大量轨迹信息。在 Servlet 技术基础上,JSP 显然能够提供对 HTTP cookie 的支持。通常有三个步骤来识别回头客:

  • 服务器脚本发送一系列 cookie 至浏览器。比如名字,年龄,ID 号码等等。
  • 浏览器在本地机中存储这些信息,以备不时之需。
  • 当下一次浏览器发送任何请求至服务器时,它会同时将这些 cookie 信息发送给服务器,然后服务器使用这些信息来识别用户或者干些其它事情。

本章节将会传授您如何去设置或重设 cookie 的方法,还有如何访问它们及如何删除它们。JSP Cookie 处理需要对中文进行编码与解码,方法如下:

String   str   =   java.net.URLEncoder.encode("中文", "UTF-8");            //编码
String   str   =   java.net.URLDecoder.decode("编码后的字符串","UTF-8");   // 解码

Cookie 剖析
Cookie 通常在 HTTP 信息头中设置(虽然 JavaScript 能够直接在浏览器中设置 cookie)。在 JSP 中,设置一个 cookie 需要发送如下的信息头给服务器:

HTTP/1.1 200 OK

Date: Tue, 12 Jul 2022 07:56:33 GMT
Server: openresty
Set-Cookie: name=runoops; expires=Wed, 13-Jul-2022 08:59:30 GMT; path=/; domain=runoops.com
Connection: close
Content-Type: text/html

正如您所见,Set-Cookie 信息头包含一个键值对,一个 GMT(格林尼治标准)时间,一个路径,一个域名。键值对会被编码为URL。有效期域是个指令,告诉浏览器在什么时候之后就可以清除这个 cookie。

如果浏览器被配置成可存储 cookie,那么它将会保存这些信息直到过期。如果用户访问的任何页面匹配了 cookie 中的路径和域名,那么浏览器将会重新将这个 cookie 发回给服务器。浏览器端的信息头长得就像下面这样,JSP 脚本通过 request 对象中的 getCookies() 方法来访问这些 cookie,这个方法会返回一个 Cookie 对象的数组:

GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name=xyz

Servlet Cookie 方法
下表列出了 Cookie 对象中常用的方法:

方法描述
publicvoidsetDomain(Stringpattern)设置cookie的域名,比如zc.com
publicStringgetDomain()获取cookie的域名,比如zc.com
publicvoidsetMaxAge(intexpiry)设置cookie有效期,以秒为单位,默认有效期为当前session的存活时间
publicintgetMaxAge()获取cookie有效期,以秒为单位,默认为-1,表明cookie会活到浏览器关闭为止
publicStringgetName()返回cookie的名称,名称创建后将不能被修改
publicvoidsetValue(StringnewValue)设置cookie的值
publicStringgetValue()获取cookie的值
publicvoidsetPath(Stringuri)设置cookie的路径,默认为当前页面目录下的所有URL,还有此目录下的所有子目录
publicStringgetPath()获取cookie的路径
publicvoidsetSecure(booleanflag)指明cookie是否要加密传输
publicvoidsetComment(Stringpurpose)设置注释描述cookie的目的。当浏览器将cookie展现给用户时,注释将会变得非常有用
publicStringgetComment()返回描述cookie目的的注释,若没有则返回null

使用 JSP 设置 cookie
使用 JSP 设置 cookie 包含三个步骤:

(1)创建一个 cookie 对象:调用 cookie 的构造函数,使用一个 cookie 名称和值做参数,它们都是字符串。Cookie cookie = new Cookie("key","value");请务必牢记,名称和值中都不能包含空格或者如下的字符:[ ] ( ) = , " / ? @ : ;
(2)设置有效期:调用 setMaxAge() 函数表明 cookie 在多长时间(以秒为单位)内有效。下面的操作将有效期设为了 24 小时。cookie.setMaxAge(60*60*24);
(3)** 将 cookie 发送至 HTTP 响应头中:**调用 response.addCookie() 函数来向 HTTP 响应头中添加 cookie。response.addCookie(cookie);

cookie.jsp 文件代码如下所示:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*" %>
<%
   // 编码,解决中文乱码   
   String str = URLEncoder.encode(request.getParameter("name"),"utf-8");  
   // 设置 name 和 url cookie 
   Cookie name = new Cookie("name",
           str);
   Cookie url = new Cookie("url",
              request.getParameter("url"));

   // 设置cookie过期时间为24小时。
   name.setMaxAge(60*60*24); 
   url.setMaxAge(60*60*24); 

   // 在响应头部添加cookie
   response.addCookie( name );
   response.addCookie( url );
%>
<html>
<head>
<title>设置 Cookie</title>
</head>
<body>

<h1>设置 Cookie</h1>

<ul>
<li><p><b>网站名:</b>
   <%= request.getParameter("name")%>
</p></li>
<li><p><b>网址:</b>
   <%= request.getParameter("url")%>
</p></li>
</ul>
</body>
</html>

以下是一个简单的 HTML 表单通过 GET 方法将客户端数据提交到 cookie.jsp 文件中,并设置 cookie:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>zc 自学教程</title>
</head>
<body>

<form action="cookie.jsp" method=GET>
站点名: <input type="text" name="name"><br />
网址: <input type="text" name="url" />
<input type="submit" value="提交" />
</form>

</body>
</html>

将以上 HTML 代码保存到 index.html 文件中。将该文件放置于当前 jsp 项目的 webapp目录下(与 cookie.jsp 同一个目录)。通过访问 http://localhost:8080/jspDemo/index.html 提交表单数据到 cookie.jsp 文件,试着输入 "站点名" 和 "网址",然后点击提交按钮,它将会在您的屏幕中显示 "站点名" 和 "网址",并且设置 "站点名" 和 "网址" 的两个 cookie。演示 Gif 图如下所示:

使用 JSP 读取 Cookie
想要读取 cookie,您就需要调用 request.getCookies() 方法来获得一个 javax.servlet.http.Cookie 对象的数组,然后遍历这个数组,使用 getName() 方法和 getValue() 方法来获取每一个 cookie 的名称和值。让我们来读取上个例子中的cookie, 以下为 getCookie.jsp 文件代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>获取 Cookie</title>
</head>
<body>
<%
   Cookie cookie = null;
   Cookie[] cookies = null;
   // 获取 cookies 的数据,是一个数组
   cookies = request.getCookies();
   if( cookies != null ){
      out.println("<h2> 查找 Cookie 名与值</h2>");
      for (int i = 0; i < cookies.length; i++){
         cookie = cookies[i];
        
         out.print("参数名 : " + cookie.getName() +" <br>");
         out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br>");
         out.print("------------------------------------<br>");
      }
  }else{
      out.println("<h2>没有发现 Cookie</h2>");
  }
%>
</body>
</html>

浏览器访问后,输出结果为:

使用 JSP 删除 cookie
删除 cookie 非常简单。如果您想要删除一个 cookie,按照下面给的步骤来做就行了:

  • 获取一个已经存在的 cookie 然后存储在 Cookie 对象中。
  • 将 cookie 的有效期设置为 0。
  • 将这个 cookie 重新添加进响应头中。

下面的程序删除一个名为 "name" 的 cookie,当您第二次运行 delCookie.jsp时,name 将会为 null。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.net.*" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>获取 Cookie</title>
</head>
<body>
<%
   Cookie cookie = null;
   Cookie[] cookies = null;
   // 获取当前域名下的cookies,是一个数组
   cookies = request.getCookies();
   if( cookies != null ){
      out.println("<h2> 查找 Cookie 名与值</h2>");
      for (int i = 0; i < cookies.length; i++){
         cookie = cookies[i];
         if((cookie.getName( )).compareTo("name") == 0 ){  //compareTo() 方法如果指定的数与参数相等返回0,小于返回-1,大于返回1
            cookie.setMaxAge(0);
            response.addCookie(cookie);
            out.print("删除 Cookie: " +  cookie.getName( ) + "<br/>");
         }
         out.print("参数名 : " + cookie.getName() +" <br>");
         out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br>");
         out.print("------------------------------------<br>");
      }
  }else{
      out.println("<h2>没有发现 Cookie</h2>");
  }
%>
</body>
</html>

通过浏览器访问,输出结果为:

再次访问 http://localhost:8080/jspDemo/delCookie.jsp 或者 http://localhost:8080/jspDemo/getCookie.jsp ,可以看到名为 "name" 的 cookie 已经不见了:


JSP Session

HTTP是无状态协议,这意味着每次客户端检索网页时,都要单独打开一个服务器连接,因此服务器不会记录下先前客户端请求的任何信息。有三种方法来维持客户端与服务器的会话:

Cookies
网络服务器可以指定一个唯一的session ID作为cookie来代表每个客户端,用来识别这个客户端接下来的请求。这可能不是一种有效的方式,因为很多时候浏览器并不一定支持cookie,所以我们不建议使用这种方法来维持会话。

隐藏表单域
一个网络服务器可以发送一个隐藏的HTML表单域和一个唯一的session ID,就像下面这样:<input type="hidden" name="sessionid" value="12345">
这个条目意味着,当表单被提交时,指定的名称和值将会自动包含在GET或POST数据中。每当浏览器发送一个请求,session_id的值就可以用来保存不同浏览器的轨迹。这种方式可能是一种有效的方式,但点击<A HREF>标签中的超链接时不会产生表单提交事件,因此隐藏表单域也不支持通用会话跟踪。

重写URL
您可以在每个URL后面添加一些额外的数据来区分会话,服务器能够根据这些数据来关联session标识符。举例来说,https://zc.com/file.html;sessionid=12345 , session标识符为sessionid=12345,服务器可以用这个数据来识别客户端。相比而言,重写URL是更好的方式来,就算浏览器不支持cookies也能工作,但缺点是您必须为每个URL动态指定session ID,就算这是个简单的HTML页面。

session对象
除了以上几种方法外,JSP利用servlet提供的HttpSession接口来识别一个用户,存储这个用户的所有访问信息。默认情况下,JSP允许会话跟踪,一个新的HttpSession对象将会自动地为新的客户端实例化。禁止会话跟踪需要显式地关掉它,通过将page指令中session属性值设为false来实现,就像下面这样:<%@ page session="false" %>JSP引擎将隐含的session对象暴露给开发者。由于提供了session对象,开发者就可以方便地存储或检索数据。

下表列出了session对象的一些重要方法:

方法描述
public Object getAttribute(String name)返回session对象中与指定名称绑定的对象,如果不存在则返回null
public Enumeration getAttributeNames()返回session对象中所有的对象名称
public long getCreationTime()返回session对象被创建的时间, 以毫秒为单位,从1970年1月1号凌晨开始算起
public String getId()返回session对象的ID
public long getLastAccessedTime()返回客户端最后访问的时间,以毫秒为单位,从1970年1月1号凌晨开始算起
public int getMaxInactiveInterval()返回最大时间间隔,以秒为单位,servlet 容器将会在这段时间内保持会话打开
public void invalidate()将session无效化,解绑任何与该session绑定的对象
public boolean isNew()返回是否为一个新的客户端,或者客户端是否拒绝加入session
public void removeAttribute(String name)移除session中指定名称的对象
public void setAttribute(String name, Object value)使用指定的名称和值来产生一个对象并绑定到session中
public void setMaxInactiveInterval(int interval)用来指定时间,以秒为单位,servlet容器将会在这段时间内保持会话有效

JSP Session应用
这个例子描述了如何使用HttpSession对象来获取创建时间和最后一次访问时间。我们将会为request对象关联一个新的session对象,如果这个对象尚未存在的话。session.jsp 实例如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
   // 获取session创建时间
   Date createTime = new Date(session.getCreationTime());
   // 获取最后访问页面的时间
   Date lastAccessTime = new Date(session.getLastAccessedTime());

   String title = "再次访问zc 自学教程实例";
   
   //Integer visitCount = new Integer(0); //jdk1.9废弃此写法,改用下面一行写法
   Integer visitCount = Integer.valueOf(0);
   
   String visitCountKey = new String("visitCount");
   String userIDKey = new String("userID");
   String userID = new String("ABCD");

   // 检测网页是否有新的访问用户
   if (session.isNew()){
      title = "访问zc 自学教程实例";
      session.setAttribute(userIDKey, userID);
      session.setAttribute(visitCountKey,  visitCount);
   } else {
       visitCount = (Integer)session.getAttribute(visitCountKey); 
       visitCount += 1;
       userID = (String)session.getAttribute(userIDKey);
       session.setAttribute(visitCountKey,  visitCount);
   }
%>
<html>
<head>
<title>Session 跟踪</title>
</head>
<body>

<h1>Session 跟踪</h1>

<table border="1" align="center"> 
<tr bgcolor="#949494"><th>Session 信息</th><th>值</th></tr> 
<tr><td>id</td><td><% out.print( session.getId()); %></td></tr> 
<tr><td>创建时间</td><td><% out.print(createTime); %></td></tr> 
<tr><td>最后访问时间</td><td><% out.print(lastAccessTime); %></td></tr> 
<tr><td>用户 ID</td><td><% out.print(userID); %></td></tr> 
<tr><td>访问次数</td><td><% out.print(visitCount); %></td></tr> 
</table> 
</body>
</html>

着访问 http://lcocalhost:8080/MavenDemo/session.jsp ,第一次运行时将会得到如下结果:

再次访问,将会得到如下结果:

删除Session数据
当处理完一个用户的会话数据后,您可以有如下选择:

  • 移除一个特定的属性:调用public void removeAttribute(String name) 方法来移除指定的属性。
  • 删除整个会话:调用public void invalidate() 方法来使整个session无效。
  • 设置会话有效期:调用 public void setMaxInactiveInterval(int interval) 方法来设置session超时。
  • 登出用户:支持servlet2.4版本的服务器,可以调用 logout()方法来登出用户,并且使所有相关的session无效。
配置web.xml文件:如果使用的是Tomcat,超时以分钟为单位,Tomcat中的默认的超时时间是30分钟。可以向下面这样配置web.xml文件:
<session-config>
    <session-timeout>15</session-timeout>
  </session-config>

Servlet中的getMaxInactiveInterval( ) 方法以秒为单位返回超时时间。如果在web.xml中配置的是15分钟,则getMaxInactiveInterval( ) 方法将会返回900。


JSP 文件上传

JSP 可以与 HTML form 标签一起使用,来允许用户上传文件到服务器。上传的文件可以是文本文件或图像文件或任何文档。本章节我们使用 Servlet 来处理文件上传,使用到的文件有:

  • upload.jsp : 文件上传表单。
  • message.jsp : 上传成功后跳转页面。
  • UploadServlet.java : 上传处理 Servlet。
  • 需要引入的 jar 文件:commons-fileupload-1.5、commons-io-2.13.0.jar

注意: Servlet 3 已经内置了文件上传这一特性,开发者不再需要将 Commons FileUpload 组件导入到工程中去,见 Servlet 文件上传

注意:添加上面两个jar的时候,注意添加到 WEB-INF 下 lib 目录里面,并添加到 build path, 以及“C:\Program Files\Java\jdk-1.8\jre\lib\ext” 目录,否则会报错:java.lang.ClassNotFoundException: org.apache.commons.fileupload.servlet.ServletFileUpload...

eclipse run的时候,tomcat启动失败:

java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException:Caused by: org.apache.catalina.LifecycleException: 无法启动组件
..

创建一个文件上传表单
下面的 HTML 代码创建了一个文件上传表单。以下几点需要注意:

  • 表单 method 属性应该设置为 POST 方法,不能使用 GET 方法。
  • 表单 enctype 属性应该设置为 multipart/form-data.
  • 表单 action 属性应该设置为在后端服务器上处理文件上传的 Servlet 文件。下面的实例使用了 UploadServlet Servlet 来上传文件。
  • 上传单个文件,您应该使用单个带有属性 type="file" 的 <input ... /> 标签。为了允许多个文件上传,请包含多个 name 属性值不同的 input 标签。输入标签具有不同的名称属性的值。浏览器会为每个 input 标签关联一个浏览按钮。

upload.jsp 文件代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>文件上传实例 - zc 自学教程 </title>
</head>
<body>
<h1>文件上传实例 -  zc 自学教程</h1>
<form method="post" action="UploadServlet" enctype="multipart/form-data">
    选择一个文件:
    <input type="file" name="uploadFile" /><br/><br/>
    <input type="submit" value="上传" />
</form>
</body>
</html>

编写后台 Servlet
以下是 Servlet UploadServlet,会接受上传的文件,并把它储存在目录 /webapps/data 中。这个目录名也可以使用外部配置来添加,比如 web.xml 中的 context-param 元素,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

	<context-param> 
		<description>Location to store uploaded file</description> 
		<param-name>file-upload</param-name> 
		<param-value>
			D:\WWW\data\
		</param-value> 
	</context-param>
	
	<servlet>
		<display-name>UploadServlet</display-name>
		<servlet-name>UploadServlet</servlet-name>
		<servlet-class>z.c.xx.UploadServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>UploadServlet</servlet-name>
		<url-pattern>/UploadServlet</url-pattern>
	</servlet-mapping>

</web-app>

pom.xml代码如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>z.c.xx</groupId>
  <artifactId>MavenDemo</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>MavenDemo Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
	</dependency>  
	  
	<dependency>
		<groupId>javax.servlet.jsp</groupId>
		<artifactId>jsp-api</artifactId>
		<version>2.2</version>
		<scope>provided</scope>
	</dependency>   
	
	<dependency>
		<groupId>com.liferay</groupId>
		<artifactId>org.apache.commons.fileupload</artifactId>
		<version>1.2.2.LIFERAY-PATCHED-1</version>
	</dependency> 
    
  </dependencies>
  <build>
    <finalName>MavenDemo</finalName>
    <sourceDirectory>src/main/java</sourceDirectory>
    <resources>
		<resource>
			<directory>src/main/resources</directory>
		</resource>
	</resources>
  </build>
</project>

UploadServlet 的源代码 如下所示:

package z.c.xx;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
 

/**
 * Servlet implementation class UploadServlet
 */

// 如果不配置 web.xml ,可以使用下面的代码
// @WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
     
    // 上传文件存储目录
    private static final String UPLOAD_DIRECTORY = "upload";
 
    // 上传配置
    private static final int MEMORY_THRESHOLD   = 1024 * 1024 * 3;  // 缓冲区大小3MB
    private static final int MAX_FILE_SIZE      = 1024 * 1024 * 40; // 上传单个文件的最大值40MB
    private static final int MAX_REQUEST_SIZE   = 1024 * 1024 * 50; // 50MB
 
    /**
     * 上传数据及保存文件
     */
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
        // 检测是否为多媒体上传
        if (!ServletFileUpload.isMultipartContent(request)) {
            // 如果不是则停止
            PrintWriter writer = response.getWriter();
            writer.println("Error: 表单必须包含 enctype=multipart/form-data");
            writer.flush();
            return;
        }
 
        // 配置上传参数,配置fileItem工厂
        DiskFileItemFactory factory = new DiskFileItemFactory();
        // 设置内存临界值 - 设置缓冲区大小,超过后将产生临时文件并存储于临时目录中
        factory.setSizeThreshold(MEMORY_THRESHOLD);
        // 设置临时存储目录
        factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
        // 创建文件上传处理器
        ServletFileUpload upload = new ServletFileUpload(factory);         
        // 设置上传文件大小的最大值
        upload.setFileSizeMax(MAX_FILE_SIZE);        
        // 设置最大请求值 (包含文件和表单数据)
        upload.setSizeMax(MAX_REQUEST_SIZE);        
        // 中文处理
        upload.setHeaderEncoding("UTF-8");         
       // 获取文件将被存储的位置
        String uploadPath = getServletContext().getInitParameter("file-upload") + UPLOAD_DIRECTORY;               
        // 如果目录不存在则创建
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }
 
        try {
            // 解析请求的内容提取文件数据
            @SuppressWarnings("unchecked")
            List<FileItem> formItems = upload.parseRequest(request);
 
            if (formItems != null && formItems.size() > 0) {
                // 迭代表单数据
                for (FileItem item : formItems) {
                    // 处理不在表单中的字段
                    if (!item.isFormField()) {
                        String fileName = new File(item.getName()).getName();
                        String filePath = uploadPath + File.separator + fileName;
                        File storeFile = new File(filePath);
                        // 在控制台输出文件的上传路径
                        System.out.println(filePath);
                        // 保存文件到硬盘
                        item.write(storeFile);
                        request.setAttribute("message",
                            "文件上传成功!");
                    }
                }
            }
        } catch (Exception ex) {
            request.setAttribute("message",
                    "错误信息: " + ex.getMessage());
        }
        // 跳转到 message.jsp
        getServletContext().getRequestDispatcher("/message.jsp").forward(
                request, response);
    }
}

message.jsp 文件代码如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<!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>文件上传结果 - zc 自学教程 </title>
</head>
<body>
    <center>${message}</center>
</body>
</html>

web.xml配置
在 web.xml 文件中创建所需的条目,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 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_2_5.xsd">

	<context-param>
		<description>Location to store uploaded file</description> 
		<param-name>file-upload</param-name>
		<param-value>
			D:\WWW\data\
		</param-value>
	</context-param>

	<servlet>
		<display-name>UploadServlet</display-name>
		<servlet-name>UploadServlet</servlet-name>
		<servlet-class>com.runoops.test.UploadServlet</servlet-class>
	</servlet>

	<servlet-mapping>
		<servlet-name>UploadServlet</servlet-name>
		<url-pattern>/UploadServlet</url-pattern>
	</servlet-mapping>

</web-app>

现在尝试使用您在上面创建的 HTML 表单来上传文件。当您在浏览器中访问: http://localhost:8080/MavenDemo/upload.jsp ,演示如下所示:


JSP 日期处理

使用JSP最重要的优势之一,就是可以使用所有Java API。本章将会详细地讲述Java中的Date类,它在java.util包下,封装了当前日期和时间。

Date类有两个构造函数。第一个构造函数使用当前日期和时间来初始化对象。Date( )
第二个构造函数接受一个参数,这个参数表示从1970年1月1日凌晨至所要表示时间的毫秒数。Date(long millisec)
获取Date对象后,您就能够使用下表列出的所有方法:

方法描述
boolean after(Date date) 如果比给定的日期晚,则返回true,否则返回false
boolean before(Date date) 如果比给定的日期早,则返回true,否则返回false
Object clone( ) 获取当前对象的一个副本
int compareTo(Date date) 如果与给定日期相等,则返回0,如果比给定日期早,则返回一个负数,如果比给定日期晚,则返回一个正数
int compareTo(Object obj) 与 compareTo(Date) 方法相同,如果 obj 不是Date类或其子类的对象,抛出ClassCastException异常
boolean equals(Object date) 如果与给定日期相同,则返回true,否则返回false
long getTime( ) 返回从1970年1月1日凌晨至此对象所表示时间的毫秒数
int hashCode( ) 返回此对象的哈希码
void setTime(long time) 使用给定参数设置时间和日期,参数time表示从1970年1月1日凌晨至time所经过的毫秒数
String toString( ) 将此对象转换为字符串并返回这个字符串

获取当前日期和时间
使用JSP编程可以很容易的获取当前日期和时间,只要使用Date对象的toString()方法就行了,就像下面这样:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*, javax.servlet.*" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>显示当前时间与日期 - zc 自学教程</title>

</head>
<body>
<h1>显示当前时间与日期</h1>

<%
   Date date = new Date();
   out.print( "<h2 align=\"center\">" +date.toString()+"</h2>");
%>
</body>
</html>

将上面的代码保存在date.jsp 文件中,然后访问 http://localhost:8080/MavenDemo/date.jsp ,运行结果如下:

刷新,就可以发现每次刷新所得到的秒数都不相同。

日期比较
就像我在开头所提到的,您可以在JSP脚本中使用任何Java方法。如果您想要比较两个日期,可以参照下面的方法来做:

  • 使用getTime()方法得到毫秒数,然后比较毫秒数就行了。
  • 使用before(),after(),equals()方法。比如,new Date(99,2,12).before(new Date(99,2,18))返回true。
  • 使用compareTo()方法,这个方法在Comparable接口中定义,在Date中实现。

使用SimpleDateFormat格式化日期
SimpleDateFormat使用一种地区敏感的方式来格式化和解析日期,它允许您使用自定义的模式来格式化日期和时间。对CurrentDate.jsp稍作修改,得到如下修改后的代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<%@ page import="javax.servlet.*,java.text.*" %>
<html>
<head>
<meta charset="UTF-8">
<title>显示当前时间与日期</title>
</head>
<body>

<h1>显示当前时间与日期</h1>

<%
   Date dNow = new Date( );
   SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss");
   out.print( "<h2 align=\"center\">" + ft.format(dNow) + "</h2>");
%>
</body>
</html>

然后访问 http://localhost:8080/MavenDemo/date.jsp ,就可以得到如下结果:

刷新

SimpleDateFormat格式码
要指定模式字符串,需要使用下表列出的格式码:

字符描述示例
G时代标识符AD
y4位数年份2001
MJuly or 07
d10
h12小时制, A.M./P.M. (1~12)12
H24小时制22
m分钟30
s55
S毫秒234
E星期Tuesday
D一年中的某天360
F一个月中某星期的某天2 (second Wed. in July)
w一年中的某星期40
W一个月中的某星期1
aA.M./P.M. 标记PM
k一天中的某个小时 (1~24)24
K一天中的某个小时,A.M./P.M. (0~11)10
z时区Eastern Standard Time
'文本分隔Delimiter
"单引号`

JSP 页面重定向

当需要将文档移动到一个新的位置时,就需要使用JSP重定向了。最简单的重定向方式就是使用response对象的sendRedirect()方法。这个方法的签名:public void response.sendRedirect(String location)throws IOException

这个方法将状态码和新的页面位置作为响应发回给浏览器。您也可以使用setStatus()和setHeader()方法来得到同样的效果:

....
String site = "https://www.runoops.com" ;
response.setStatus(response.SC_MOVED_TEMPORARILY);
response.setHeader("Location", site); 
....

这个例子表明了JSP如何进行页面重定向,将代码保存在redirecting.jsp文件中,然后访问 http://localhost:8080/MavenDemo/redirecting.jsp ,它将会把您带至 https://www.baidu.com/

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<html>
<html>
<head>
<meta charset="UTF-8">
<title>页面重定向 - zc 自学教程</title>
</head>
<body>
<h1>页面重定向</h1>
<%
   // 重定向到新地址
   String site = new String("https://www.baidu.com");
   response.setStatus(response.SC_MOVED_TEMPORARILY);
   response.setHeader("Location", site); 
%>
</body>
</html>


JSP 自动刷新

想象一下,如果要直播比赛的比分,或股票市场的实时状态,或当前的外汇配给,该怎么实现呢?显然,要实现这种实时功能,您就不得不规律性地刷新页面。JSP提供了一种机制来使这种工作变得简单,它能够定时地自动刷新页面。刷新一个页面最简单的方式就是使用response对象的setIntHeader()方法。这个方法的签名如下:public void setIntHeader(String header, int headerValue)这个方法通知浏览器在给定的时间后刷新,时间以秒为单位。

页面自动刷新程序示例
这个例子使用了setIntHeader()方法来设置刷新头,模拟一个数字时钟:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<html>
<head>
<meta charset="UTF-8">
<title>自动刷新实例 - zc 自学教程</title>
</head>
<body>

<h2>自动刷新实例</h2>
<%
   // 设置每隔5秒刷新一次
   response.setIntHeader("Refresh", 5);
   // 获取当前时间
   Calendar calendar = new GregorianCalendar();
   String am_pm;
   int hour = calendar.get(Calendar.HOUR);
   int minute = calendar.get(Calendar.MINUTE);
   int second = calendar.get(Calendar.SECOND);
   if(calendar.get(Calendar.AM_PM) == 0)
      am_pm = "AM";
   else
      am_pm = "PM";
   String CT = hour+":"+ minute +":"+ second +" "+ am_pm;
   out.println("当前时间为: " + CT + "\n");
%>

</body>
</html>

把以上代码保存在refresh.jsp 文件中,访问http://localhost:8080/MavenDemo/refresh.jsp。它会每隔5秒钟刷新一次页面并获取系统当前时间。运行结果如下:

您也可以自己动手写个更复杂点的程序。

注册跳转的例子
实现注册后,三秒跳转到登录页,有倒数3,2,1效果:

<script>
onload=function(){
    setInterval(go, 1000);
};
var x=3; //利用了全局变量来执行
function go(){
    x--;
    if(x>0){
        document.getElementById("time").innerHTML=x;  //每次设置的x的值都不一样了。
    }else{
        location.href='https://www.baidu.com';  // 设置你的注册页面
    } 
}  
</script>
<span id="time">3</span>

具体实现refresh.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*" %>
<html>
<head>
<meta charset="UTF-8">
<title>自动刷新实例 - zc 自学教程</title>
</head>
<body>

<h2>倒数3,2,1跳转到登录页实例</h2>
<script>
onload=function(){
    setInterval(go, 1000);
};
var x=3; //利用了全局变量来执行
function go(){
    x--;
    if(x>0){
        document.getElementById("time").innerHTML=x;  //每次设置的x的值都不一样了。
    }else{
        location.href='login.html';  // 设置你的注册页面
    } 
}  
</script>
<span id="time">3</span>

</body>
</html>

login.html,其中头像图片背景图片

<!DOCTYPE<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            background: url('./upload/背景.jpg') center center fixed;
            background-size: cover;
            padding: 20px;
        }
        form{
            width: 343px;
            height: 500px;
            margin: 0 auto;
            padding: 30px;
            border:1px solid rgba(0, 0, 0, 0.2);
            border-radius: 5px;
            background-color: rgba(0,0,0,0.5);
            box-shadow: 0 0 13px 3px rgba(0,0,0,0.5);
            overflow: hidden;

        }
        .border-radius{
            width: 200px;
            height: 200px;
            border-radius: 200px;
            background: url('./upload/头像.png') no-repeat center center;
            margin: 40px auto;
            border:5px solid rgba(255,255,255,0.4)
        }
        input{
            width:276px ;
            height: 48px;
            border:1px solid rgba(68, 67, 67, 0.4);
            border-radius:4px;
            display: block;
            font-size: 18px;
            color:rgb(59, 59, 59);
            padding-left: 45px;
            padding-right: 20px;
            margin-bottom: 20px;
            margin-left: 20px;
            background: rgba(255,255,255,0.4) no-repeat 16px 16px;
        }
        input[type=submit]{
            cursor: pointer;
        }
        ::-webkit-input-placeholder{
            color: white;
        }
        .btn{
            width: 138px;
            height: 44px;
            border-radius: 4px;
            padding:15px 20px;
            border-radius: 6px;
            color:whitesmoke;
            margin-left: 60px;
        }
        .btn:hover{
            border:1px solid black;
            box-shadow: 0px 1px 0 black;
        }
        input:focus{
            background-color: rgba(0, 0, 0, 0.2);
            box-shadow: 0 0 5px 1px rgba(255,255,255,.0.5);
            overflow: hidden;
        }
        .btn:active{
            margin-top: 1px;
            text-shadow: 0 -1px #333333;
            background: #00B0DC;
            color:#fff
        }
    </style>
</head>
<body>
<form action="">
    <div class="border-radius"></div>
    <input type="text" name="name" placeholder="用户名">
    <input type="password" name="password" placeholder="登录密码">
    <input type="submit" name="submit" value="登录" class="btn">
</form>
</body>
</html>

访问http://192.168.230.199:8080/MavenDemo/refresh.jsp


JSP 发送邮件

虽然使用JSP实现邮件发送功能很简单,但是需要有JavaMail API,并且需要安装JavaBean Activation Framework。

您可以从 Java 网站下载最新版本的 JavaMail,打开网页右侧有个 Downloads 链接,点击它下载。
您可以从 Java 网站下载最新版本的 JAF(版本 1.1.1)
你也可以使用本站提供的下载链接:javax.mail-1.6.2.jar、activation-1.1.1.jar

下载并解压这些文件,在根目录下,您将会看到一系列jar包。将javax.mail-1.6.2.jar包和activation-1.1.1.jar 包加入CLASSPATH变量中:

  • 粘贴javax.mail-1.6.2.jar、activation-1.1.1.jar 到 D:\eclipsecode\MavenDemo\src\main\webapp\WEB-INF\lib

  • 粘贴javax.mail-1.6.2.jar、activation-1.1.1.jar 到 C:\Program Files\Java\jdk-1.8\jre\lib\ext

  • 项目名->Build path->config build path->Java Build Path->Libraries->Add External JARS->javax.mail-1.6.2.jar、activation-1.1.1.jar

用户认证部分
如果邮件服务器需要用户名和密码来进行用户认证的话,可以像下面这样来设置:

 properties.setProperty("mail.user", "myuser");
 properties.setProperty("mail.password", "mypwd");

使用表单发送邮件
使用HTML表单接收一封邮件,并通过request对象获取所有邮件信息:

String to = request.getParameter("to");
String from = request.getParameter("from");
String subject = request.getParameter("subject");
String messageText = request.getParameter("body");

获取以上信息后,您就可以使用前面提到的例子来发送邮件了。

163邮箱发送附件到qq邮箱
先登录Foxmail终端软件:

SendEmail.jsp如下:

<%@ page import="java.util.*" %>
<%@ page import="javax.mail.*" %>
<%@ page import="javax.mail.internet.*" %>
<%@ page import="javax.activation.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
        String result;
        // 收件人的电子邮件
        String to = "644249552@qq.com";
        // 发件人的电子邮件名和密码
        String from = "m15510960941@163.com";
        String psd = "这里写授权码";
        //设置用户名
        String user = "m15510960941";

        Properties properties = new Properties();

        try {
            //设置用户的认证方式
            properties.setProperty( "mail.smtp.auth", "true" );
            //设置传输协议
            properties.setProperty( "mail.transport.protocol", "smtp" );
            //SMTP邮件服务器
            properties.setProperty( "mail.smtp.host", "smtp.163.com" );
            //SMTP邮件服务器默认端口
            properties.setProperty( "mail.smtp.port", "25" );
            // 获取默认的Session对象。
            Session mailSession = Session.getDefaultInstance( properties );
            // 创建一个默认的MimeMessage对象。
            Message message = new MimeMessage( mailSession );
            // 根据session对象获取邮件传输对象Transport
            Transport transport = mailSession.getTransport();
            // 设置 From: 头部的header字段
            message.setFrom( new InternetAddress( from ) );
            // 设置 To: 头部的header字段
            message.addRecipient( Message.RecipientType.TO, new InternetAddress( to ) );
            // 设置 Subject: header字段
            message.setSubject( "This is the Subject Line!" );
            // 创建消息部分
            BodyPart messageBodyPart = new MimeBodyPart();
            // 填充消息
            messageBodyPart.setText("This is message body");
            // 创建多媒体消息
            Multipart multipart = new MimeMultipart();
            // 设置文本消息部分
            multipart.addBodyPart(messageBodyPart);
            // 附件部分
            messageBodyPart = new MimeBodyPart();
            //使用绝对路径
            String filename = "D:\\www\\data\\girl.png";
            //添加文件到message
            DataSource source = new FileDataSource(filename);
            messageBodyPart.setDataHandler(new DataHandler(source));
            messageBodyPart.setFileName(filename);
            multipart.addBodyPart(messageBodyPart);
            // 发送完整消息
            message.setContent(multipart);
            message.setSentDate(new Date());
            // 设置发件人的账户名和密码
            transport.connect(user,psd);
            // 发送邮件,并发送到所有收件人地址,message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
            transport.sendMessage( message, message.getAllRecipients() );

            result = "Sent message successfully....";
        } catch (Exception e) {
            e.printStackTrace();
            result = "Error: unable to send message....";
        }

%>
<html>
<head>
<title>Send Email using JSP</title>
</head>
<body>
<center><h1>Send Email using JSP</h1></center>
<p align="center">
<% out.println("Result: " + result + "\n"); %>
</p>
</body>
</html>

获取授权码:

访问 http://192.168.230.199:8080/MavenDemo/SendEmail.jsp ,结果如下:

qq邮箱发送附件到qq邮箱

<%@ page import="java.util.*" %>
<%@ page import="javax.mail.*" %>
<%@ page import="javax.mail.internet.*" %>
<%@ page import="javax.activation.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
        String result;
        // 收件人的电子邮件
        String to = "352614391@qq.com";
        // 发件人的电子邮件名和密碼
        String from = "644249552@qq.com";
        String psd = "此处填写具体授权码xxx";
        //设置用户名
        String user = "644249552";

        Properties properties = new Properties();

        try {
            //设置用户的认证方式
            properties.setProperty( "mail.smtp.auth", "true" );
            //设置传输协议
            properties.setProperty( "mail.transport.protocol", "smtp" );
            //SMTP邮件服务器
            properties.setProperty( "mail.smtp.host", "smtp.qq.com" );
            //SMTP邮件服务器默认端口
            properties.setProperty( "mail.smtp.port", "25" );
            // 获取默认的Session对象。
            Session mailSession = Session.getDefaultInstance( properties );
            // 创建一个默认的MimeMessage对象。
            Message message = new MimeMessage( mailSession );
            // 根据session对象获取邮件传输对象Transport
            Transport transport = mailSession.getTransport();
            // 设置 From: 头部的header字段
            message.setFrom( new InternetAddress( from ) );
            // 设置 To: 头部的header字段
            message.addRecipient( Message.RecipientType.TO, new InternetAddress( to ) );
            // 设置 Subject: header字段
            message.setSubject( "This is the Subject Line!" );
            // 创建消息部分
            BodyPart messageBodyPart = new MimeBodyPart();
            // 填充消息
            messageBodyPart.setText("This is message body");
            // 创建多媒体消息
            Multipart multipart = new MimeMultipart();
            // 设置文本消息部分
            multipart.addBodyPart(messageBodyPart);
            // 附件部分
            messageBodyPart = new MimeBodyPart();
            //使用绝对路径
            String filename = "D:\\www\\data\\hahaha.txt";
            //添加文件到message
            DataSource source = new FileDataSource(filename);
            messageBodyPart.setDataHandler(new DataHandler(source));
            messageBodyPart.setFileName(filename);
            multipart.addBodyPart(messageBodyPart);
            // 发送完整消息
            message.setContent(multipart);
            message.setSentDate(new Date());
            // 设置发件人的账户名和密码
            transport.connect(user,psd);
            // 发送邮件,并发送到所有收件人地址,message.getAllRecipients() 获取到的是在创建邮件对象时添加的所有收件人, 抄送人, 密送人
            transport.sendMessage( message, message.getAllRecipients() );

            result = "Sent message successfully....";
        } catch (Exception e) {
            e.printStackTrace();
            result = "Error: unable to send message....";
        }

%>
<html>
<head>
<title>Send Email using JSP</title>
</head>
<body>
<center><h1>Send Email using JSP</h1></center>
<p align="center">
<% out.println("Result: " + result + "\n"); %>
</p>
</body>
</html>

java发邮件无法连接。异常信息如下:

com.sun.mail.util.MailConnectException: Couldn't connect to host, port: localhost, 25; timeout -1;
  nested exception is:
	java.net.ConnectException: Connection refused: connect
	at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2209)
	at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:740)
	at javax.mail.Service.connect(Service.java:366)
	at javax.mail.Service.connect(Service.java:246)
	at javax.mail.Service.connect(Service.java:267)
	at org.apache.jsp.SendEmail_jsp._jspService(SendEmail_jsp.java:184)
	at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
	at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:466)
	at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:379)
	at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:327)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:623)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:673)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1790)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:750)
Caused by: java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method)
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:75)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:476)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:218)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:200)
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:162)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:394)
	at java.net.Socket.connect(Socket.java:606)
	at java.net.Socket.connect(Socket.java:555)
	at com.sun.mail.util.SocketFetcher.createSocket(SocketFetcher.java:359)
	at com.sun.mail.util.SocketFetcher.getSocket(SocketFetcher.java:238)
	at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2175)
	... 33 more


SMTP服务器25端口用不了。
解决方案:下载SMTP服务(用Winmail Mail Server替换易邮服务器)

第一步,下载&安装Winmail Mail Server
最新版本7.1网址,可试用90天。安装步骤一直next即可,两个都选择。

最终安装后得到:

第二步,启动Winmail Mail Server,并使用Mail管理工具
选择上面,开启Mail服务器。应该会弹窗

可以不用管,这个界面是用email地址和密码生成email的第一种方法。可以用方法二,在登录管理工具之后,添加用户生成email。点击下面一个管理工具,启动界面

  • 本地主机的意思是,将mail服务器运行在自己的电脑上(一般运行在服务器上)。【1】
  • 远程主机,可以通过第一种添加用户生成的ip地址登录,端口默认即可。
  • 登录用户中的用户名即管理员,不用改。密码是安装时设置的那个。

登录进去第一件事,设置域名管理

进去第二步,添加用户组&用户。

第三步,使用Foxmail终端软件操作Winmail服务器上mail内容

  1. 官网下载
  2. 登录mail用户
  • 选择其他登录方式
  • 用邮箱名和创建时设置的密码登录即可
  • 设置POP服务器和SMTP服务器。即mail服务器运行的机器ip地址。


发送一封简单的邮件
这个例子展示了如何从您的机器发送一封简单的邮件。它假定localhost已经连接至网络并且有能力发送一封邮件。与此同时,请再一次确认mail.jar包和activation.jar包已经添加进CLASSPATH变量中。

<%@ page import="java.io.*,java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*,javax.activation.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
   String result;
   // 收件人的电子邮件
   String to = "lisi@zc.com";
   // 发件人的电子邮件
   String from = "zhangsan@zc.com";
   String psd = "aaaAAA111";
   String user = "zhangsan";   
   // 假设你是从本地主机发送电子邮件
   String host = "127.0.0.1";
   // 获取系统属性对象
   Properties properties = System.getProperties();
   // 设置邮件服务器
   properties.setProperty("mail.smtp.host", host);
   // 获取默认的Session对象。
   Session mailSession = Session.getDefaultInstance(properties);

   try{
      // 创建一个默认的MimeMessage对象。
      MimeMessage message = new MimeMessage(mailSession);
      // 设置 From: 头部的header字段
      message.setFrom(new InternetAddress(from));
      // 设置 To: 头部的header字段
      message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
      // 设置 Subject: header字段
      message.setSubject("This is the Subject Line!");
      // 现在设置的实际消息
      message.setText("This is actual message");
      // 设置发件人的账户名和密码
      Transport transport = mailSession.getTransport();
      transport.connect(user,psd);
      // 发送消息
      Transport.send(message);
      result = "Sent message successfully....";
   }catch (MessagingException mex) {
      mex.printStackTrace();
      result = "Error: unable to send message....";
   }
%>
<html>
<head>
<title>Send Email using JSP</title>
</head>
<body>
<center><h1>Send Email using JSP</h1></center>
<p align="center">
<% out.println("Result: " + result + "\n"); %>
</p>
</body>
</html>

现在访问 http://192.168.230.199:8080/MavenDemo/SendEmail.jsp ,它将会发送一封邮件给 lisi@zc.com 并显示如下结果:

如果想要把邮件发送给多人,下面列出的方法可以用来指明多个邮箱地址:void addRecipients(Message.RecipientType type, Address[] addresses)throws MessagingException

参数的描述如下:

  • type:这个值将会被设置成 TO(收件人)、CC 或 BCC。CC 表示 Carbon Copy(抄送),BCC 表示 Blind Carbon Copy(密件抄送)。例子程序中使用的是 TO。
  • addresses:这是一个邮箱地址的数组,当指定邮箱地址时需要使用InternetAddress()方法。

发送一封HTML邮件
这个例子发送一封简单的HTML邮件。它假定您的localhost已经连接至网络并且有能力发送邮件。与此同时,请再一次确认mail.jar包和activation.jar包已经添加进CLASSPATH变量中。这个例子和前一个例子非常相似,不过在这个例子中我们使用了setContent()方法,将"text/html"做为第二个参数传给它,用来表明消息中包含了HTML内容。

<%@ page import="java.io.*,java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*,javax.activation.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
   String result;
   // 收件人的电子邮件
   String to = "lisi@zc.com";
   // 发件人的电子邮件
   String from = "zhangsan@zc.com";
   String psd = "aaaAAA111";
   String user = "zhangsan";   
   // 假设你是从本地主机发送电子邮件
   String host = "127.0.0.1";
   // 获取系统属性对象
   Properties properties = System.getProperties();
   // 设置邮件服务器
   properties.setProperty("mail.smtp.host", host);
   // 获取默认的Session对象。
   Session mailSession = Session.getDefaultInstance(properties);

   try{
      // 创建一个默认的MimeMessage对象。
      MimeMessage message = new MimeMessage(mailSession);
      // 设置 From: 头部的header字段
      message.setFrom(new InternetAddress(from));
      // 设置 To: 头部的header字段
      message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
      // 设置 Subject: header字段
      message.setSubject("This is the Subject Line!");
      // 设置 HTML消息
      message.setContent("<h1>This is actual message,别回复了!</h1>","text/html" );
      // 设置发件人的账户名和密码
      Transport transport = mailSession.getTransport();
      transport.connect(user,psd);
      // 发送消息
      Transport.send(message);
      result = "Sent message successfully....";
   }catch (MessagingException mex) {
      mex.printStackTrace();
      result = "Error: unable to send message....";
   }
%>
<html>
<head>
<title>Send Email using JSP</title>
</head>
<body>
<center><h1>Send Email using JSP</h1></center>
<p align="center">
<% out.println("Result: " + result + "\n"); %>
</p>
</body>
</html>

现在你可以尝试使用以上JSP文件来发送HTML消息的电子邮件。

在邮件中包含附件
这个例子告诉我们如何发送一封包含附件的邮件。

<%@ page import="java.io.*,java.util.*,javax.mail.*"%>
<%@ page import="javax.mail.internet.*,javax.activation.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
   String result;
   // 收件人的电子邮件
   String to = "lisi@zc.com";
   // 发件人的电子邮件
   String from = "zhangsan@zc.com";
   String psd = "aaaAAA111";
   String user = "zhangsan";   
   // 假设你是从本地主机发送电子邮件
   String host = "127.0.0.1";
   // 获取系统属性对象
   Properties properties = System.getProperties();
   // 设置邮件服务器
   properties.setProperty("mail.smtp.host", host);
   // 获取默认的Session对象。
   Session mailSession = Session.getDefaultInstance(properties);

   try{
      // 创建一个默认的MimeMessage对象。
      MimeMessage message = new MimeMessage(mailSession);
      // 设置 From: 头部的header字段
      message.setFrom(new InternetAddress(from));
      // 设置 To: 头部的header字段
      message.addRecipient(Message.RecipientType.TO,new InternetAddress(to));
      // 设置 Subject: header字段
      message.setSubject("This is the Subject Line!");
      // 创建消息部分
      BodyPart messageBodyPart = new MimeBodyPart();
      // 填充消息
      messageBodyPart.setText("This is message body");     
      // 创建多媒体消息
      Multipart multipart = new MimeMultipart();
      // 设置文本消息部分
      multipart.addBodyPart(messageBodyPart);
      // 附件部分
      messageBodyPart = new MimeBodyPart();
      String filename = "D:\\www\\data\\11.txt";
      DataSource source = new FileDataSource(filename);
      messageBodyPart.setDataHandler(new DataHandler(source));
      messageBodyPart.setFileName(filename);
      multipart.addBodyPart(messageBodyPart);
      // 发送完整消息
      message.setContent(multipart );
      // 发送消息
      Transport.send(message);
      String title = "Send Email";
      result = "Sent message successfully....";
   }catch (MessagingException mex) {
      mex.printStackTrace();
      result = "Error: unable to send message....";
   }
%>
<html>
<head>
<title>Send Email using JSP</title>
</head>
<body>
<center><h1>Send Email using JSP</h1></center>
<p align="center">
<% out.println("Result: " + result + "\n"); %>
</p>
</body>
</html>

运行java代码实现发送邮件

  • javax.mail.Session:上下文环境信息,如服务器的主机名、端口号、协议名称等
  • javax.mail.Message:邮件模型,发送邮件和接收邮件的媒介,封装了邮件的信息,如发件人、收件人、邮件标题、邮件内容等
  • javax.mail.Transport:连接邮件SMTP服务器,发送邮件 javax.mail.Store:连接邮件POP3、IMAP服务器,收取邮件

创建SendEmail.java

import java.util.Properties;

import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.Message.RecipientType;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

/**
 * 邮件发送的工具类 使用eyoumailserver,内部账户发邮件
 */
/*
 * javax.mail.Session:上下文环境信息,如服务器的主机名、端口号、协议名称等
 * javax.mail.Message:邮件模型,发送邮件和接收邮件的媒介,封装了邮件的信息,如发件人、收件人、邮件标题、邮件内容等
 * javax.mail.Transport:连接邮件SMTP服务器,发送邮件 javax.mail.Store:连接邮件POP3、IMAP服务器,收取邮件
 */
public class SendEmail {

	private static String userName = "zhangsan@zc.com";// 发送邮件的人
	private static String password = "aaaAAA111";// 发送邮件的账户的密码
	private static String userName2 = "lisi@zc.com";// 接收邮件的人
	private static String port = "25";// 465、587,默认25

	/**
	 * 该方法用来发送邮件
	 * 
	 * @param to:给谁发邮件
	 **/
	public static void sendMain(String to) throws AddressException, MessagingException {
		// 1、创建连接对象,连接到邮箱服务器
		Properties props = new Properties();
		// 开启debug调试
		props.setProperty("mail.debug", "true");
		// stmp服务器需要进行身份验证,也就是有户名和密码的校验,这样才能通过验证
		props.setProperty("mail.smtp.auth", "true");
		// 发送邮件协议名称
		props.setProperty("mail.transport.protocol", "smtp");
		// 设置邮件服务器主机名
		props.setProperty("mail.host", "127.0.0.1");// 本地"localhost"
		// 设端口号(该配置可写可不写)
		props.setProperty("mail.smtp.port", port);
		
		// Authenticator:认证信息
		Session session = Session.getInstance(props, new Authenticator() {
			@Override
			protected PasswordAuthentication getPasswordAuthentication() {
				return new PasswordAuthentication(userName, password);// 使用它给其他账户发邮件
			}
		});

		// 2、创建邮件对象
		Message message = new MimeMessage(session);
		// 2.1设置发件人
		message.setFrom(new InternetAddress(userName));
		// 2、2设置收件人
		message.addRecipient(RecipientType.TO, new InternetAddress(to));
		// 2.3邮件的主题
		message.setSubject("测试发消息");
		// 2.4邮件的正文(即邮件的内容)
		message.setContent("测试邮件:来自zhangsan@zc.com的邮件", "text/html;charset=utf-8");

		// 3.发送邮件
        Transport trans = session.getTransport();  
        //连接邮件服务器  
        trans.connect(userName, password);    
        //发送邮件  
        trans.sendMessage(message, message.getAllRecipients());    
        //关闭连接  
        trans.close();  

		Transport.send(message);// (两种方式都可以)

		System.out.println("发送成功");
	}

	public static void main(String[] args) {
		try {
			sendMain(userName2);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

结果:


JSP 连接数据库

假定您已经了解了 JDBC 应用程序的工作方式。在您开始学习 JSP 数据库访问之前,请访问 Java MySQL 连接 来设置相关驱动及配置。

Java MySQL 连接
Java 使用 JDBC 连接 MySQL 数据库。Java 连接 MySQL 需要驱动包,最新版下载解压后得到 jar 库文件,然后在tomcat 下 lib 目录、对应的项目中build path导入该库文件。

MySQL 8.0 以上版本的数据库连接有所不同:

  • MySQL 8.0 以上版本驱动包见上面的下载地址 。
  • com.mysql.jdbc.Driver 更换为 com.mysql.cj.jdbc.Driver。
  • MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。
  • allowPublicKeyRetrieval=true 允许客户端从服务器获取公钥。
  • 最后还需要设置 CST。

加载驱动与连接数据库方式如下:

Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_demo?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC","root","password");

创建测试数据
接下来我们在 MySQL 中创建 testdb 数据库,并创建 websites 数据表,表结构如下:

mysql -u root -p123456
show databases;
create database zc;
show databases;
use zc;
show tables;


CREATE TABLE `websites` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` char(20) NOT NULL DEFAULT '' COMMENT '站点名称',
  `url` varchar(255) NOT NULL DEFAULT '',
  `alexa` int(11) NOT NULL DEFAULT '0' COMMENT 'Alexa 排名',
  `country` char(10) NOT NULL DEFAULT '' COMMENT '国家',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

desc websites;
select * from websites;

插入一些数据:

INSERT INTO `websites` VALUES
	( '1', 'Google', 'https://www.google.cm/', '1', 'USA' ),
	( '2', '淘宝', 'https://www.taobao.com/', '13', 'CN' ),
	( '3', 'zc自学教程', 'http://www.zc.com', '5892', '' ),
	( '4', '微博', 'http://weibo.com/', '20', 'CN' ),
	( '5', 'Facebook', 'https://www.facebook.com/', '3', 'USA' );

select * from websites;

数据表显示如下:

连接数据库
以下实例MySQLDemo.java使用了 JDBC 连接 MySQL 数据库,注意一些数据如用户名,密码需要根据你的开发环境来配置:

package z.c.xx;

import java.sql.*;
 
public class MySQLDemo {
 
    // 旧连接方式,将废弃 - JDBC 驱动名及数据库 URL
    // static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
    // static final String DB_URL = "jdbc:mysql://localhost:3306/zc";
 
    // 新的连接方式 - JDBC 驱动名及数据库 URL
    static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver";  
    static final String DB_URL = "jdbc:mysql://localhost:3306/zc?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
 
 
    // 数据库的用户名与密码,需要根据自己的设置
    static final String USER = "root";
    static final String PASS = "123456";
 
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        try{
            // 注册 JDBC 驱动
            Class.forName(JDBC_DRIVER);
        
            // 打开链接
            System.out.println("连接数据库...");
            conn = DriverManager.getConnection(DB_URL,USER,PASS);
        
            // 执行查询
            System.out.println(" 实例化Statement对象...");
            stmt = conn.createStatement();
            String sql;
            sql = "SELECT id, name, url FROM websites";
            ResultSet rs = stmt.executeQuery(sql);
        
            // 展开结果集数据库
            while(rs.next()){
                // 通过字段检索
                int id  = rs.getInt("id");
                String name = rs.getString("name");
                String url = rs.getString("url");
    
                // 输出数据
                System.out.print("ID: " + id);
                System.out.print(", 站点名称: " + name);
                System.out.print(", 站点 URL: " + url);
                System.out.print("\n");
            }
            // 完成后关闭
            rs.close();
            stmt.close();
            conn.close();
        }catch(SQLException se){
            // 处理 JDBC 错误
            se.printStackTrace();
        }catch(Exception e){
            // 处理 Class.forName 错误
            e.printStackTrace();
        }finally{
            // 关闭资源
            try{
                if(stmt!=null) stmt.close();
            }catch(SQLException se2){
            }// 什么都不做
            try{
                if(conn!=null) conn.close();
            }catch(SQLException se){
                se.printStackTrace();
            }
        }
        System.out.println("Goodbye!");
    }
}

执行结果:

SELECT操作
接下来的这个例子告诉我们如何使用JSTL SQL标签来运行SQL SELECT语句:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
<title>SELECT 操作</title>
</head>
<body>
<!--JDBC 驱动名及数据库 URL 。数据库的用户名与密码,需要根据自己的设置。useUnicode=true&characterEncoding=utf-8 防止中文乱码 -->
<sql:setDataSource var="snapshot" driver="com.mysql.jdbc.Driver"
     url="jdbc:mysql://localhost:3306/zc?useUnicode=true&characterEncoding=utf-8"
     user="root"  password="123456"/>

<!--查询数据 --> 
<sql:query dataSource="${snapshot}" var="result"> SELECT * from websites; </sql:query>

<h1>JSP 数据库实例 - zc自学教程</h1>
<table border="1" width="100%">
<tr> 
   <th>ID</th>
   <th>站点名</th>
   <th>站点地址</th>
</tr>
<c:forEach var="row" items="${result.rows}">
<tr>
   <td><c:out value="${row.id}"/></td>
   <td><c:out value="${row.name}"/></td>
   <td><c:out value="${row.url}"/></td>
</tr>
</c:forEach>
</table>
 
</body>
</html>

访问http://127.0.0.1:8080/MavenDemo/dbtest.jsp ,运行结果如下:

INSERT操作
这个例子告诉我们如何使用JSTL SQL标签来运行SQL INSERT语句:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert 操作</title>
</head>
<body>
<!--JDBC 驱动名及数据库 URL 。数据库的用户名与密码,需要根据自己的设置。useUnicode=true&characterEncoding=utf-8 防止中文乱码 -->
<sql:setDataSource var="snapshot" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/zc?useUnicode=true&characterEncoding=utf-8"   user="root"  password="123456"/>

<!--插入数据 -->
<sql:update dataSource="${snapshot}" var="result">INSERT INTO websites (name,url,alexa,country) VALUES ('zc自动教程移动站', 'http://m.zc.com', 5993, 'CN');</sql:update>

<sql:query dataSource="${snapshot}" var="result"> SELECT * from websites; </sql:query>

<h1>JSP 数据库实例 - zc自学教程</h1>
<table border="1" width="100%">
<tr>
   <th>ID</th>
   <th>站点名</th>
   <th>站点地址</th>
</tr>
<c:forEach var="row" items="${result.rows}">
<tr>
   <td><c:out value="${row.id}"/></td>
   <td><c:out value="${row.name}"/></td>
   <td><c:out value="${row.url}"/></td>
</tr>
</c:forEach>
</table>
 
</body>
</html>

访问http://127.0.0.1:8080/MavenDemo/dbtest.jsp ,运行结果如下:

DELETE操作
这个例子告诉我们如何使用JSTL SQL标签来运行SQL DELETE语句:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
<title>Delete 操作</title>
</head>
<body>
<!--JDBC 驱动名及数据库 URL 。数据库的用户名与密码,需要根据自己的设置。useUnicode=true&characterEncoding=utf-8 防止中文乱码 -->
<sql:setDataSource var="snapshot" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/zc?useUnicode=true&characterEncoding=utf-8" user="root"  password="123456"/>

<!--删除 ID 为 18 的数据-->
<sql:update dataSource="${snapshot}" var="count">
  DELETE FROM websites WHERE Id = ?
  <sql:param value="${18}" />
</sql:update>

<sql:query dataSource="${snapshot}" var="result"> SELECT * from websites; </sql:query>

<h1>JSP 数据库实例 - zc自学教程</h1>

<table border="1" width="100%">
<tr><th>ID</th><th>站点名</th><th>站点地址</th></tr>
<c:forEach var="row" items="${result.rows}">
<tr><td><c:out value="${row.id}"/></td><td><c:out value="${row.name}"/></td><td><c:out value="${row.url}"/></td></tr>
</c:forEach>
</table>
 
</body>
</html>

访问http://127.0.0.1:8080/MavenDemo/dbtest.jsp ,运行结果如下:

UPDATE操作
这个例子告诉我们如何使用JSTL SQL标签来运行SQL UPDATE语句:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.*,java.sql.*"%>
<%@ page import="javax.servlet.http.*,javax.servlet.*" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>
<head>
<title>Update 操作</title>
</head>
<body>
<!--JDBC 驱动名及数据库 URL 。数据库的用户名与密码,需要根据自己的设置。useUnicode=true&characterEncoding=utf-8 防止中文乱码 -->
<sql:setDataSource var="snapshot" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/zc?useUnicode=true&characterEncoding=utf-8" user="root"  password="123456"/>

<!-- 修改 ID 为 3 的名字:zc自学教程改为 zc -->
<c:set var="SiteId" value="3"/>
 
<sql:update dataSource="${snapshot}" var="count">
  UPDATE websites SET name = 'zc' WHERE Id = ?
  <sql:param value="${SiteId}" />
</sql:update>

<sql:query dataSource="${snapshot}" var="result"> SELECT * from websites; </sql:query>

<h1>JSP 数据库实例 - zc自学教程</h1>
<table border="1" width="100%">
<tr><th>ID</th><th>站点名</th><th>站点地址</th></tr>
<c:forEach var="row" items="${result.rows}">
<tr><td><c:out value="${row.id}"/></td><td><c:out value="${row.name}"/></td><td><c:out value="${row.url}"/></td></tr>
</c:forEach>
</table>
 
</body>
</html>

访问http://127.0.0.1:8080/MavenDemo/dbtest.jsp ,运行结果如下:


JSP XML 数据处理

当通过HTTP发送XML数据时,就有必要使用JSP来处理传入和流出的XML文档了,比如RSS文档。作为一个XML文档,它仅仅只是一堆文本而已,使用JSP创建XML文档并不比创建一个HTML文档难。

使用JSP发送XML
使用JSP发送XML内容就和发送HTML内容一样。唯一的不同就是您需要把页面的context属性设置为text/xml。要设置context属性,使用<%@page % >命令,就像这样:<%@ page contentType="text/xml" %>
接下来这个例子向浏览器发送XML内容:

<%@ page contentType="text/xml" %>

<books>
   <book>
      <name>Padam History</name>
      <author>ZARA</author>
      <price>100</price>
   </book>
</books>

使用不同的浏览器来访问这个例子,看看这个例子所呈现的文档树。

在JSP中处理XML
在使用JSP处理XML之前,您需要将与XML 和XPath相关的三个库文件(xercesImpl.jar,xalan.jar,serializer.jar)放在\lib目录下:XercesImpl.jarxalan.jar,本站下载,包含 xercesImpl.jar,xalan.jar,serializer.jar 三个库文件:点击下载

books.xml文件:

<books>
<book>
  <name>Padam History</name>
  <author>ZARA</author>
  <price>100</price>
</book>
<book>
  <name>Great Mistry</name>
  <author>NUHA</author>
  <price>2000</price>
</book>
</books>

xml.jsp文件:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>

<html>
<head>
  <title>JSTL x:parse Tags</title>
</head>
<body>
<h3>Books Info:</h3>
<c:import var="bookInfo" url="http://localhost:8080/MavenDemo/books.xml"/>
 
<x:parse xml="${bookInfo}" var="output"/>
<b>The title of the first book is:</b>
<x:out select="$output/books/book[1]/name" /><br>

<b>The price of the second book:</b> 
<x:out select="$output/books/book[2]/price" />
 
</body>
</html>

访问 http://127.0.0.1:8080/MavenDemo/xml.jsp 如下:

使用JSP格式化XML
这个是XSLT样式表style.xsl文件:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
 
<xsl:output method="html" indent="yes"/>
 
<xsl:template match="/">
  <html>
  <body>
   <xsl:apply-templates/>
  </body>
  </html>
</xsl:template>
 
<xsl:template match="books">
  <table border="1" width="100%">
    <xsl:for-each select="book">
      <tr>
        <td><i><xsl:value-of select="name"/></i></td>
        <td><xsl:value-of select="author"/></td>
        <td><xsl:value-of select="price"/></td>
      </tr>
    </xsl:for-each>
  </table>
</xsl:template>
</xsl:stylesheet>

这个是xml.jsp文件:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="x" uri="http://java.sun.com/jsp/jstl/xml" %>
 
<html>
<head>
  <title>JSTL x:transform Tags</title>
</head>
<body>
<h3>Books Info:</h3>
<c:set var="xmltext">
  <books>
    <book>
      <name>Padam History</name>
      <author>ZARA</author>
      <price>100</price>
    </book>
    <book>
      <name>Great Mistry</name>
      <author>NUHA</author>
      <price>2000</price>
    </book>
  </books>
</c:set>
 
<c:import url="http://localhost:8080/MavenDemo/style.xsl" var="xslt"/>
<x:transform xml="${xmltext}" xslt="${xslt}"/>
 
</body>
</html>

运行结果如下:


JSP JavaBean

JavaBean 是特殊的 Java 类,使用 Java 语言书写,并且遵守 JavaBean API 规范。接下来给出的是 JavaBean 与其它 Java 类相比而言独一无二的特征:

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

JavaBean 属性
一个 JavaBean 对象的属性应该是可访问的。这个属性可以是任意合法的 Java 数据类型,包括自定义 Java 类。一个 JavaBean 对象的属性可以是可读写,或只读,或只写。JavaBean 对象的属性通过 JavaBean 实现类中提供的两个方法来访问:

方法描述
getPropertyName()举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 getMyName() 来读取这个属性。这个方法也称为访问器。
setPropertyName()举例来说,如果属性的名称为 myName,那么这个方法的名字就要写成 setMyName()来写入这个属性。这个方法也称为写入器。

一个只读的属性只提供 getPropertyName() 方法,一个只写的属性只提供 setPropertyName() 方法。

JavaBean 程序示例,这是 StudentsBean.java 文件:

package z.c.xx;

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

访问JavaBean
<jsp:useBean> 标签可以在 JSP 中声明一个 JavaBean,然后使用。声明后,JavaBean 对象就成了脚本变量,可以通过脚本元素或其他自定义标签来访问。<jsp:useBean> 标签的语法格式如下:<jsp:useBean id="bean 的名字" scope="bean 的作用域" typeSpec/>
其中,根据具体情况,scope 的值可以是 page,request,session 或 application。id值可任意只要不和同一 JSP 文件中其它 <jsp:useBean> 中 id 值一样就行了。

接下来给出的是 <jsp:useBean> 标签的一个简单的用法:

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

http://127.0.0.1:8080/MavenDemo/JavaBean.jsp 将会产生如下结果:

访问 JavaBean 对象的属性
在 <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="z.c.xx.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>

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

</body>
</html>

访问http://127.0.0.1:8080/MavenDemo/JavaBean.jsp ,运行结果如下:


JSP 国际化

在开始前,需要解释几个重要的概念:

  • 国际化(i18n):表明一个页面根据访问者的语言或国家来呈现不同的翻译版本。
  • 本地化(l10n):向网站添加资源,以使它适应不同的地区和文化。比如网站的印度语版本。
  • 区域:这是一个特定的区域或文化,通常认为是一个语言标志和国家标志通过下划线连接起来。比如"en_US"代表美国英语地区。
如果想要建立一个全球化的网站,就需要关心一系列项目。本章将会详细告诉您如何处理国际化问题,并给出了一些例子来加深理解。

JSP容器能够根据request的locale属性来提供正确地页面版本。接下来给出了如何通过request对象来获得Locale对象的语法:java.util.Locale request.getLocale()

检测Locale
下表列举出了Locale对象中比较重要的方法,用于检测request对象的地区,语言,和区域。所有这些方法都会在浏览器中显示国家名称和语言名称:

方法描述
String getCountry()返回国家/地区码的英文大写,或 ISO 3166 2-letter 格式的区域
String getDisplayCountry()返回要显示给用户的国家名称
String getLanguage()返回语言码的英文小写,或ISO 639 格式的区域
String getDisplayLanguage()返回要给用户看的语言名称
String getISO3Country()返回国家名称的3字母缩写
String getISO3Language()返回语言名称的3字母缩写

实例演示如何在JSP中显示语言和国家:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.* "%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
   //获取客户端本地化信息
   Locale locale = request.getLocale();
   String language = locale.getLanguage();
   String country = locale.getCountry();
%>
<html>
<head>
<title>Detecting Locale 检测区域设置</title>
</head>
<body>
<center>
<h1>Detecting Locale 检测区域设置</h1>
</center>
<p align="center">
<% 
   out.println("Locale : " + locale  + "<br />");
   out.println("Language : " + language  + "<br />");
   out.println("Country  : " + country   + "<br />");
%>
</p>
</body>
</html>

结果如下:

语言设置
JSP 可以使用西欧语言来输出一个页面,比如英语,西班牙语,德语,法语,意大利语等等。由此可见,设置 Content-Language 信息头来正确显示所有字符是很重要的。

第二点就是,需要使用 HTML 字符实体来显示特殊字符,比如 "ñ" 代表的是 ñ,"¡"代表的是 ¡ :

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.* "%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
    // Set response content type
    response.setContentType("text/html");
    // Set spanish language code.
    response.setHeader("Content-Language", "es");
    String title = "En Espa?ol";

%>
<html>
<head>
<title><%  out.print(title); %></title>
</head>
<body>
<center>
<h1><%  out.print(title); %></h1>
</center>
<div align="center">
<p>En Espa?ol</p>
<p>?Hola Mundo!</p>
<p>哈哈哈!</p>
</div>
</body>
</html>

区域特定日期
可以使用java.text.DateFormat类和它的静态方法getDateTimeInstance()来格式化日期和时间。接下来的这个例子显示了如何根据指定的区域来格式化日期和时间:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.* "%>
<%@ page import="java.text.DateFormat,java.util.Date" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
    String title = "Locale Specific Dates";
    //Get the client's Locale
    Locale locale = request.getLocale( );
    String date = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT, locale).format(new Date( ));
%>
<html>
<head>
<title><% out.print(title); %></title>
</head>
<body>
<center>
<h1><% out.print(title); %></h1>
</center>
<div align="center">
<p>Local Date: <%  out.print(date); %></p>
</div>
</body>
</html>

区域特定货币
可以使用java.text.NumberFormat类和它的静态方法getCurrencyInstance()来格式化数字。比如在区域特定货币中的long型和double型。接下来的例子显示了如何根据指定的区域来格式化货币:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.* "%>
<%@ page import="java.text.NumberFormat,java.util.Date" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
    String title = "Locale Specific Currency";
    //Get the client's Locale
    Locale locale = request.getLocale( );
    NumberFormat nft = NumberFormat.getCurrencyInstance(locale);
    String formattedCurr = nft.format(1000000);
%>
<html>
<head>
<title><% out.print(title); %></title>
</head>
<body>
<center>
<h1><% out.print(title); %></h1>
</center>
<div align="center">
<p>Formatted Currency: <%  out.print(formattedCurr); %></p>
</div>
</body>
</html>

区域特定百分比
可以使用java.text.NumberFormat类和它的静态方法getPercentInstance()来格式化百分比。接下来的例子告诉我们如何根据指定的区域来格式化百分比:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.io.*,java.util.Locale" %>
<%@ page import="javax.servlet.*,javax.servlet.http.* "%>
<%@ page import="java.text.NumberFormat,java.util.Date" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%
    String title = "Locale Specific Percentage";
    //Get the client's Locale
    Locale locale = request.getLocale( );
    NumberFormat nft = NumberFormat.getPercentInstance(locale);
    String formattedPerc = nft.format(0.51);
%>
<html>
<head>
<title><% out.print(title); %></title>
</head>
<body>
<center>
<h1><% out.print(title); %></h1>
</center>
<div align="center">
<p>Formatted Percentage: <%  out.print(formattedPerc); %></p>
</div>
</body>
</html>


JSP 自定义标签

自定义标签就是用户(开发者)自己定义的标签。自定义标签可以让 JSP 页面中不含有 Java 代码,只含有 HTML 代码和部分标签,就能实现业务逻辑的调用。自定义标签的优点如下:

  • JSP 页面对脚本的需求和依赖性
  • 将 JSP 页面和业务逻辑分开,增加了程序的可维护性
  • 可重复调用相同的业务逻辑,增加了程序的可重用性

使用自定义标签步骤如下:

  1. 自定义标签实现类
  2. 编写 tld 标签库描述文件
  3. 在 JSP 页面中使用自定义标签

自定义标签的语法
使用 taglib 指令指定 tld 文件的路径。<%@ taglib prefix="ex" uri="WEB-INF/custom.tld"%>其中:prefix 指定自定义标签的前缀,uri 指定 tld 文件的路径。

使用自定义标签有以下 2 种格式,其中prefix 表示自定义标签的前缀,tagname 表示自定义标签的名称,attr 表示自定义标签的属性,value 表示自定义标签的属性值:

<prefix:tagname attr1=value1....attrn=valuen />

或

<prefix:tagname attr1=value1....attrn=valuen >     标签体     </prefix:tagname>  

创建”Hello”标签
下面创建一个简单的自定义标签 <bc:Hello>。

1、创建处理标签的Java类 HelloTag.java 代码如下,重写了doTag() 方法,getJspContext() 方法用来获取 JspContext 对象,并将 out.println 中的内容传递给 JspWriter 对象:

package z.c.xx;

import javax.servlet.jsp.tagext.*;
import javax.servlet.jsp.*;
import java.io.*;

public class HelloTag extends SimpleTagSupport {

  public void doTag() throws JspException, IOException {
    JspWriter out = getJspContext().getOut();
    out.println("欢迎来到自学教程,我们的网址是:www.zc.com");
  }
}

注意:SimpleTagSupport 实现了 SimpleTag 接口,该接口是 JSP 2.X 标签库新增的自定义标签接口类。该接口极其简单,提供了 doTag() 方法去处理自定义标签中的逻辑以及标签体。相对来说,JSP 1.X 比较复杂,处理标签的 Java 类需要继承 TagSupport 类,重写 doStartTag()、doAfterBody()、doEndTag() 等方法。

2、创建tld标签库描述文件
tld 文件采用 XML 文件格式进行描述,后缀名为 .tld,用来将写好的类映射成 JSP 标签。tld 文件保存在 WEB-INF 目录下,为了管理方便,可以在目录下创建 tlds 文件夹。tld 文件中只能有一对 taglib 标签,taglib 标签下可以有多个 tag 标签,每个 tag 标签代表一个自定义标签。文章后面会对 tld 文件中的各个标签进行说明。custom.tld 代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <!-- 标签库版本号 -->
    <tlib-version>1.0</tlib-version>
    <!-- JSP版本号 --> 
    <jsp-version>2.0</jsp-version>
    <!-- 当前标签库的前缀 -->
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>z.c.xx.HelloTag</tag-class>
        <!-- 正文内容正文内容,没有则用 empty 表示 -->
        <body-content>empty</body-content>
        <!-- 自定义标签的功能描述 -->
        <description>输出内容</description>
    </tag>
</taglib>  

3、使用自定义标签
使用自定义标签和使用 JSTL 标签是一样的, diy.JSP 文件中使用 Hello 标签代码如下。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="bc" uri="WEB-INF/tlds/custom.tld"%>
<!DOCTYPE html>
<html>
<head>
<title>自学教程(zc.com)</title>
</head>
<body>
    <bc:Hello />
</body>
</html>

运行结果如下:

tld标签库描述文件
tld 文件中常用的标签有 taglib、tag、attribute 和 variable。下面以 custom.tld 文件为例介绍其含义。
1、<taglib>标签:<taglib> 标签用来设置整个标签库信息,其说明如下表所示。

属  性说  明
tlib-version标签库版本号
jsp-versionJSP版本号
short-name当前标签库的前缀
uri页面引用的自定义标签的 uri 地址
name自定义标签名称
tag-class自定义标签实现类路径
description自定义标签的功能描述
attribute自定义标签的指定属性,可以有多个

2、<tag>标签:<tag> 标签用来定义标签具体的内容,其说明如下表所示。

属  性说  明
name自定义标签名称
tag-class自定义标签实现类
body-content有 3 个值:empty(表示没有标签体)、JSP(表示标签体可以加入 JSP 程序代码)、tagdependent(表示标签体中的内容由标签自己处理)
description自定义标签的功能描述
attribute自定义标签功能的指定属性,可以有多个
variable自定义标签的变量属性

3、<attribute>标签:<attribute> 标签用来定义 <tag> 标签中的属性,其说明如下表所示。使用 <attribute> 的属性时要注意元素顺序。

属  性说  明
name属性名称
description属性描述
required指定属性是否是必须的,默认值:false
rtexprvalue属性值是否支持 JSP 表达式
type定义该属性的 Java 类型,默认值:String
fragment如果声明了该属性,属性值将被视为一个 JspFragment

4、<variable> 标签:<variable> 标签用来定义 <tag> 标签中的变量属性,其说明如下表所示。

属  性说  明
declare变量声明
description变量描述
name-from-attribute指定的属性名称,其值为变量,在调用 JSP 页面时可以使用的名字
name-given变量名(标签使用时的变量名)
scope变量的作用范围,有 3 个值:NESTED 开始和结束标签之间、AT_BEGIN 从开始标签到页面结束、AT_END 从结束标签之后到页面结束
variable-class变量的 Java 类型,默认值:String

自定义标签属性
在自定义标签中设置属性,自定义标签类中必须有相应的 setter 方法。为 bc:Hello 标签添加 message 属性,HelloTag.java 类代码如下:

package z.c.xx;

import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;
public class HelloTag extends SimpleTagSupport {
    private String message;
    public void setMessage(String message) {
        this.message = message;
    }
    StringWriter sw = new StringWriter();
    public void doTag() throws JspException, IOException {
        if (message != null) {
            // 从属性中使用 message
            JspWriter out = getJspContext().getOut();
            out.println(message);
        } else {
            // 从内容中使用 message
            getJspBody().invoke(sw);
            getJspContext().getOut().println(sw.toString());
        }
    }
}

下面使用 标签为 bc:Hello 标签添加 message 属性,custom.tld 如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <!-- 标签库版本号 -->
    <tlib-version>1.0</tlib-version>
    <!-- JSP版本号 --> 
    <jsp-version>2.0</jsp-version>
    <!-- 当前标签库的前缀 -->
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>z.c.xx.HelloTag</tag-class>
        <!-- 正文内容正文内容,没有则用 empty 表示 -->
        <body-content>empty</body-content>
        <!-- 自定义标签的功能描述 -->
        <description>输出内容</description>
        <attribute>
            <name>message</name>
        </attribute>
    </tag>
</taglib>

这时就可以在 diy.jsp 页面中使用 message 属性了,如下:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="bc" uri="WEB-INF/tlds/custom.tld"%>
<!DOCTYPE html>
<html>
<head>
<title>自学教程(zc.com)</title>
</head>
<body>
    <bc:Hello message="欢迎来到zc自学教程,我们的网址是:www.zc.com!"/>
</body>
</html>

运行结果:

自定义标签的标签体
可以像 JSTL 标签库一样在标签中包含消息内容,如在 Hello 标签中包含内容,JSP 使用格式如下:

<bc:Hello>
   欢迎来到自学教程,我们的网址是:www.runoops.com
</bc:Hello>

修改 HelloTag.java 类,如下:

package z.c.xx;

import java.io.IOException;
import java.io.StringWriter;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class HelloTag extends SimpleTagSupport {
    StringWriter sw = new StringWriter();
    public void doTag() throws JspException, IOException {
        getJspBody().invoke(sw);
        getJspContext().getOut().println(sw.toString());
    }
}

修改 custom.tld 文件,如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE taglib  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"  "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <!-- 标签库版本号 -->
    <tlib-version>1.0</tlib-version>
    <!-- JSP版本号 --> 
    <jsp-version>2.0</jsp-version>
    <!-- 当前标签库的前缀 -->
    <short-name>Example TLD</short-name>
    <tag>
        <!-- 自定义标签的名称,在页面中通过它来使用标签 -->
        <name>Hello</name>
        <!-- 自定义标签的实现类路径 -->
        <tag-class>z.c.xx.HelloTag</tag-class>
        <!-- 正文内容正文内容,没有则用 empty 表示 -->
        <body-content>tagdependent</body-content>
    </tag>
</taglib>

diy.JSP 文件中使用 Hello 标签代码如下。

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="bc" uri="WEB-INF/tlds/custom.tld"%>
<!DOCTYPE html>
<html>
<head>
<title>自学教程(zc.com)</title>
</head>
<body>
    <bc:Hello>
   		欢迎来到自学教程,我们的网址是:www.zc.com!
	</bc:Hello>
</body>
</html>

运行结果如下:


JSP 多语言切换

使用JSP的fmt标签实现国际化支持。使用JSP的fmt标签配置i18n国际化资源文件可以实现根据不同的地区和语言切换不同的显示。switchLan.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>    
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<c:out value="${system_language}" escapeXml="false" default="defaultValue" />
<fmt:setBundle basename="i18n/i18n_${system_language}"/>

<div id="footer">
    <div id="bookname">
        <span><fmt:message key="common.bookname"/></span>
    </div>
    <div id="copyright">
        <span><fmt:message key="common.copyright"/></span>
    </div>
    <div id="language">
        <span><fmt:message key="common.language"/>:</span>
        <a href="#" data-value="zh_CN">中文</a>
        <span>|</span>
        <a href="#" data-value="en_US">English</a>
    </div>
</div>

<script type="text/javascript">
        $(function () {
            $('#language').find('a').click(function () {
                var language = $(this).data('value');
                document.cookie = "cookie_language=" + language +";expires=365";
                location.reload();
            });
        });
    </script>
</body>
</html>

在src/main/resources 路径下新建i18n文件夹(或者在其他的路径下也可以,编译之后,文件夹要在classes目录下),新建资源文件 i18n_en_US.properties 和 i18n_zh_CN.properties,内容如下:

i18n_en_US.properties:

common.copyright=Copyright @ 2023
common.language=language
common.bookname=HTTP by picture

i18n_zh_CN.properties:

common.copyright=版权所有 @ 2023
common.language=系统语言
common.bookname=图解HTTP

我们使用过滤器肯定要导入相应的jar包才行,Filter就在servlet-api.jar中,我们将该jar包放到WEB-INF下的lib目录下面,然后加入项目。



在服务器端添加一个filter.java,在请求中判断需要显示哪种语言,通过设置system_language的值,使页面的显示发生变化:

package z.c.xx;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Cookie;
import java.util.Locale;


/**
 * Servlet Filter implementation class FirstFilter
 */
public class FirstFilter implements Filter {
    private FilterConfig filterConfig = null;
    String paramValue = null;
    private static final String COOKIE_LANGUAGE = "cookie_language";
    private static final String SYSTEM_LANGUAGE = "system_language";
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        paramValue = filterConfig.getInitParameter("encoding");
        System.out.println("初始化!"); 
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("begin headers-------------------");
        Enumeration<?> headerNames = ((HttpServletRequest)request).getHeaderNames();
        
        while(headerNames.hasMoreElements()) {
            String headerName = (String)headerNames.nextElement();
            System.out.println(headerName + ": " + ((HttpServletRequest)request).getHeader(headerName));
        }
        System.out.println("end headers-------------------");
        
        //在调用目标前写入响应内容
        request.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String systemLanguage = getSystemLanguage(httpServletRequest);
        request.setAttribute(SYSTEM_LANGUAGE, systemLanguage);        
        System.out.println("FirstFilter执行前!");
        
        chain.doFilter(request, response);

        //在目标返回后写入响应内容
        System.out.println("FirstFilter执行后!");
        PrintWriter out = response.getWriter();
        out.println("<br><br><br><br><br><br>名称为encoding的初始化参数的值为:" + paramValue);
        out.println("<br>当前Web程序的真实路径为:" + filterConfig.getServletContext().getRealPath("/"));
        
        //out.println("<br>修改了test.html文件!");
    }
    
    @Override
    public void destroy() {
        this.filterConfig = null;
    }
    
    private String getSystemLanguage(HttpServletRequest request) {
        String systemLanguage = null;
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (COOKIE_LANGUAGE.equals(cookie.getName())) {
                    systemLanguage = cookie.getValue();
                    break;
                }
            }
        }

        if (systemLanguage == null || "".equals(systemLanguage)) {
            systemLanguage = request.getLocale().toString();
        }
        if (systemLanguage == null || "".equals(systemLanguage)) {
            systemLanguage = Locale.getDefault().toString();
        }
        return systemLanguage;
    }
}

我们通过request.setAttribute("system_language",${system_language}); 来改变JSP中的bundle的名字,实现切换显示语言的功能,getSystemLanguage()方法,先是从cookie中找显示语言,若没有找到,则使用浏览器的语言,如果是空,则取操作系统的默认语言。
web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
  <filter>
    <display-name>FirstFilter</display-name>
    <filter-name>FirstFilter</filter-name>
    <filter-class>z.c.xx.FirstFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>FirstFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

为了能够动态显示出来效果,我们提供两个按钮,点击之后可以切换语言:


JSP 调试

要测试/调试一个JSP或servlet程序总是那么的难。JSP和Servlets程序趋向于牵涉到大量客户端/服务器之间的交互,这很有可能会产生错误,并且很难重现出错的环境。接下来将会给出一些小技巧和小建议,来帮助您调试程序。

使用System.out.println()
System.out.println()可以很方便地标记一段代码是否被执行。当然,我们也可以打印出各种各样的值。此外:

  • 自从System对象成为Java核心对象后,它便可以使用在任何地方而不用引入额外的类。使用范围包括Servlets,JSP,RMI,EJB's,Beans,类和独立应用。
  • 与在断点处停止运行相比,用System.out进行输出不会对应用程序的运行流程造成重大的影响,这个特点在定时机制非常重要的应用程序中就显得非常有用了。

接下来给出了使用System.out.println()的语法:System.out.println("Debugging message");

这是一个使用System.out.print()的简单例子:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head><title>System.out.println</title></head>
<body>
<c:forEach var="counter" begin="1" end="10" step="1" >
   <c:out value="${counter-5}"/><br />
   <% System.out.println( "counter= " + pageContext.findAttribute("counter") ); %>
</c:forEach>
<br /><br /><br /><% out.println( "hahaha" ); %>
</body>
</html>

现在,如果运行上面的例子的话,它将会产生如下的结果:

如果使用的是Tomcat服务器,将jstl.jar和standard.jar复制到Tomcat文件的lib目录下,启动D:\apache-tomcat-9.0.79\bin\tomcat9w.exe,debug.jsp放到D:\apache-tomcat-9.0.79\webapps\ROOT\,就能够在logs目录下的stdout.log文件中发现多出了如下内容:

使用这种方法可以将变量和其它的信息输出至系统日志中,用来分析并找出造成问题的深层次原因。

使用JDB Logger
J2SE日志框架可为任何运行在JVM中的类提供日志记录服务。因此我们可以利用这个框架来记录任何信息。让我们来重写以上代码,使用JDK中的 logger API:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page import="java.util.logging.Logger" %>
<%@page import="java.util.logging.Level" %>

<html>
<head><title>Logger.info</title></head>
<body>
<% //创建一个日志记录器,它首先通过this.getClass().getName()获取当前类的类名,然后使用这个类名作为参数调用Logger.getLogger()方法,获取对应的日志记录器。
   Logger logger=Logger.getLogger(this.getClass().getName());  
   // 设置日志级别
   logger.setLevel(Level.INFO);
   //获取日志级别
   Level level = logger.getLevel();   %>  
  
<% out.println( "日志级别:" + level ); %>
<c:forEach var="counter" begin="1" end="10" step="1" >
   <c:set var="myCount" value="${counter-5}" />
   <c:out value="${myCount}"/></br>
   <% String message = "counter=" + pageContext.findAttribute("counter")+ " myCount="+ pageContext.findAttribute("myCount");
	  logger.info( message );
	  logger.info( "counter=" + pageContext.findAttribute("counter")+ " myCount="+ pageContext.findAttribute("myCount") );
   %>
</c:forEach>

</body>
</html>

它的运行结果与先前的类似,但是,它可以获得额外的信息输出至stdout.log文件中。在这我们使用了logger中的info方法。下面我们给出stdout.log文件中的一个快照:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@page import="java.util.logging.Logger" %>
<%@page import="java.util.logging.Level" %>

<html>
<head><title>Logger.info</title></head>
<body>
<% //创建一个日志记录器,它首先通过this.getClass().getName()获取当前类的类名,然后使用这个类名作为参数调用Logger.getLogger()方法,获取对应的日志记录器。
   Logger logger=Logger.getLogger(this.getClass().getName());  
   // 设置日志级别
   logger.setLevel(Level.INFO);
   //获取日志级别
   Level level = logger.getLevel();   %>  
  
<% out.println( "日志级别:" + level ); %>
<c:forEach var="counter" begin="1" end="10" step="1" >
   <c:set var="myCount" value="${counter-5}" />
   <c:out value="${myCount}"/></br>
   <% String message = "counter=" + pageContext.findAttribute("counter")+ " myCount="+ pageContext.findAttribute("myCount");
	  logger.info( message );
	  logger.info( "counter=" + pageContext.findAttribute("counter")+ " myCount="+ pageContext.findAttribute("myCount") );
	  System.out.println( message );
   %>
</c:forEach>

</body>
</html>

消息可以使用各种优先级发送,通过使用sever(),warning(),info(),config(),fine(),finer(),finest()方法。finest()方法用来记录最好的信息,而sever()方法用来记录最严重的信息。

Logger是Java日志框架中的一个重要类,用于记录和管理日志信息。getLogger()方法用于获取指定类名的日志记录器。如果指定的类名不存在,那么getLogger()方法会返回一个空的日志记录器。在使用Logger.getLogger(this.getClass().getName())获取日志记录器时,需要注意以下几点:

  1. 在使用getLogger()方法获取日志记录器时,需要传入一个类名作为参数。这个类名通常是当前类的类名,可以通过this.getClass().getName()获取。
  2. 如果指定的类名不存在,那么getLogger()方法会返回一个空的日志记录器。因此,在使用getLogger()方法获取日志记录器时,需要确保指定的类名存在。
  3. 在获取日志记录器后,可以使用log()方法记录日志信息。log()方法有多个重载版本,可以用于记录不同级别的日志信息。

以下是一些常见的日志级别及其含义:

  1. Level.SEVERE:表示严重的错误,例如系统崩溃、程序崩溃等。
  2. Level.WARNING:表示警告信息,例如系统出现异常、程序出现异常等。
  3. Level.INFO:表示一般的信息,例如程序运行状态、系统运行状态等。
  4. Level.CONFIG:表示配置信息,例如程序配置信息、系统配置信息等。
  5. Level.FINE:表示详细的日志信息,例如程序运行状态、系统运行状态等。
  6. Level.FINER:表示更详细的日志信息,例如程序运行状态、系统运行状态等。
  7. Level.FINEST:表示最详细的日志信息,例如程序运行状态、系统运行状态等。

初始化Log4j的一个实例
Log4jExample.java:

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class Log4jExample {
    public static void main(String[] args) {
    	// 加载日志配置文件
        PropertyConfigurator.configure("src/main/resources/log4j.properties");
        
        // 创建一个日志记录器,创建一个Log4j实例
        Logger logger = Logger.getLogger(Log4jExample.class);

        // 记录一条日志信息
        logger.info("This is an example log message.");
    }
}

在上述代码中,首先通过Logger.getLogger()方法创建了一个Log4j实例。然后,通过logger.info()方法记录了一条日志信息。info()方法用于记录一条信息级别的日志信息。需要注意的是,为了使用Log4j,需要在项目中添加Log4j的依赖。在Maven项目中,可以在pom.xml文件中添加以下依赖:

	<dependency>
    	<groupId>log4j</groupId>
    	<artifactId>log4j</artifactId>
    	<version>1.2.17</version>
	</dependency>	

在Gradle项目中,可以在build.gradle文件中添加以下依赖:dependencies { implementation 'log4j:log4j:1.2.17' }
log4j.properties

###将日志信息输出到控制台###

log4j.appender.stdout=org.apache.log4j.ConsoleAppender

log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}%m%n  %l%n

###将日志信息输出到文件中###

log4j.appender.file=org.apache.log4j.FileAppender

log4j.appender.file.File=sysInfo.log

log4j.appender.file.layout=org.apache.log4j.PatternLayout

log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss}%m%n  %l%n

###设置日志的优先级别###

log4j.rootLogger=debug,stdout,file	

调试工具
NetBeans是树形结构,是开源的Java综合开发环境,支持开发独立的Java应用程序和网络应用程序,同时也支持JSP调试。NetBeans支持如下几个基本的调试功能:

  • 断点
  • 单步跟踪
  • 观察点
详细的信息可以查看NetBeans使用手册。

使用JDB Debugger
可以在JSP和servlets中使用jdb命令来进行调试,就像调试普通的应用程序一样。

通常,我们直接调试sun.servlet.http.HttpServer 对象来查看HttpServer在响应HTTP请求时执行JSP/Servlets的情况。这与调试applets非常相似。不同之处在于,applets程序实际调试的是sun.applet.AppletViewer。

大部分调试器在调试applets时都能够自动忽略掉一些细节,因为它知道如何调试applets。如果想要将调试对象转移到JSP身上,就需要做好以下两点:

  • 设置调试器的classpath,让它能够找到sun.servlet.http.Http-Server 和相关的类。
  • 设置调试器的classpath,让它能够找到您的JSP文件和相关的类。
设置好classpath后,开始调试sun.servlet.http.Http-Server 。您可以在JSP文件的任意地方设置断点,只要你喜欢,然后使用浏览器发送一个请求给服务器就应该可以看见程序停在了断点处。

使用注释
程序中的注释在很多方面都对程序的调试起到一定的帮助作用。注释可以用在调试程序的很多方面中。

JSP使用Java注释。如果一个BUG消失了,就请仔细查看您刚注释过的代码,通常都能找出原因。

客户端和服务器的头模块
有时候,当JSP没有按照预定的方式运行时,查看未加工的HTTP请求和响应也是很有用的。如果对HTTP的结构很熟悉的话,您可以直接观察request和response然后看看这些头模块到底怎么了。

重要调试技巧
这里我们再透露两个调试JSP的小技巧:

  • 使用浏览器显示原始的页面内容,用来区分是否是格式问题。这个选项通常在View菜单下。
  • 确保浏览器在强制重新载入页面时没有捕获先前的request输出。若使用的是Netscape Navigator浏览器,则用Shift-Reload;若使用的是IE浏览器,则用Shift-Refresh。

JSP 异常处理

当编写JSP程序的时候,程序员可能会遗漏一些BUG,这些BUG可能会出现在程序的任何地方。JSP代码中通常有以下几类异常:

  • 检查型异常:检查型异常就是一个典型的用户错误或者一个程序员无法预见的错误。举例来说,如果一个文件将要被打开,但是无法找到这个文件,则一个异常被抛出。这些异常不能在编译期被简单地忽略。
  • 运行时异常:一个运行时异常可能已经被程序员避免,这种异常在编译期将会被忽略。
  • 错误:错误不是异常,但问题是它超出了用户或者程序员的控制范围。错误通常会在代码中被忽略,您几乎不能拿它怎么样。举例来说,栈溢出错误。这些错误都会在编译期被忽略
本节将会给出几个简单而优雅的方式来处理运行时异常和错误。

使用Exception对象
exception对象是Throwable子类的一个实例,只在错误页面中可用。下表列出了Throwable类中一些重要的方法:

方法描述
public String getMessage() 返回异常的信息。这个信息在Throwable构造函数中被初始化
public ThrowablegetCause() 返回引起异常的原因,类型为Throwable对象
public String toString() 返回类名
public void printStackTrace() 将异常栈轨迹输出至System.err
public StackTraceElement [] getStackTrace() 以栈轨迹元素数组的形式返回异常栈轨迹
public ThrowablefillInStackTrace() 使用当前栈轨迹填充Throwable对象

JSP提供了可选项来为每个JSP页面指定错误页面。无论何时页面抛出了异常,JSP容器都会自动地调用错误页面。接下来的例子为main.jsp指定了一个错误页面。使用<%@page errorPage="XXXXX"%>指令指定一个错误页面。

<%@ page errorPage="showError.jsp" %>

<html>
<head>
   <title>Error Handling Example</title>
</head>
<body>
<%
   // Throw an exception to invoke the error page
   int x = 1;
   if (x == 1)
   {
      throw new RuntimeException("Error condition!!!");
   }
%>
</body>
</html>

现在,编写showError.jsp文件如下:

<%@ page isErrorPage="true" %>
<html>
<head>
<title>Show Error Page</title>
</head>
<body>
<h1>Opps...</h1>
<p>Sorry, an error occurred.</p>
<p>Here is the exception stack trace: </p>
<pre>
<% exception.printStackTrace(response.getWriter()); %>
</pre>

注意到,showError.jsp文件使用了<%@page isErrorPage="true"%>指令,这个指令告诉JSP编译器需要产生一个异常实例变量。

现在试着访问main.jsp页面,它将会产生如下结果:

在错误页面中使用JSTL标签
可以利用JSTL标签来编写错误页面showError.jsp。这个例子中的代码与上例代码的逻辑几乎一样,但是本例的代码有更好的结构,并且能够提供更多信息:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page isErrorPage="true"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Show Error Page</title>
</head>
<body>
	<h1>Opps...</h1>
	<table width="100%" border="1">
		<tr valign="top"><td width="40%"><b>Error:</b></td><td>${pageContext.exception}</td></tr>
		<tr valign="top"><td><b>URI:</b></td><td>${pageContext.errorData.requestURI}</td></tr>
		<tr valign="top"><td><b>Status code:</b></td><td>${pageContext.errorData.statusCode}</td></tr>
		<tr valign="top"><td><b>Stack trace:</b></td><td><c:forEach var="trace" items="${pageContext.exception.stackTrace}"><p>${trace}</p></c:forEach></td></tr>
	</table>
</body>
</html>

运行结果如下:

使用 try…catch块
如果您想要将异常处理放在一个页面中,并且对不同的异常进行不同的处理,那么您就需要使用try…catch块了。接下来的这个例子显示了如何使用try…catch块,将这些代码放在main.jsp中:

<html>
<head>
   <title>Try...Catch Example</title>
</head>
<body>
<%
   try{
      int i = 1;
      i = i / 0;
      out.println("The answer is " + i);
   }
   catch (Exception e){
      out.println("An exception occurred: " + e.getMessage());
   }
%>
</body>
</html>

试着访问main.jsp,它将会产生如下结果:


posted @ 2023-08-17 16:59  weiwei0123  阅读(80)  评论(0编辑  收藏  举报