概述

JavaWeb 用途

JavaWeb 技术广泛应用于构建各种类型的 Web 应用程序从简单的个人博客到复杂的企业级系统主要用于以下几个核心领域


企业级管理系统这是 JavaWeb 最传统也是最重要的应用领域包括 ERP企业资源计划CRM客户关系管理OA办公自动化等系统凭借其稳定性安全性和强大的事务处理能力Java 成为大型企业信息系统的首选技术
电子商务平台大型电商平台如淘宝京东的后台系统大量使用 JavaWeb 技术其高并发处理能力和分布式架构支持能够应对海量用户访问和交易处理
金融银行系统银行核心交易系统支付网关证券交易平台等对安全性和可靠性要求极高的系统几乎都采用 JavaWeb 技术构建
社交网络平台微博LinkedIn 等社交平台的后端服务需要处理海量用户数据和实时交互JavaWeb 提供了强大的技术支持
云计算与微服务现代云原生应用微服务架构中Spring Cloud 等 JavaWeb 框架成为主流选择支持服务的快速开发部署和扩展
API 服务与后端接口为移动端前端应用提供 RESTful API 接口JavaWeb 以其成熟的生态系统和优秀的性能表现成为后端服务开发的主流技术

JavaWeb 历史

🌱 起源Servlet 与 JSP 时代 (1997-2000)

JavaWeb 的发展始于 Sun 公司推出的一系列 Web 技术标准

  • Servlet 诞生1997 年Sun 发布了 Servlet 1.0 规范这是 Java 用于 Web 开发的第一个标准Servlet 运行在服务器端负责处理 HTTP 请求并生成响应
  • JSP 出现1999 年JSPJavaServer Pages1.0 发布JSP 允许在 HTML 中嵌入 Java 代码简化了动态网页的开发
  • 早期局限这一时期的 JavaWeb 开发较为繁琐需要手动配置大量的 XML 文件且 Servlet 和 JSP 的职责划分不够清晰

🚀 发展Struts 与 MVC 模式 (2000-2005)

随着 Web 应用复杂度的增加MVCModel-View-Controller设计模式被引入 JavaWeb 开发

  • Struts 框架2000 年Apache Struts 1.0 发布成为第一个广泛使用的 JavaWeb MVC 框架它将业务逻辑数据展示和控制流程分离提高了代码的可维护性
  • Hibernate ORM2001 年Hibernate 出现解决了 Java 对象与关系数据库之间的映射问题ORM简化了数据库操作
  • Spring 框架萌芽2002 年Rod Johnson 出版Expert One-on-One J2EE Design and Development提出了轻量级容器概念为 Spring 框架的诞生奠定基础

💎 成熟Spring 时代 (2005-2013)

Spring 框架的出现彻底改变了 JavaWeb 开发的格局

  • Spring 1.02004 年正式发布引入了依赖注入DI和面向切面编程AOP等核心概念极大地简化了企业级应用开发
  • Spring MVC2005 年Spring 2.0 引入了 Spring MVC 框架提供了比 Struts 更优雅更灵活的 Web 层解决方案
  • SSH 组合Struts + Spring + Hibernate 成为当时最流行的 JavaWeb 技术栈被称为”SSH 三大框架”
  • 注解驱动Spring 2.5 开始支持注解配置减少了 XML 配置的复杂度

🌟 革新Spring Boot 与微服务 (2014-至今)

随着云计算和微服务架构的兴起JavaWeb 进入了快速开发和自动化配置的时代

  • Spring Boot2014 年Spring Boot 1.0 发布提出了”约定优于配置”的理念实现了零配置启动和自动装配极大降低了 JavaWeb 项目的搭建门槛
  • 微服务架构Spring Cloud 基于 Spring Boot提供了一整套微服务解决方案包括服务发现配置中心负载均衡熔断器等
  • 响应式编程Spring 5.0 引入了 WebFlux支持响应式编程模型能够以非阻塞的方式处理高并发请求
  • 云原生时代JavaWeb 技术与 DockerKubernetes 等云原生技术深度融合成为构建现代化云应用的重要技术栈

JavaWeb 特点

⭐⭐ 核心优势


