当前位置: 首页 > article >正文

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

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

 

目录

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

4.1 MySQL 数据库基础操作

4.2 JDBC 技术深度解析

4.3 数据库连接池的应用​

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

5.1 Cookie 详解​

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的数据库,代码如下:

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),代码如下:

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,代码如下:

INSERT INTO users (username, email, password) VALUES 
('john_doe', 'john@example.com', 'password123');

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

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

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

SELECT * FROM users;

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

SELECT username, email FROM users;

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

SELECT * FROM users WHERE username = 'john_doe';

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

SELECT * FROM users ORDER BY username ASC;

ASC表示升序,DESC表示降序。​

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

UPDATE users SET email = 'new_john@example.com' WHERE username = 'john_doe';

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

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分别为数据库的用户名和密码。例如:

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 语句为:

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")等方法获取指定列的值。例如:

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 数据库并执行查询操作:

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文件中添加以下依赖:

<dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.5</version>
</dependency>

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

<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、用户名、密码等基本信息,还配置了连接池的一些属性,如初始连接数、最小连接数、最大连接数、获取连接失败后的重试策略等。​

接下来,通过代码获取连接池中的连接并执行数据库操作,示例代码如下:

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​

5.1 Cookie 详解​

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.com、api.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:

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,值为JohnCookie 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 的代码如下:

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 {// 获取客户端发送的所有CookieCookie[] cookies = request.getCookies();if (cookies != null) {for (Cookie cookie : cookies) {// 找到名为username的Cookieif ("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 中保存和获取数据:

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,值为Johnsession.setAttribute("username", "John");// 获取Session的IDString 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 数据的代码如下:

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 的代码如下:

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失效,即销毁Sessionsession.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,以提高系统的安全性。

相关文章:

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

Java Web从入门到精通&#xff1a;全面探索与实战&#xff08;一&#xff09;-CSDN博客 目录 四、Java Web 开发中的数据库操作&#xff1a;以 MySQL 为例 4.1 MySQL 数据库基础操作 4.2 JDBC 技术深度解析 4.3 数据库连接池的应用​ 五、Java Web 中的会话技术&#xff…...

基于大模型的阵发性室上性心动过速风险预测与治疗方案研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与目标 1.3 研究方法与数据来源 二、阵发性室上性心动过速概述 2.1 定义与分类 2.2 发病机制与流行病学 2.3 临床表现与诊断方法 三、大模型在阵发性室上性心动过速预测中的应用 3.1 大模型技术原理与特点 3.2 模型构…...

秒杀业务的实现过程

一.后台创建秒杀的活动场次信息&#xff0c;并关联到要秒杀的商品或服务&#xff1b; 二.通过定时任务&#xff0c;将秒杀的活动信息和商品服务信息存储到redis; 三.在商品展示页的显眼位置加载秒杀活动信息&#xff1b; 四.用户参与秒杀&#xff0c;创建订单&#xff0c;将…...

spring mvc @ResponseBody 注解转换为 JSON 的原理与实现详解

ResponseBody 注解转换为 JSON 的原理与实现详解 1. 核心作用 ResponseBody 是 Spring MVC 的一个注解&#xff0c;用于将方法返回的对象直接序列化为 HTTP 响应体&#xff08;如 JSON 或 XML&#xff09;&#xff0c;而不是通过视图解析器渲染为视图&#xff08;如 HTML&…...

TDengine.C/C++ 连接器

简介 C/C 开发人员可以使用 TDengine 的客户端驱动&#xff0c;即 C/C 连接器&#xff08;以下都用 TDengine 客户端驱动表示&#xff09;&#xff0c;开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。…...

[docker] 简单操作场景

Docker的简单操作场景 1 安装 暂时没空写&#xff5e; 2 登陆 一共4步&#xff1a; ~$ sudo docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d765d4c1eb5f ubuntu:24.04 "/bin/bash" …...

skynet.rawcall使用详解及应用场景

目录 核心特性函数原型使用场景场景 1&#xff1a;高性能二进制传输&#xff08;如文件转发&#xff09;场景 2&#xff1a;自定义序列化协议&#xff08;如 Protocol Buffers&#xff09;场景 3&#xff1a;跨服务共享内存&#xff08;避免拷贝&#xff09; 配套接收方实现与 …...

使用SpringSecurity下,发生重定向异常

使用SpringSecurity下&#xff0c;发生空转异常 环境信息&#xff1a; Spring Boot 3.4.4 &#xff0c; jdk 17 &#xff0c; springSecurity 6.4.4 问题背景&#xff1a; 没有自定义controller &#xff0c;改写了login 页面&#xff0c;并且进行了成功后的跳转处理&#xf…...

gbase8s之逻辑导出导入脚本(完美版本)

该脚本dbexport.sh用于快速导出库和导入库&#xff08;使用多并发unload&#xff0c;和多并发dbload的方式&#xff09; #!/bin/sh #脚本功能&#xff1a;将数据导出成文本&#xff0c;迁移至其他实例 #最后更新时间&#xff1a;2023-12-19 #使用方法&#xff1a; #1.执行该脚…...

Elasticsearch | ES索引模板、索引和索引别名的创建与管理

关注&#xff1a;CodingTechWork 引言 在使用 Elasticsearch (ES) 和 Kibana 构建数据存储和分析系统时&#xff0c;索引模板、索引和索引别名的管理是关键步骤。本文将详细介绍如何通过 RESTful API 和 Kibana Dev Tools 创建索引模板、索引以及索引别名&#xff0c;并提供具…...

【Easylive】视频删除方法详解:重点分析异步线程池使用

【Easylive】项目常见问题解答&#xff08;自用&持续更新中…&#xff09; 汇总版 方法整体功能 这个deleteVideo方法是一个综合性的视频删除操作&#xff0c;主要完成以下功能&#xff1a; 权限验证&#xff1a;检查视频是否存在及用户是否有权限删除核心数据删除&…...

力扣hot100_回溯(2)_python版本

一、39. 组合总和&#xff08;中等&#xff09; 代码&#xff1a; class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:ans []path []def dfs(i: int, left: int) -> None:if left 0:# 找到一个合法组合ans.append(pa…...

SGLang实战:从KV缓存复用到底层优化,解锁大模型高效推理的全栈方案

在当今快速发展的人工智能领域&#xff0c;大型语言模型(LLM)的应用已从简单对话扩展到需要复杂逻辑控制、多轮交互和结构化输出的高级任务。面对这一趋势&#xff0c;如何高效地微调并部署这些大模型成为开发者面临的核心挑战。本文将深入探讨SGLang——这一专为大模型设计的高…...

LPDDR4内存颗粒命名规则全解析:三星、镁光、海力士、南亚、长鑫等厂商型号解码与选型指南

由于之前DDR的系列选型文章有很好的反馈&#xff0c;所以补充LPDDR4低功耗内存的选型和命名规则&#xff0c;总结了目前市面上常用的内存&#xff0c;供硬件工程师及数码爱好者参考。 在智能手机、平板电脑和低功耗设备中&#xff0c;LPDDR4 SDRAM凭借其高带宽、低功耗特性成为…...

特权FPGA之Johnson移位

完整代码&#xff1a; module johnson(clk,rst_n,led,sw1_n,sw2_n,sw3_n);input clk; //时钟信号&#xff0c;50MHz input rst_n; //复位信号&#xff0c;低电平有效 output[3:0] led; //LED控制&#xff0c;1--灭&#xf…...

网络安全小知识课堂(最终完结版)

网络安全入门 &#xff1a;从 “小白” 到 “守护者” 的蜕变之旅 写在完结之际 历经 13 篇的深度探索&#xff0c;我们从 DDoS 攻击的 “流量洪水” 一路闯关到 HTTPS 的 “加密堡垒”&#xff0c;揭开了网络安全世界的层层面纱。感谢每一位读者的陪伴与互动&#xff0c;你们…...

2025年AI生成引擎搜索发展现状与趋势总结​​

2025年AI生成引擎搜索发展现状与趋势总结 ​​一、国内外AI生成引擎搜索发展现状​​ ​​1. 国内动态​​ ​​社交搜索崛起​​&#xff1a;小红书2024年Q4日均搜索量达6亿次&#xff0c;用户更依赖社交平台UGC内容进行决策&#xff08;如购物、旅游场景&#xff09;&#…...

【杂谈】Godot4.4导出到Android平台(正式导出)

学博而后可约&#xff0c;事历而后知要。 目录 一、准备二、Gradle构建三、配置Java SDK四、配置Android SDK五、配置密钥 一、准备 本文在前文【杂谈】Godot4.4导出到安卓平台&#xff08;调试导出&#xff09;的基础上&#xff0c;进行正式导出。调试导出并不是真正的编译导…...

VBA将Word文档内容逐行写入Excel

如果你需要将Word文档的内容导入Excel工作表来进行数据加工&#xff0c;使用下面的代码可以实现&#xff1a; Sub ImportWordToExcel()Dim wordApp As Word.ApplicationDim wordDoc As Word.DocumentDim excelSheet As WorksheetDim filePath As VariantDim i As LongDim para…...

基于AI设计开发出来的业务系统是什么样的?没有菜单?没有表格?

基于AI设计开发出的业务系统仍然会包含菜单、表格等传统UI元素&#xff0c;但AI技术会显著改变它们的实现方式和交互逻辑。以下是具体分析&#xff1a; 一、传统元素的持续存在 功能刚需性 • 菜单承担着系统导航的核心功能&#xff0c;表格则是结构化数据展示的基础载体。根…...

C++ -异常之除以 0 问题(整数除以 0 编译时检测、整数除以 0 运行时检测、浮点数除以 0 编译时检测、浮点数除以 0 运行时检测)

一、整数除以 0&#xff08;编译时检测&#xff09; 1、演示 #include <iostream>using namespace std;int main() {int result 10 / 0;cout << result << endl;return 0; }程序无法运行&#xff0c;输出结果 error C2124: 被零除或对零求模2、演示解读 …...

数字足迹管理(DFM):你的网络隐身指南

数字足迹管理&#xff08;DFM&#xff09;&#xff1a;你的网络隐身指南 ‌你可能不知道&#xff0c;你的姓名、电话、住址正在网上被“明码标价”‌ ——而这一切&#xff0c;可能只是因为你点过外卖、寄过快递&#xff0c;甚至注册过一个网站。 一、什么是数字足迹管理&#…...

如何避免“过度承诺”导致的验收失败

如何避免“过度承诺”导致的验收失败&#xff1f;关键在于&#xff1a; 评估可行性、设置合理目标、高频沟通反馈、阶段性验收、做好风险管理。其中设置合理目标至关重要&#xff0c;很多团队往往在项目初期为迎合客户或领导而报出“最理想方案”&#xff0c;忽略了资源、技术及…...

MySQL学习笔记集--游标

游标 在MySQL中&#xff0c;游标&#xff08;Cursor&#xff09;是一种数据库对象&#xff0c;它允许您逐行处理查询结果集。游标通常与存储过程一起使用&#xff0c;因为它们需要在存储过程或函数中声明和操作。游标的使用涉及几个步骤&#xff1a;声明游标、打开游标、从游标…...

紧跟数字人热潮:123 数字人分身克隆系统源码部署与风口洞察

在当今数字化浪潮中&#xff0c;数字人技术无疑已成为最具活力与潜力的领域之一&#xff0c;正以迅猛之势席卷多个行业&#xff0c;重塑着人们的交互方式与商业运作模式。C 站作为技术交流的前沿阵地&#xff0c;汇聚了众多关注前沿科技的开发者与技术爱好者&#xff0c;今天来…...

QT控件 修改QtTreePropertyBrowser自定义属性编辑器源码,添加第一列标题勾选,按钮,右键菜单事件等功能

头阵子遇到一个需要修改QtTreePropertyBrowser控件的需求&#xff0c;QT开发做这么久了&#xff0c;这个控件倒是第一次用&#xff0c;费了点时间研究&#xff0c;在这里做个简单的总结。 QtTreePropertyBrowser控件 是 Qt 解决方案 (Qt Solutions) 中的一个组件&#xff0c;用…...

Excel 日期值转换问题解析

目录 问题原因 解决方案 方法1&#xff1a;使用 DateTime.FromOADate 转换 方法2&#xff1a;处理可能为字符串的情况 方法3&#xff1a;使用 ExcelDataReader 时的处理 额外提示 当你在 Excel 单元格中看到 2024/12/1&#xff0c;但 C# 读取到 45627 时&#xff0c;这是…...

0. 七小时挑战:自研企业级任务调度器--前言

在软件开发的世界里&#xff0c;有一个亘古不变的问题&#xff1a;“为什么不直接用现成的&#xff1f;”这句话听起来合理、理性、务实&#xff0c;甚至有点老道。毕竟&#xff0c;时间宝贵、预算有限&#xff0c;轮子已经造好了&#xff0c;何必再动手&#xff1f; 但有时候…...

Spring 核心注解深度解析:@Autowired、@Repository 与它们的协作关系

引言 在 Spring 框架中&#xff0c;​依赖注入&#xff08;DI&#xff09;​​ 是实现松耦合架构的核心机制。Autowired 和 Repository 作为两个高频使用的注解&#xff0c;分别承担着 ​依赖装配​ 和 ​数据访问层标识​ 的关键职责。本文将深入探讨它们的功能特性、协作模式…...

开源模型应用落地-模型上下文协议(MCP)-从数据孤岛到万物互联(一)

一、前言 当开发者还在为每个AI工具编写臃肿的API适配器时&#xff0c;一场关于「连接」的技术革命已悄然降临。模型上下文协议&#xff08;MCP&#xff09;正在用一套全新的交互语法&#xff0c;重新定义人工智能与物理世界的对话方式。MCP协议如同为AI系统装上了“万能接口”…...