Java Web从入门到精通:全面探索与实战(二)

Java Web从入门到精通:全面探索与实战(一)-CSDN博客

目录

[四、Java Web 开发中的数据库操作:以 MySQL 为例](#四、Java Web 开发中的数据库操作:以 MySQL 为例)

[4.1 MySQL 数据库基础操作](#4.1 MySQL 数据库基础操作)

[4.2 JDBC 技术深度解析](#4.2 JDBC 技术深度解析)

[4.3 数据库连接池的应用​](#4.3 数据库连接池的应用)

[五、Java Web 中的会话技术:Cookie 与 Session​](#五、Java Web 中的会话技术:Cookie 与 Session)

[5.1 Cookie 详解​](#5.1 Cookie 详解)

[5.2 Session 详解​](#5.2 Session 详解)


四、Java Web 开发中的数据库操作:以 MySQL 为例

4.1 MySQL 数据库基础操作

MySQL 作为一种广泛使用的开源关系型数据库管理系统,在 Java Web 开发中扮演着举足轻重的数据存储与管理角色。理解并掌握 MySQL 的基础操作是进行 Java Web 数据库开发的基石。​
数据库在 MySQL 中是数据存储与组织的核心容器,宛如一个大型的仓库,用于存放各类数据。创建数据库时,使用CREATE DATABASE语句,语法为CREATE DATABASE [IF NOT EXISTS] database_name;。其中,IF NOT EXISTS为可选参数,用于避免在数据库已存在时抛出错误。例如,创建一个名为testdb的数据库,代码如下:

TypeScript 复制代码
CREATE DATABASE IF NOT EXISTS testdb;

若要切换当前操作的数据库,使用USE语句,如USE testdb;,这就像是进入仓库的特定区域进行操作。查看所有数据库,可执行SHOW DATABASES;,它会列出系统中所有的数据库,方便我们了解数据库的整体情况。而当某个数据库不再需要时,可使用DROP DATABASE语句删除,如DROP DATABASE testdb;,但此操作需谨慎,因为一旦执行,数据库及其所有数据将被永久删除。​
表是数据库中数据存储的具体结构,类似于仓库中的一个个货架,每个货架存放特定类型的数据。创建表时,需定义表名及各列的名称、数据类型和约束条件。例如,创建一个名为users的表,用于存储用户信息,包含id(用户 ID,整数类型,自动递增且为主键)、username(用户名,可变长度字符串,最大长度为 50)、email(邮箱,可变长度字符串,最大长度为 100)和password(密码,可变长度字符串,最大长度为 50),代码如下:

TypeScript 复制代码
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100),
    password VARCHAR(50)
);

查看数据库中的所有表,执行SHOW TABLES;,它会展示当前数据库中所有的表名。若要查看某个表的结构,使用DESCRIBE语句,如DESCRIBE users;,它会详细列出表中各列的信息,包括列名、数据类型、是否允许为空等,帮助我们了解表的设计。当表不再需要时,使用DROP TABLE语句删除,如DROP TABLE users;,同样,此操作会删除表及其所有数据,需谨慎使用。​
在数据库中插入数据是常见操作,向users表中插入一条用户数据,包含用户名john_doe、邮箱john@example.com和密码password123,代码如下:

TypeScript 复制代码
INSERT INTO users (username, email, password) VALUES 
('john_doe', 'john@example.com', 'password123');

若要插入多条数据,可在VALUES关键字后用逗号分隔多个值列表,如:

TypeScript 复制代码
INSERT INTO users (username, email, password) VALUES 
('jane_smith', 'jane@example.com', 'password456'),
('tom_wilson', 'tom@example.com', 'password789');

从数据库中查询数据是获取信息的关键操作。查询users表中所有用户的信息,使用SELECT语句,代码如下:

TypeScript 复制代码
SELECT * FROM users;

这里的*表示选择所有列。若只查询部分列,如只查询username和email列,代码为:

TypeScript 复制代码
SELECT username, email FROM users;

若要根据条件查询,如查询用户名为john_doe的用户信息,使用WHERE子句,代码如下:

TypeScript 复制代码
SELECT * FROM users WHERE username = 'john_doe';

还可以对查询结果进行排序,如按username升序排序,代码为:

TypeScript 复制代码
SELECT * FROM users ORDER BY username ASC;

ASC表示升序,DESC表示降序。​
更新数据库中的数据用于修改现有记录。将users表中用户名为john_doe的用户邮箱更新为new_john@example.com,代码如下:

TypeScript 复制代码
UPDATE users SET email = 'new_john@example.com' WHERE username = 'john_doe';

删除数据库中的数据用于移除不再需要的记录。删除users表中用户名为tom_wilson的用户记录,代码如下:

TypeScript 复制代码
DELETE FROM users WHERE username = 'tom_wilson';

4.2 JDBC 技术深度解析

JDBC(Java Database Connectivity)是 Java 语言中用于连接和操作数据库的重要 API,它为 Java 开发者提供了一种统一的方式来与各种不同类型的数据库进行交互,使得 Java 应用程序能够方便地访问和管理数据库中的数据,就像一座桥梁,连接着 Java 程序与数据库。​
JDBC 的核心接口和常用类构成了其强大功能的基础。DriverManager类是 JDBC 的管理层,负责管理数据库驱动程序的加载和建立数据库连接。它就像是一个交通枢纽管理员,协调着 Java 程序与不同数据库之间的连接。例如,在加载 MySQL 数据库驱动时,使用Class.forName("com.mysql.cj.jdbc.Driver");语句,告知DriverManager要使用的数据库驱动类。​
Connection接口代表与数据库的连接,它是与数据库交互的基础。通过DriverManager.getConnection(url, username, password)方法获取连接对象,其中url为数据库连接字符串,username和password分别为数据库的用户名和密码。例如:

TypeScript 复制代码
String url = "jdbc:mysql://localhost:3306/testdb";
String username = "root";
String password = "password";
Connection connection = DriverManager.getConnection(url, username, password);

Statement接口用于执行 SQL 语句,它可以直接执行静态 SQL 语句。通过Connection对象的createStatement()方法创建Statement对象,如Statement statement = connection.createStatement();。然后使用statement.executeQuery(sql)方法执行查询语句,返回ResultSet结果集;使用statement.executeUpdate(sql)方法执行插入、更新、删除等语句,返回受影响的行数。​
PreparedStatement接口继承自Statement接口,它主要用于执行预编译的 SQL 语句。预编译的 SQL 语句可以提高执行效率,并且能有效防止 SQL 注入攻击。通过Connection对象的prepareStatement(sql)方法创建PreparedStatement对象,其中sql为带有参数占位符(?)的 SQL 语句。例如,插入用户数据的预编译 SQL 语句为:

TypeScript 复制代码
String sql = "INSERT INTO users (username, email, password) VALUES (?,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, "new_user");
preparedStatement.setString(2, "new_user@example.com");
preparedStatement.setString(3, "new_password");
int rowsAffected = preparedStatement.executeUpdate();

ResultSet接口用于存储查询结果集,它提供了一系列方法来遍历和获取结果集中的数据。通过Statement或PreparedStatement执行查询语句后返回ResultSet对象,然后使用while (resultSet.next())循环遍历结果集,通过resultSet.getString("column_name")等方法获取指定列的值。例如:

TypeScript 复制代码
String sql = "SELECT * FROM users";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
    int id = resultSet.getInt("id");
    String username = resultSet.getString("username");
    String email = resultSet.getString("email");
    System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
}

接下来,我们通过一个完整的代码示例来展示如何使用 JDBC 连接 MySQL 数据库并执行查询操作:

TypeScript 复制代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class JdbcExample {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/testdb";
        String username = "root";
        String password = "password";

        try {
            // 加载驱动程序
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 获取连接
            Connection connection = DriverManager.getConnection(url, username, password);
            // 创建Statement对象
            Statement statement = connection.createStatement();
            // 执行查询语句
            String sql = "SELECT * FROM users";
            ResultSet resultSet = statement.executeQuery(sql);
            // 处理结果集
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String usernameFromDb = resultSet.getString("username");
                String email = resultSet.getString("email");
                System.out.println("ID: " + id + ", Username: " + usernameFromDb + ", Email: " + email);
            }
            // 关闭资源
            resultSet.close();
            statement.close();
            connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,首先加载 MySQL 驱动程序,然后通过DriverManager获取与数据库的连接。接着创建Statement对象,执行查询语句并获取结果集。最后,通过循环遍历结果集,输出查询到的用户信息。操作完成后,依次关闭ResultSet、Statement和Connection对象,以释放资源。​
在使用 JDBC 过程中,可能会遇到一些常见问题。例如,驱动程序未找到异常ClassNotFoundException,这通常是因为没有正确添加数据库驱动包或驱动类名写错。解决方法是确保数据库驱动包已正确添加到项目的类路径中,并检查驱动类名是否正确。​
还有连接数据库失败的问题,可能是由于连接字符串错误、用户名或密码错误、数据库服务器未启动等原因导致。此时需要仔细检查连接字符串、用户名和密码,确保数据库服务器处于运行状态。​
SQL 注入攻击也是一个需要关注的问题,如用户输入的数据被恶意拼接在 SQL 语句中,可能导致数据泄露或数据被篡改。使用PreparedStatement代替Statement可以有效防止 SQL 注入攻击,因为PreparedStatement会对参数进行预处理,避免了直接将用户输入的数据拼接到 SQL 语句中。​

4.3 数据库连接池的应用​

在 Java Web 开发中,频繁地创建和销毁数据库连接会带来显著的性能开销,因为建立数据库连接涉及网络通信、数据库认证等复杂操作,耗费时间和资源。数据库连接池技术应运而生,它通过预先创建一定数量的数据库连接,并将这些连接存储在连接池中,当应用程序需要与数据库进行交互时,可以直接从连接池中获取一个可用的连接,而不需要每次都重新建立连接。使用完成后,连接会被返回到连接池中,以便后续使用。这种方式大大减少了连接的建立和销毁的开销,提高了系统的性能和响应速度,就像一个连接的 "仓库",随时为应用程序提供可用的连接。​
常见的数据库连接池有 HikariCP、C3P0、DBCP 等。HikariCP 以其高性能和低延迟著称,具有快速的连接获取速度、低资源消耗和高并发性能等特点,适用于高并发、高性能需求的应用程序。C3P0 是一个老牌的 Java 数据库连接池,具有较高的稳定性和可靠性,支持自动回收连接、测试连接的有效性等功能,还提供了多种配置选项,用户可以根据具体需求进行灵活配置。DBCP 是 Apache Commons 项目的一部分,具有简单易用、配置灵活等特点,支持连接池的基本功能,如连接回收、连接测试等,同时还提供了一些高级功能,如连接的统计信息、自动重连等。​
接下来,我们以 C3P0 为例,展示如何配置和使用数据库连接池。首先,需要在项目中添加 C3P0 的依赖。如果使用 Maven 项目,在pom.xml文件中添加以下依赖:

TypeScript 复制代码
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.5</version>
</dependency>

然后,在src目录下创建c3p0-config.xml配置文件,进行连接池的配置,示例代码如下:

TypeScript 复制代码
<c3p0-config>
    <default-config>
        <!-- 数据库驱动名 -->
        <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
        <!-- 数据库的url -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/testdb</property>
        <!-- 用户名 -->
        <property name="user">root</property>
        <!-- 密码 -->
        <property name="password">password</property>
        <!-- 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default:3 -->
        <property name="acquireIncrement">5</property>
        <!-- 初始化数据库连接池时连接的数量 -->
        <property name="initialPoolSize">5</property>
        <!-- 数据库连接池中的最小的数据库连接数 -->
        <property name="minPoolSize">5</property>
        <!-- 数据库连接池中的最大的数据库连接数 -->
        <property name="maxPoolSize">10</property>
        <!-- 连接关闭时默认将所有未提交的操作回滚。Default: false -->
        <property name="autoCommitOnClose">false</property>
        <!-- 每60秒检查所有连接池中的空闲连接。Default:0 -->
        <property name="idleConnectionTestPeriod">60</property>
        <!-- 最大空闲时间,指定的时间内未使用则连接被丢弃。若为0则永不丢弃。Default:0 -->
        <property name="maxIdleTime">300</property>
    </default-config>
</c3p0-config>

在上述配置文件中,设置了数据库驱动类、连接 URL、用户名、密码等基本信息,还配置了连接池的一些属性,如初始连接数、最小连接数、最大连接数、获取连接失败后的重试策略等。​
接下来,通过代码获取连接池中的连接并执行数据库操作,示例代码如下:

TypeScript 复制代码
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class C3P0Example {
    public static void main(String[] args) {
        // 创建C3P0数据源
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        try {
            // 配置数据源属性(也可通过c3p0-config.xml配置)
            dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testdb");
            dataSource.setUser("root");
            dataSource.setPassword("password");
            // 配置连接池属性(也可通过c3p0-config.xml配置)
            dataSource.setMinPoolSize(5);
            dataSource.setMaxPoolSize(10);
            dataSource.setCheckoutTimeout(3000);

            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                // 从连接池获取连接
                connection = dataSource.getConnection();
                // 执行查询
                String sql = "SELECT * FROM users";
                preparedStatement = connection.prepareStatement(sql);
                resultSet = preparedStatement.executeQuery();
                // 处理查询结果
                while (resultSet.next()) {
                    int id = resultSet.getInt("id");
                    String username = resultSet.getString("username");
                    String email = resultSet.getString("email");
                    System.out.println("ID: " + id + ", Username: " + username + ", Email: " + email);
                }
            } catch (SQLException ex) {
                ex.printStackTrace();
            } finally {
                // 释放资源
                if (resultSet != null) {
                    try {
                        resultSet.close();
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
                if (preparedStatement != null) {
                    try {
                        preparedStatement.close();
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
                if (connection != null) {
                    try {
                        connection.close(); // 将连接放回连接池
                    } catch (SQLException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } catch (PropertyVetoException ex) {
            ex.printStackTrace();
        } finally {
            // 关闭数据源(通常在应用程序关闭时执行)
            dataSource.close();
        }
    }
}

五、Java Web 中的会话技术:Cookie 与 Session​

Cookie 是一种客户端会话管理技术,它就像是服务器发给客户端浏览器的一张小纸条,用于在客户端存储少量数据。当用户访问服务器时,服务器可以将一些信息以 Cookie 的形式发送给浏览器,浏览器会将这些 Cookie 存储在本地。当下次用户再次访问服务器时,浏览器会自动将这些 Cookie 发送给服务器,服务器可以根据这些 Cookie 来识别用户的身份或获取相关的用户信息。​
Cookie 的主要作用包括:​

  • 会话状态管理:例如用户登录信息的记录,当用户登录成功后,服务器可以将用户的登录状态以 Cookie 的形式发送给浏览器,下次用户访问时,服务器可以通过 Cookie 判断用户是否已经登录,从而决定是否需要用户再次登录。
  • 个性化设置:存储用户的个性化设置,如用户在网站上设置的语言偏好、主题风格等,服务器可以根据 Cookie 中的设置为用户提供个性化的服务。
  • 购物车功能:在电商网站中,Cookie 可以用于存储用户购物车中的商品信息,方便用户在不同页面之间切换时,购物车中的商品信息不会丢失。
    在 Java 中,操作 Cookie 主要使用javax.servlet.http.Cookie类。常用的属性和方法如下:​

  • 属性:

  • name:Cookie 的名称,用于标识 Cookie,名称必须唯一。
  • value:Cookie 的值,用于存储具体的数据。
  • maxAge:Cookie 的最大生存时间,以秒为单位。如果设置为正数,Cookie 会在指定的时间后过期;如果设置为负数,Cookie 会在浏览器关闭时过期(默认情况);如果设置为 0,则会立即删除该 Cookie。
  • path:Cookie 的路径,指定 Cookie 在哪些路径下有效。例如,如果设置为/app,则只有访问/app或其子路径(如/app/products)的请求才会携带该 Cookie;如果设置为/,则整个网站的所有路径下的请求都会携带该 Cookie。
  • domain:Cookie 的域名,指定 Cookie 在哪个域名下有效。默认情况下,Cookie 只在创建它的域名下有效;如果设置为一级域名(如.example.com),则该域名及其所有子域名(如www.example.comapi.example.com)下的请求都可以访问该 Cookie。
  • secure:是否仅通过 HTTPS 连接传输 Cookie。如果设置为true,则只有在使用 HTTPS 协议访问时,浏览器才会将该 Cookie 发送给服务器,以提高 Cookie 在传输过程中的安全性;如果设置为false(默认值),则 HTTP 和 HTTPS 连接都可以传输 Cookie。
  • httpOnly:是否只能通过 HTTP (S) 请求访问 Cookie。如果设置为true,则 JavaScript 代码无法读取或修改该 Cookie,从而增强了 Cookie 的安全性,防止 Cookie 被 JavaScript 脚本窃取或篡改;如果设置为false(默认值),则 JavaScript 代码可以访问 Cookie。
  • 常用方法:
  • Cookie(String name, String value):构造方法,用于创建一个 Cookie 对象,传入 Cookie 的名称和值。
  • void setMaxAge(int expiry):设置 Cookie 的最大生存时间,单位为秒。
  • String getName():获取 Cookie 的名称。
  • String getValue():获取 Cookie 的值。
  • void setValue(String value):设置 Cookie 的值。
  • void setPath(String path):设置 Cookie 的路径。
  • void setDomain(String domain):设置 Cookie 的域名。
  • void setSecure(boolean flag):设置是否仅通过 HTTPS 连接传输 Cookie。
  • void setHttpOnly(boolean flag):设置是否只能通过 HTTP (S) 请求访问 Cookie。
    下面通过代码示例来展示如何创建、发送和获取 Cookie:
TypeScript 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/cookieExample")
public class CookieExampleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 创建一个Cookie对象,名称为username,值为John
        Cookie cookie = new Cookie("username", "John");
        // 设置Cookie的最大生存时间为1小时(3600秒)
        cookie.setMaxAge(3600);
        // 设置Cookie的路径为根路径
        cookie.setPath("/");
        // 将Cookie添加到响应中,发送给客户端
        response.addCookie(cookie);

        response.getWriter().println("Cookie has been set.");
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

上述代码创建了一个名为username,值为John的 Cookie,并设置了它的最大生存时间为 1 小时,路径为根路径,然后将其添加到响应中发送给客户端。​
在另一个 Servlet 中获取 Cookie 的代码如下:

TypeScript 复制代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/getCookieExample")
public class GetCookieExampleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取客户端发送的所有Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                // 找到名为username的Cookie
                if ("username".equals(cookie.getName())) {
                    String username = cookie.getValue();
                    response.getWriter().println("Username from Cookie: " + username);
                    break;
                }
            }
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

这段代码从客户端请求中获取所有的 Cookie,并遍历查找名为username的 Cookie,如果找到则输出其值。​
需要注意的是,在设置 Cookie 的路径时,要根据实际需求进行设置。如果路径设置不当,可能会导致 Cookie 无法在预期的页面中被发送或接收。例如,如果一个 Cookie 的路径设置为/app,那么在访问根路径(/)下的页面时,该 Cookie 不会被发送;只有在访问/app或其子路径下的页面时,该 Cookie 才会被发送。此外,Cookie 的大小有限制,每个 Cookie 通常不能超过 4KB,并且浏览器对同一个域名下的 Cookie 数量也有限制,一般最多为 20 个左右,在使用 Cookie 时要考虑这些限制因素。​

5.2 Session 详解​

Session 是一种服务端会话管理技术,它为每个用户的浏览器创建一个独享的会话空间,用于在服务器端存储用户的会话数据。当用户访问服务器时,服务器会为其创建一个 Session 对象,并分配一个唯一的 Session ID。这个 Session ID 通常会通过 Cookie 发送给客户端浏览器,浏览器在后续的请求中会将这个 Session ID 发送回服务器,服务器根据这个 Session ID 来识别用户的会话,并获取该用户在 Session 中存储的数据。​
Session 的工作原理如下:​

  1. 用户首次访问服务器时,服务器会创建一个新的 Session 对象,并生成一个唯一的 Session ID。
  2. 服务器将 Session ID 通过 Cookie 发送给客户端浏览器,这个 Cookie 的名称通常为JSESSIONID。
  3. 客户端浏览器在后续的请求中,会将包含JSESSIONID的 Cookie 发送回服务器。
  4. 服务器接收到请求后,根据 Cookie 中的JSESSIONID找到对应的 Session 对象,从而获取该用户的会话数据。
    Session 的主要作用是在一次会话中,为用户提供一个可以在不同页面或请求之间共享数据的空间。例如,在一个电商网站中,用户在浏览商品时将商品添加到购物车,这些购物车中的商品信息就可以存储在 Session 中。当用户跳转到结算页面时,服务器可以从 Session 中获取购物车信息,展示给用户并进行结算操作。​
    在 Java 中,操作 Session 主要通过HttpSession接口。常用的方法如下:​
  • HttpSession getSession():获取当前请求的 Session 对象。如果当前请求没有 Session 对象,则会创建一个新的 Session 对象。
  • HttpSession getSession(boolean create):获取当前请求的 Session 对象。如果create参数为true,且当前请求没有 Session 对象,则会创建一个新的 Session 对象;如果create参数为false,且当前请求没有 Session 对象,则返回null。
  • void setAttribute(String name, Object value):在 Session 中存储一个属性,属性名为name,属性值为value。
  • Object getAttribute(String name):从 Session 中获取指定属性名的属性值。如果属性不存在,则返回null。
  • void removeAttribute(String name):从 Session 中移除指定属性名的属性。
  • String getId():获取 Session 的唯一标识符(Session ID)。
  • long getCreationTime():获取 Session 的创建时间,返回值为自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数。
  • long getLastAccessedTime():获取客户端最后一次访问该 Session 的时间,返回值为自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数。
  • void setMaxInactiveInterval(int interval):设置 Session 的最大非活动间隔时间,单位为秒。如果在这个时间内客户端没有访问该 Session,则 Session 会被销毁。
  • int getMaxInactiveInterval():获取 Session 的最大非活动间隔时间,单位为秒。
  • void invalidate():使当前 Session 失效,即销毁 Session 对象及其存储的所有属性。
    下面通过代码示例来展示如何创建、获取和销毁 Session,以及在 Session 中保存和获取数据:
TypeScript 复制代码
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 javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/sessionExample")
public class SessionExampleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取当前请求的Session对象,如果不存在则创建一个新的
        HttpSession session = request.getSession();

        // 在Session中保存一个属性,名称为username,值为John
        session.setAttribute("username", "John");

        // 获取Session的ID
        String sessionId = session.getId();
        response.getWriter().println("Session ID: " + sessionId);
        response.getWriter().println("Username saved in Session: " + session.getAttribute("username"));

        // 设置Session的最大非活动间隔时间为30分钟(1800秒)
        session.setMaxInactiveInterval(1800);
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

上述代码获取当前请求的 Session 对象,在 Session 中保存了一个名为username,值为John的属性,并输出了 Session ID 和保存的用户名。同时,设置了 Session 的最大非活动间隔时间为 30 分钟。​
在另一个 Servlet 中获取 Session 数据的代码如下:

TypeScript 复制代码
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 javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/getSessionExample")
public class GetSessionExampleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取当前请求的Session对象
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 从Session中获取名为username的属性值
            String username = (String) session.getAttribute("username");
            response.getWriter().println("Username from Session: " + username);
        } else {
            response.getWriter().println("Session does not exist.");
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

这段代码获取当前请求的 Session 对象(如果不存在则不创建),并从 Session 中获取名为username的属性值进行输出。​
销毁 Session 的代码如下:

TypeScript 复制代码
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 javax.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/destroySessionExample")
public class DestroySessionExampleServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 获取当前请求的Session对象
        HttpSession session = request.getSession(false);
        if (session != null) {
            // 使Session失效,即销毁Session
            session.invalidate();
            response.getWriter().println("Session has been destroyed.");
        } else {
            response.getWriter().println("Session does not exist.");
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

这段代码获取当前请求的 Session 对象(如果不存在则不创建),并调用invalidate方法使 Session 失效,即销毁 Session 及其存储的所有属性。​
Session 与 Cookie 有着密切的关系,Session 的实现依赖于 Cookie 来传递 Session ID。如果客户端禁用了 Cookie,那么 Session ID 就无法通过 Cookie 发送回服务器,服务器也就无法识别用户的会话。为了解决这个问题,可以采用 URL 重写的方式,将 Session ID 附加在 URL 后面进行传递。例如,原本的 URL 为http://example.com/page,经过 URL 重写后变为http://example.com/page;jsessionid=1234567890,这样服务器仍然可以根据 URL 中的 Session ID 来识别用户的会话。不过,这种方式存在一定的安全风险,因为 Session ID 暴露在 URL 中,可能会被恶意用户窃取和利用,所以在实际应用中,应尽量确保客户端启用 Cookie 来传递 Session ID,以提高系统的安全性。

相关推荐
HuiSoul2003 小时前
Spring MVC
java·后端·spring mvc
摇滚侠5 小时前
面试实战 问题二十四 Spring 框架中循环依赖问题的解决方法
java·后端·spring
三木水7 小时前
Spring-rabbit使用实战七
java·分布式·后端·spring·消息队列·java-rabbitmq·java-activemq
别来无恙1497 小时前
Spring Boot文件下载功能实现详解
java·spring boot·后端·数据导出
optimistic_chen7 小时前
【Java EE初阶 --- 网络原理】JVM
java·jvm·笔记·网络协议·java-ee
weixin_456904277 小时前
Java泛型与委托
java·spring boot·spring
悟能不能悟8 小时前
能刷java题的网站
java·开发语言
程序员陆通9 小时前
Java高并发场景下的缓存穿透问题定位与解决方案
java·开发语言·缓存
北执南念9 小时前
Java多线程基础总结
java
David爱编程10 小时前
JDK vs JRE:到底有什么本质区别?99% 的人都答不上来
java·后端