跨平台性与可移植性
JavaWeb 应用运行在 JVM 上继承了 Java “一次编写到处运行”的特性无论是 LinuxWindows 还是 macOS 服务器只要安装了合适的 JDK 和 Web 容器如 Tomcat应用就能正常运行这大大降低了部署和维护的成本


成熟的生态系统
JavaWeb 拥有极其丰富的开源框架和工具库从 Web 层的 Spring MVCStruts到业务层的 Spring Framework再到持久层的 HibernateMyBatis开发者可以根据需求选择合适的技术组合此外还有大量的第三方库支持日志安全缓存消息队列等功能


强大的企业级支持
JavaWeb 技术规范Java EE / Jakarta EE定义了一整套企业级应用标准包括事务管理安全性分布式计算消息服务等这些标准确保了不同厂商的实现具有兼容性为企业级应用提供了可靠的技术保障


⭐⭐ 稳健与安全


类型安全与编译检查 Java 是强类型语言编译器会在编译阶段捕获大部分类型错误减少了运行时异常的发生

  • 内存管理 JVM 的自动垃圾回收机制避免了内存泄漏和悬空指针等问题
  • 安全机制 Java 提供了完善的安全 API支持加密认证授权等功能Spring Security 等框架进一步简化了 Web 应用的安全实现
  • 异常处理 完善的异常处理机制使得程序能够在遇到错误时优雅降级而不是直接崩溃

高性能与可扩展性
虽然 Java 常被误解为性能较低但现代 JVM 通过 JIT即时编译技术已经能够达到接近原生代码的性能更重要的是JavaWeb 应用具有优秀的水平扩展能力

  • 多线程支持 Java 原生支持多线程能够充分利用多核 CPU 资源
  • 集群部署 JavaWeb 应用可以轻松部署在集群环境中通过负载均衡实现高可用和高并发
  • 异步处理 Servlet 3.0+ 和 Spring WebFlux 支持异步非阻塞处理显著提升了系统的吞吐量

⭐⭐ 生态与扩展


前后端分离支持
现代 JavaWeb 开发普遍采用前后端分离架构后端专注于提供 RESTful API 或 GraphQL 接口前端使用 VueReactAngular 等框架构建用户界面这种架构使得前后端可以独立开发测试和部署提高了开发效率


微服务与云原生
JavaWeb 技术与微服务架构天然契合Spring Cloud 提供了一站式微服务解决方案包括

  • 服务注册与发现EurekaNacos
  • 配置中心Config ServerNacos Config
  • 负载均衡RibbonLoadBalancer
  • 熔断器HystrixSentinel
  • 网关ZuulGateway

持续集成与 DevOps
JavaWeb 项目与 MavenGradle 等构建工具完美集成支持自动化构建测试和部署结合 JenkinsGitLab CI 等 CI/CD 工具可以实现高效的 DevOps 流程


JavaWeb 运行机制

JavaWeb 应用的运行机制基于客户端-服务器C/S架构通过 HTTP 协议进行通信其核心组件包括 Web 服务器Servlet 容器和应用服务器

⭐⭐ 核心组件Web 服务器Servlet 容器与应用服务器

Web 服务器 如 Apache HTTP ServerNginx主要负责处理静态资源HTMLCSSJS图片等的请求并可以将动态请求转发给后端的 Servlet 容器
Servlet 容器 如 TomcatJettyUndertow是 JavaWeb 应用的核心运行环境它负责加载管理和执行 Servlet处理 HTTP 请求和响应
应用服务器 如 WildFlyWebLogicWebSphere除了包含 Servlet 容器外还提供了完整的 Java EE 规范实现包括 EJBJMSJTA 等企业级功能

⭐⭐ 请求处理流程

这是 JavaWeb 应用运行的核心流程发生在用户访问网站时

  1. 客户端发起请求 用户在浏览器中输入 URL 或点击链接浏览器发送 HTTP 请求到服务器
  2. Web 服务器接收 Nginx 或 Apache 接收到请求如果是静态资源则直接返回如果是动态请求则转发给 Tomcat
  3. Servlet 容器处理 Tomcat 根据 URL 映射找到对应的 Servlet创建或复用 Servlet 实例
  4. 业务逻辑执行 Servlet 调用 Service 层处理业务Service 层通过 DAO 层访问数据库
  5. 生成响应 处理结果封装成 HTTP 响应JSONHTML 等返回给客户端
  6. 浏览器渲染 浏览器接收响应并渲染页面或处理数据

⭐⭐ 生命周期管理

Servlet 容器负责管理 Servlet 的生命周期

  • 加载与实例化 容器启动时或首次请求时加载 Servlet 类并创建实例
  • 初始化 调用 init() 方法进行初始化配置
  • 服务 每次请求到来时调用 service() 方法处理请求
  • 销毁 容器关闭时调用 destroy() 方法释放资源

Web 基础

HTTP 协议

HTTPHyperText Transfer Protocol是 Web 应用的基础协议定义了客户端和服务器之间通信的规则

HTTP 特点

特性 说明
无状态 服务器不保存客户端的状态信息每次请求都是独立的
无连接 HTTP/1.0 每次请求后断开连接HTTP/1.1+ 支持持久连接
简单快速 请求方法少GETPOST 等通信效率高
灵活 可以传输任意类型的数据通过 Content-Type 标识

HTTP 请求

HTTP 请求由请求行请求头和请求体三部分组成

⭐⭐ 请求行

1
GET /index.html HTTP/1.1
  • 请求方法GETPOSTPUTDELETEPATCH 等
  • 请求路径URL 的路径部分
  • HTTP 版本HTTP/1.1HTTP/2HTTP/3

⭐⭐ 请求头

1
2
3
4
5
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html,application/json
Content-Type: application/json
Authorization: Bearer token123

常见请求头

  • Host目标主机
  • User-Agent客户端信息
  • Accept可接受的内容类型
  • Content-Type请求体的数据类型
  • Authorization认证信息
  • Cookie携带的 Cookie 数据

⭐⭐ 请求体

POSTPUT 等方法可以携带请求体包含提交的数据

1
2
3
4
{
"username": "admin",
"password": "123456"
}

HTTP 响应

HTTP 响应由状态行响应头和响应体三部分组成

⭐⭐ 状态行

1
HTTP/1.1 200 OK
  • HTTP 版本
  • 状态码200404500 等
  • 状态描述OKNot FoundInternal Server Error 等

⭐⭐ 常见状态码

状态码 含义 说明
200 OK 请求成功
201 Created 资源创建成功
301 Moved Permanently 永久重定向
302 Found 临时重定向
304 Not Modified 资源未修改缓存命中
400 Bad Request 请求参数错误
401 Unauthorized 未授权需要认证
403 Forbidden 禁止访问
404 Not Found 资源不存在
500 Internal Server Error 服务器内部错误
502 Bad Gateway 网关错误
503 Service Unavailable 服务不可用

⭐⭐ 响应头

1
2
3
4
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Set-Cookie: sessionId=abc123; Path=/; HttpOnly
Cache-Control: max-age=3600

⭐⭐ 响应体

服务器返回的实际内容可以是 HTMLJSONXML图片等

1
2
3
4
5
6
7
8
{
"code": 200,
"message": "success",
"data": {
"id": 1,
"name": "张三"
}
}

HTTP 方法

方法 用途 是否幂等 是否有请求体
GET 获取资源
POST 创建资源
PUT 更新资源全量
PATCH 更新资源部分
DELETE 删除资源
HEAD 获取响应头
OPTIONS 查询支持的方法

由于 HTTP 是无状态协议为了保持用户的登录状态等信息需要使用 Session 和 Cookie 技术

Cookie 是服务器发送到浏览器并保存在本地的一小段数据浏览器会在后续请求中自动携带

  • 存储在客户端浏览器
  • 大小限制通常不超过 4KB
  • 数量限制每个域名最多 20-50 个 Cookie
  • 安全性较低可以被用户查看和修改
  • 可以设置过期时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 创建 Cookie
Cookie cookie = new Cookie("username", "admin");
cookie.setMaxAge(7 * 24 * 60 * 60); // 7天过期
cookie.setPath("/");
cookie.setHttpOnly(true); // 防止 JavaScript 访问
response.addCookie(cookie);

// 读取 Cookie
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("username".equals(c.getName())) {
String value = c.getValue();
}
}
}

// 删除 Cookie
Cookie cookie = new Cookie("username", null);
cookie.setMaxAge(0); // 立即过期
cookie.setPath("/");
response.addCookie(cookie);

Session

Session 是服务器端存储的用户会话数据通过 Session ID 与客户端关联

⭐⭐ Session 特点

  • 存储在服务器端
  • 大小无限制受服务器内存限制
  • 安全性较高数据不暴露在客户端
  • 依赖 Cookie 或 URL 重写传递 Session ID
  • 可以设置超时时间

⭐⭐ Session 操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取 Session不存在则创建
HttpSession session = request.getSession();

// 获取 Session不存在则返回 null
HttpSession session = request.getSession(false);

// 存储数据
session.setAttribute("user", userObject);
session.setAttribute("loginTime", new Date());

// 读取数据
User user = (User) session.getAttribute("user");
Date loginTime = (Date) session.getAttribute("loginTime");

// 删除数据
session.removeAttribute("user");

// 销毁 Session
session.invalidate();

// 设置超时时间
session.setMaxInactiveInterval(30 * 60); // 30分钟

⭐⭐ Session 工作原理

1
2
3
4
5
1. 客户端首次访问服务器
2. 服务器创建 Session生成唯一的 Session ID
3. 服务器将 Session ID 通过 Cookie 发送给客户端
4. 客户端后续请求自动携带 Session ID
5. 服务器根据 Session ID 找到对应的 Session 数据

Token 认证

现代 Web 应用更倾向于使用 Token如 JWT进行认证替代传统的 Session-Cookie 机制

⭐⭐ JWTJSON Web Token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 生成 Token
String token = Jwts.builder()
.setSubject(user.getId().toString())
.claim("username", user.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();

// 验证 Token
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();

String userId = claims.getSubject();
String username = claims.get("username", String.class);

Servlet 技术

Servlet 简介

Servlet 是 JavaWeb 的核心技术它是一个运行在服务器端的 Java 程序负责处理 HTTP 请求并生成响应

Servlet 生命周期

Servlet 的生命周期由 Servlet 容器管理包括三个阶段

⭐⭐ 初始化阶段

1
2
3
4
5
6
7
8
9
public class MyServlet extends HttpServlet {

@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
// 初始化代码只执行一次
System.out.println("Servlet 初始化");
}
}

⭐⭐ 服务阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 处理 GET 请求
response.setContentType("text/html;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<h1>Hello Servlet</h1>");
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 处理 POST 请求
request.setCharacterEncoding("utf-8");
String username = request.getParameter("username");
// 处理业务逻辑
}

⭐⭐ 销毁阶段

1
2
3
4
5
@Override
public void destroy() {
// 释放资源只执行一次
System.out.println("Servlet 销毁");
}

Servlet 配置

⭐⭐ web.xml 配置传统方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

⭐⭐ 注解配置推荐

1
2
3
4
5
6
7
8
9
10
11
@WebServlet(
name = "MyServlet",
urlPatterns = "/hello",
loadOnStartup = 1,
initParams = {
@WebInitParam(name = "encoding", value = "UTF-8")
}
)
public class MyServlet extends HttpServlet {
// ...
}

HttpServletRequest

HttpServletRequest 对象封装了客户端发送的 HTTP 请求信息

获取请求参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取单个参数
String username = request.getParameter("username");
String password = request.getParameter("password");

// 获取多个同名参数如复选框
String[] hobbies = request.getParameterValues("hobby");

// 获取所有参数名
Enumeration<String> paramNames = request.getParameterNames();
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();
String value = request.getParameter(name);
}

// 获取请求体JSON 等
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
String jsonBody = sb.toString();

获取请求头

1
2
3
4
5
6
7
8
9
10
// 获取单个请求头
String userAgent = request.getHeader("User-Agent");
String contentType = request.getHeader("Content-Type");

// 获取所有请求头
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
}

获取其他信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 请求方法
String method = request.getMethod(); // GET, POST, etc.

// 请求 URI
String uri = request.getRequestURI(); // /app/hello

// 请求 URL
StringBuffer url = request.getRequestURL(); // http://localhost:8080/app/hello

// 查询字符串
String queryString = request.getQueryString(); // name=admin&age=25

// 客户端 IP
String ip = request.getRemoteAddr();

// 上下文路径
String contextPath = request.getContextPath(); // /app

HttpServletResponse

HttpServletResponse 对象用于向客户端发送 HTTP 响应

设置响应头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 设置内容类型
response.setContentType("text/html;charset=utf-8");
response.setContentType("application/json;charset=utf-8");

// 设置响应头
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Access-Control-Allow-Origin", "*");

// 设置 Cookie
Cookie cookie = new Cookie("sessionId", "abc123");
response.addCookie(cookie);

// 重定向
response.sendRedirect("/login");

输出响应内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 输出文本
PrintWriter out = response.getWriter();
out.println("<h1>Hello World</h1>");

// 输出 JSON
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.println("{\"code\":200,\"message\":\"success\"}");

// 输出字节流文件下载
ServletOutputStream outputStream = response.getOutputStream();
FileInputStream fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
fis.close();
outputStream.flush();

状态码

1
2
3
4
// 设置状态码
response.setStatus(200);
response.setStatus(404);
response.sendError(404, "页面不存在");

JSP 技术

JSP 简介

JSPJavaServer Pages是一种动态网页技术允许在 HTML 中嵌入 Java 代码虽然现在前后端分离架构中 JSP 使用较少但理解 JSP 有助于深入理解 JavaWeb

JSP 执行原理

1
2
3
4
5
1. 客户端请求 JSP 页面
2. JSP 引擎将 JSP 翻译成 Servlet 源代码
3. 编译 Servlet 为 class 文件
4. 加载并执行 Servlet
5. 生成 HTML 响应返回给客户端

JSP 基本语法

⭐⭐ JSP 表达式

1
2
3
<!-- 输出变量值 -->
<p>用户名<%= username %></p>
<p>当前时间<%= new java.util.Date() %></p>

⭐⭐ JSP 脚本片段

1
2
3
4
5
6
7
8
9
<%
// Java 代码
String name = request.getParameter("name");
if (name != null) {
out.println("你好" + name);
} else {
out.println("请输入姓名");
}
%>

⭐⭐ JSP 声明

1
2
3
4
5
6
7
8
<%!
// 声明成员变量和方法
private int count = 0;

public int getCount() {
return ++count;
}
%>

⭐⭐ JSP 指令

1
2
3
4
5
6
7
8
<!-- page 指令 -->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>

<!-- include 指令 -->
<%@ include file="header.jsp" %>

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

JSP 内置对象

JSP 提供了 9 个内置对象无需声明即可直接使用

对象 类型 说明
request HttpServletRequest 请求对象
response HttpServletResponse 响应对象
session HttpSession 会话对象
application ServletContext 应用上下文
out JspWriter 输出流
pageContext PageContext 页面上下文
config ServletConfig 配置对象
page Object 当前页面对象this
exception Throwable 异常对象仅在错误页面可用

JSTL 与 EL 表达式

EL 表达式

ELExpression Language简化了 JSP 中的数据访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- 访问作用域中的属性 -->
${username}
${user.name}
${user.address.city}

<!-- 运算 -->
${1 + 2}
${a > b ? "大于" : "小于"}

<!-- 隐式对象 -->
${param.username} <!-- 请求参数 -->
${header.User-Agent} <!-- 请求头 -->
${cookie.sessionId.value} <!-- Cookie -->
${sessionScope.user} <!-- Session 属性 -->
${requestScope.message} <!-- Request 属性 -->

JSTL 标签库

JSTLJSP Standard Tag Library提供了常用的标签减少 JSP 中的 Java 代码

⭐⭐ 核心标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!-- 条件判断 -->
<c:if test="${user.age >= 18}">
<p>成年人</p>
</c:if>

<c:choose>
<c:when test="${score >= 90}">优秀</c:when>
<c:when test="${score >= 60}">及格</c:when>
<c:otherwise>不及格</c:otherwise>
</c:choose>

<!-- 循环遍历 -->
<c:forEach items="${userList}" var="user" varStatus="status">
<p>${status.index + 1}. ${user.name} - ${user.age}</p>
</c:forEach>

<!-- 设置变量 -->
<c:set var="count" value="0" scope="session" />

<!-- 移除变量 -->
<c:remove var="count" />

<!-- URL 处理 -->
<c:url value="/user/detail" var="url">
<c:param name="id" value="${user.id}" />
</c:url>
<a href="${url}">详情</a>

⭐⭐ 格式化标签

1
2
3
4
5
6
7
8
9
10
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<!-- 日期格式化 -->
<fmt:formatDate value="${user.createTime}" pattern="yyyy-MM-dd HH:mm:ss" />

<!-- 数字格式化 -->
<fmt:formatNumber value="${price}" pattern="#,##0.00" />

<!-- 国际化 -->
<fmt:message key="welcome.message" />

Filter 与 Listener

Filter过滤器

Filter 用于在请求到达 Servlet 之前或响应返回客户端之前进行预处理或后处理

Filter 生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@WebFilter("/*")
public class EncodingFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 初始化只执行一次
}

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");

// 放行继续执行下一个 Filter 或 Servlet
chain.doFilter(request, response);

// 后处理
}

@Override
public void destroy() {
// 销毁只执行一次
}
}

常见应用场景

  • 字符编码统一处理
  • 登录权限验证
  • XSS 攻击防护
  • 敏感词过滤
  • 请求日志记录
  • GZIP 压缩

Filter 链

多个 Filter 可以组成一个 Filter 链按照配置顺序依次执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.example.EncodingFilter</filter-class>
</filter>
<filter>
<filter-name>AuthFilter</filter-name>
<filter-class>com.example.AuthFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>AuthFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>

Listener监听器

Listener 用于监听 Web 应用中的各种事件如上下文创建Session 创建属性变化等

常见监听器

⭐⭐ ServletContextListener

监听应用启动和关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebListener
public class AppListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
// 应用启动时执行
System.out.println("应用启动");
}

@Override
public void contextDestroyed(ServletContextEvent sce) {
// 应用关闭时执行
System.out.println("应用关闭");
}
}

⭐⭐ HttpSessionListener

监听 Session 创建和销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebListener
public class SessionListener implements HttpSessionListener {

@Override
public void sessionCreated(HttpSessionEvent se) {
// Session 创建
System.out.println("Session 创建: " + se.getSession().getId());
}

@Override
public void sessionDestroyed(HttpSessionEvent se) {
// Session 销毁
System.out.println("Session 销毁: " + se.getSession().getId());
}
}

⭐⭐ ServletRequestListener

监听请求创建和销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebListener
public class RequestListener implements ServletRequestListener {

@Override
public void requestInitialized(ServletRequestEvent sre) {
// 请求开始
}

@Override
public void requestDestroyed(ServletRequestEvent sre) {
// 请求结束
}
}

Spring MVC

Spring MVC 简介

Spring MVC 是 Spring 框架提供的 Web 层框架基于 MVC 设计模式实现了请求处理的解耦

MVC 架构

层次 职责 示例
Model模型 业务逻辑和数据 ServiceDAOEntity
View视图 数据展示 JSPThymeleafJSON
Controller控制器 请求处理和路由 @Controller 类

工作流程

1
2
3
4
5
6
1. 客户端发送请求
2. DispatcherServlet 接收请求
3. HandlerMapping 找到对应的 Controller
4. Controller 处理业务返回 ModelAndView
5. ViewResolver 解析视图
6. 渲染视图并返回响应

Controller

基本用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Controller
@RequestMapping("/user")
public class UserController {

@Autowired
private UserService userService;

// 查询列表
@GetMapping("/list")
public String list(Model model) {
List<User> users = userService.findAll();
model.addAttribute("users", users);
return "user/list"; // 视图名称
}

// 查询详情
@GetMapping("/{id}")
public String detail(@PathVariable Integer id, Model model) {
User user = userService.findById(id);
model.addAttribute("user", user);
return "user/detail";
}

// 新增
@PostMapping("/add")
public String add(User user) {
userService.save(user);
return "redirect:/user/list"; // 重定向
}

// 修改
@PostMapping("/update")
public String update(User user) {
userService.update(user);
return "redirect:/user/list";
}

// 删除
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
userService.delete(id);
return "redirect:/user/list";
}
}

RESTful API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@RestController
@RequestMapping("/api/user")
public class UserApiController {

@Autowired
private UserService userService;

// 查询列表
@GetMapping
public ResponseEntity<List<User>> list() {
List<User> users = userService.findAll();
return ResponseEntity.ok(users);
}

// 查询详情
@GetMapping("/{id}")
public ResponseEntity<User> detail(@PathVariable Integer id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}

// 新增
@PostMapping
public ResponseEntity<User> add(@RequestBody User user) {
userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(user);
}

// 修改
@PutMapping("/{id}")
public ResponseEntity<Void> update(@PathVariable Integer id, @RequestBody User user) {
user.setId(id);
userService.update(user);
return ResponseEntity.noContent().build();
}

// 删除
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Integer id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}

参数绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@GetMapping("/search")
public String search(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String sort,
Model model
) {
// 参数自动绑定
List<User> users = userService.search(keyword, page, size, sort);
model.addAttribute("users", users);
return "user/list";
}

@PostMapping("/upload")
public String upload(
@RequestParam("file") MultipartFile file,
@RequestParam String description
) {
// 文件上传
if (!file.isEmpty()) {
// 处理文件
}
return "success";
}

数据验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@PostMapping("/add")
public String add(@Valid User user, BindingResult result, Model model) {
if (result.hasErrors()) {
// 验证失败返回表单页面
model.addAttribute("errors", result.getAllErrors());
return "user/form";
}
userService.save(user);
return "redirect:/user/list";
}

// User 实体类
public class User {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
private String username;

@Email(message = "邮箱格式不正确")
private String email;

@Min(value = 18, message = "年龄必须大于等于18")
@Max(value = 100, message = "年龄必须小于等于100")
private Integer age;

// Getter 和 Setter
}

拦截器

拦截器类似于 Filter但更加灵活可以针对特定的 Controller 或方法

自定义拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Component
public class AuthInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 请求处理前执行
HttpSession session = request.getSession();
User user = (User) session.getAttribute("user");

if (user == null) {
// 未登录跳转到登录页
response.sendRedirect("/login");
return false;
}

return true; // 放行
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
// 请求处理后执行
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// 视图渲染后执行
}
}

配置拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration
public class WebConfig implements WebMvcConfigurer {

@Autowired
private AuthInterceptor authInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/admin/**") // 拦截路径
.excludePathPatterns("/admin/login"); // 排除路径
}
}

Spring Boot

Spring Boot 简介

Spring Boot 基于”约定优于配置”的理念简化了 Spring 应用的初始搭建和开发过程

核心特性

  • 自动配置 根据 classpath 中的依赖自动配置 Spring 应用
  • 起步依赖 提供一组便捷的依赖描述符
  • 内嵌服务器 内置 TomcatJetty 或 Undertow无需部署 WAR 文件
  • 生产就绪 提供监控健康检查外部化配置等功能
  • 无代码生成 不需要 XML 配置或代码生成

快速开始

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
myproject/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/myproject/
│ │ │ ├── MyProjectApplication.java # 启动类
│ │ │ ├── controller/ # 控制器
│ │ │ ├── service/ # 业务层
│ │ │ ├── mapper/ # 数据访问层
│ │ │ ├── entity/ # 实体类
│ │ │ └── config/ # 配置类
│ │ └── resources/
│ │ ├── application.yml # 配置文件
│ │ ├── static/ # 静态资源
│ │ └── templates/ # 模板文件
│ └── test/
└── pom.xml

启动类

1
2
3
4
5
6
7
@SpringBootApplication
public class MyProjectApplication {

public static void main(String[] args) {
SpringApplication.run(MyProjectApplication.class, args);
}
}

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# application.yml
server:
port: 8080
servlet:
context-path: /api

spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver

jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true

常用注解

注解 作用 示例
@SpringBootApplication 标记启动类 主类上使用
@RestController RESTful 控制器 @RestController
@RequestMapping 请求映射 @RequestMapping("/user")
@GetMapping GET 请求映射 @GetMapping("/list")
@PostMapping POST 请求映射 @PostMapping("/add")
@PutMapping PUT 请求映射 @PutMapping("/{id}")
@DeleteMapping DELETE 请求映射 @DeleteMapping("/{id}")
@RequestBody 绑定请求体 @RequestBody User user
@PathVariable 绑定路径变量 @PathVariable Integer id
@RequestParam 绑定请求参数 @RequestParam String name
@Autowired 依赖注入 @Autowired UserService userService
@Service 服务层组件 @Service
@Repository 数据访问层组件 @Repository
@Configuration 配置类 @Configuration
@Bean 定义 Bean @Bean public DataSource dataSource()

最佳实践

项目结构规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
com.example.project/
├── common/ # 通用模块
│ ├── constant/ # 常量定义
│ ├── exception/ # 异常定义
│ ├── result/ # 统一返回结果
│ └── utils/ # 工具类
├── config/ # 配置类
├── controller/ # 控制层
├── service/ # 业务层
│ ├── impl/ # 业务实现
│ └── dto/ # 数据传输对象
├── mapper/ # 数据访问层
├── entity/ # 实体类
└── ProjectApplication.java # 启动类

统一返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;

public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}

public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
}

全局异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}

@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.error(500, "系统异常请联系管理员");
}
}

日志规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Slf4j
@Service
public class UserService {

public User findById(Integer id) {
log.info("查询用户ID: {}", id);
try {
User user = userMapper.findById(id);
log.debug("查询结果: {}", user);
return user;
} catch (Exception e) {
log.error("查询用户失败ID: {}", id, e);
throw new BusinessException("查询用户失败");
}
}
}

常见问题

中文乱码问题

1
2
3
4
5
6
7
8
9
问题请求或响应中的中文显示为乱码

解决方案
1. 配置过滤器统一编码
2. Spring Boot 配置
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
3. 数据库连接 URL 添加characterEncoding=utf8

跨域问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
问题前端访问后端接口时出现 CORS 错误

解决方案
1. 使用 @CrossOrigin 注解
@CrossOrigin(origins = "*")

2. 配置全局跨域
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}

报错处理

💗💗 404 Not Found

1
2
3
4
5
6
7
8
9
10
11
12
错误信息
Whitelabel Error Page - This application has no explicit mapping for /error

错误原因
1. 请求路径错误
2. Controller 未正确配置
3. 包扫描范围不对

解决方案
1. 检查 @RequestMapping 路径
2. 确认 Controller 类上有 @Controller 或 @RestController
3. 确保 Controller 在启动类的子包下

💗💗 500 Internal Server Error

1
2
3
4
5
6
7
8
9
10
11
12
错误信息
There was an unexpected error (type=Internal Server Error, status=500)

错误原因
1. 代码逻辑错误
2. 数据库连接失败
3. 依赖注入失败

解决方案
1. 查看控制台日志定位具体错误
2. 检查数据库配置和连接
3. 确认 @Autowired 注入的 Bean 存在

💗💗 Bean 注入失败

1
2
3
4
5
6
7
8
9
10
11
12
错误信息
Field xxx required a bean of type 'xxx' that could not be found

错误原因
1. 缺少 @Component@Service 等注解
2. 包扫描范围不包含该类
3. 循环依赖

解决方案
1. 添加相应的组件注解
2. 调整 @ComponentScan 范围
3. 使用 @Lazy 解决循环依赖

学习资源

  • 视频
    • 尚硅谷 JavaWeb 教程https://www.bilibili.com/video/BV1UN411x7xe
  • 官方文档
    • Spring 官方文档https://spring.io/projects/spring-framework
    • Spring Boot 官方文档https://spring.io/projects/spring-boot
    • Jakarta EE 规范https://jakarta.ee/specifications/
  • 书籍
    • 深入理解 Java Web杨开振著
    • Spring Boot 实战Craig Walls 著
    • Spring 揭秘王福强著
  • 教程
    • Spring 官方指南https://spring.io/guides
    • Baeldung Spring 教程https://www.baeldung.com/category/spring/
  • 工具
    • PostmanAPI 测试工具
    • Swagger/OpenAPIAPI 文档生成
    • JMeter压力测试工具
  • 社区
    • Stack Overflowhttps://stackoverflow.com/questions/tagged/spring
    • Spring 中文社区https://spring.io/projects