Skip to content

Spring学习与分析 - 从Servlet到SpringBoot的演进

BX

Spring学习与分析

最近在上课,闲余时间搞一点小开发什么的,主要也是想深入以开发的角度学习一下spring,感觉还是有点帮助的

本文将从传统Servlet容器开始,逐步深入Spring框架的核心概念,帮助你理解从传统Java Web开发到现代SpringBoot开发的演进过程。

传统的Servlet容器方式

也就是在 Tomcat、Jetty、Resin 等 Servlet 容器里运行 Web 应用

在传统模式下,Web 应用不是直接运行的 JAR,而是需要打成一个 .war (Web Archive) 文件

Maven配置

<packaging>war</packaging>

目录结构

your-app.war
 └── WEB-INF/
      ├── web.xml
      ├── classes/   (编译后的字节码)
      └── lib/       (依赖的jar包)

部署流程

主要一个流程如下:

WAR 包 →

写 web.xml 注册 Servlet/Filter

放到容器的 webapps →

启动容器访问应用。

Tomcat映射逻辑

在Tomcat中映射逻辑如下,ROOT 对应的就是/

位置上下文路径(Context Path)访问方式示例
webapps/ROOT// (默认)http://localhost:8080/
webapps/test//testhttp://localhost:8080/test/
webapps/blog//bloghttp://localhost:8080/blog/

web.xml配置详解

配置 web.xml 部署描述符 | Google App Engine flexible environment docs | Google Cloud

很全面的一个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_3_0.xsd"
         version="3.0">

    <!-- 应用显示名 -->
    <display-name>My Traditional Java WebApp</display-name>

    <!-- 全局上下文参数(在 ServletContext 中可读) -->
    <context-param>
        <param-name>app.mode</param-name>
        <param-value>prod</param-value>
    </context-param>

    <!-- 监听器:常用于初始化资源/集成框架 -->
    <listener>
        <listener-class>com.example.listener.AppContextListener</listener-class>
    </listener>

    <!-- 编码过滤器(中间件) -->
    <filter>
        <filter-name>EncodingFilter</filter-name>
        <filter-class>com.example.filter.EncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>EncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <!-- 业务 Servlet -->
    <servlet>
        <servlet-name>HelloServlet</servlet-name>
        <servlet-class>com.example.servlet.HelloServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>greeting</param-name>
            <param-value>Hello</param-value>
        </init-param>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>HelloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <!-- 会话 & 安全配置 -->
    <session-config>
        <session-timeout>30</session-timeout>
        <cookie-config>
            <http-only>true</http-only>
            <secure>true</secure>
        </cookie-config>
        <tracking-mode>COOKIE</tracking-mode>
    </session-config>

    <!-- 错误页映射 -->
    <error-page>
        <error-code>404</error-code>
        <location>/error/404.jsp</location>
    </error-page>
    <error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/error/500.jsp</location>
    </error-page>

    <!-- 欢迎页列表 -->
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

注解时代的到来

Servlet 3.0 开始,可以用注解自动注册,无需再写 web.xml

@WebServlet 代替 <servlet><servlet-mapping>,一个简单的例子:

import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.*;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        resp.getWriter().println("Hello World!");
    }
}

同理:

过滤器机制

就是Java Web的中间件

过滤器 (javax.servlet.Filter) 是在请求到达 Servlet/JSP 之前和响应返回客户端之前执行的一段逻辑。它是典型的责任链模式:多个过滤器可以按顺序链式调用。

整个流程:

客户端发起请求 →
容器(Tomcat)检查 URL 映射到哪些 Filter
依次执行这些 Filter 的 doFilter() →
执行目标 Servlet/JSP
响应返回时,再倒序经过这些 Filter
返回给客户端。

常见应用场景:

  1. 统一编码处理
<filter>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>com.example.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 登录认证
<filter>
    <filter-name>AuthFilter</filter-name>
    <filter-class>com.example.filter.AuthFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>AuthFilter</filter-name>
    <url-pattern>/secure/*</url-pattern>
</filter-mapping>

Spring框架的演进

Spring 并不是完全取代 Servlet,而是在 Servlet 基础上演进出来的更高层框架

项目目录规范

在src目录下存放java代码,遵循标准的Maven/Gradle项目结构:

Spring MVC工作流程

Spring MVC 的核心是 DispatcherServlet,它作为前端控制器处理所有请求:

请求 → DispatcherServlet → Controller → Service → Mapper → DB → 返回

Spring MVC工作流程

常用注解详解

  1. @Controller - 标记类是一个控制器,返回页面视图
  2. @RestController - = @Controller + @ResponseBody,直接返回 JSON
  3. @RequestMapping - 定义请求路径和方法,类上 + 方法上结合使用
  4. @GetMapping / @PostMapping / @PutMapping / @DeleteMapping - 更具体的请求方式映射
  5. @RequestParam - 获取 URL 参数
  6. @PathVariable - 获取路径参数
  7. @RequestBody - 获取请求体 JSON → Java 对象

实际应用示例

LoginController.java

package com.bx33661.springlearn.controller;

import com.bx33661.springlearn.entity.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LoginController {
    @PostMapping("/login")
    public String login(User user) {
        System.out.println("name: " + user.getName());
        System.out.println("password: " + user.getPassword());
        return "success";
    }
}

User.java

package com.bx33661.springlearn.entity;

public class User {
    private String name;
    private String password;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

这里的关键在于:@PostMapping("/login") 方法的 login(User user) 能够直接接收一个 User 对象作为参数,这是因为 Spring Framework参数绑定(Parameter Binding)消息转换(Message Conversion) 机制在背后自动完成了 HTTP 请求数据到 Java 对象的转换。

POJO和JavaBean规范

POJO - Plain Old Java Object

POJO 是一种轻量级的 Java 对象,遵循以下约定:

主要示例就是上面那个User类

JavaBean规范

JavaBean 是一种特殊的 POJO,符合 JavaBean 规范的类

规范要求:

  1. 必须有一个无参构造函数(方便反射创建对象)
  2. 属性必须是 私有的(private)
  3. 提供对应的 getter/setter 方法 来访问属性
  4. 可以实现 Serializable 接口(方便序列化和传输)

具体示例:

import java.io.Serializable;

public class Student implements Serializable {
    // 为了避免不同编译器生成不同的序列化ID
    private static final long serialVersionUID = 1L;

    private String id;
    private String name;

    // 必须有无参构造函数
    public Student() {}

    // 有参构造也可以,方便初始化
    public Student(String id, String name) {
        this.id = id;
        this.name = name;
    }

    // Getter / Setter
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    // 重写toString,方便打印对象
    @Override
    public String toString() {
        return "Student{id='" + id + "', name='" + name + "'}";
    }
}

Resource目录学习

主要是存放一些资源,但是具体规范需要看是什么资源:

这个目录会被原样拷贝到编译输出目录:

target/classes/

效果就是:

src/main/resources/application.properties
→ target/classes/application.properties

所以我们可以这样去写:

InputStream in = getClass().getResourceAsStream("/application.properties");

Spring错误处理机制

Spring Boot 内部有一个 BasicErrorController,会拦截错误并返回默认的 JSON 或 HTML

典型的报错界面

Spring Boot错误页面

JSON格式错误信息

{
  "timestamp": "2025-09-29T13:22:33.134+00:00",
  "status": 404,
  "error": "Not Found",
  "message": "No message available",
  "path": "/test"
}

错误信息解析:

自定义错误页面

⚠️ 注意:Spring Boot 会自动根据 HTTP 状态码,去 templates/error/static/error/ 下找对应的页面

目录结构:

src/main/resources/
    └── templates/error/      # 或者 static/error/
        ├── 404.html
        ├── 500.html
        └── error.html

如果是使用Thymeleaf这种模板引擎的话,需要注意依赖和对应控制器设置。

同时可以自定义错误控制器,覆盖原生的错误处理:

@Controller
public class MyErrorController implements ErrorController {

    @RequestMapping("/error")
    @ResponseBody
    public Map<String, Object> handleError(HttpServletRequest request) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("msg", "出错啦,请联系管理员~");
        return map;
    }

    public String getErrorPath() {
        return "/error";
    }
}

SpringBoot的革命性改进

“约定优于配置”也称为Convention over Configuration,也叫 CoC 原则。

简单的理解:你不需要显式写一堆重复的配置,只要遵循框架规定的默认规则,框架就会帮你自动完成;只有当你需要自定义时,才去写额外配置。

对比传统开发: 传统方式需要写很多XML等配置文件,大部分时间都在调试和写配置文件。SpringBoot就是要解决这样的难题。

约定优于配置的例子:

SpringBoot启动示例

package org.example.start;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StartApplication {

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

启动日志分析

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.5.6)

2025-09-28T20:48:34.589+08:00  INFO 36440 --- [start] [           main] org.example.start.StartApplication       : Starting StartApplication using Java 17.0.16 with PID 36440
2025-09-28T20:48:34.591+08:00  INFO 36440 --- [start] [           main] org.example.start.StartApplication       : No active profile set, falling back to 1 default profile: "default"
2025-09-28T20:48:35.143+08:00  INFO 36440 --- [start] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2025-09-28T20:48:35.429+08:00  INFO 36440 --- [start] [           main] org.example.start.StartApplication       : Started StartApplication in 1.174 seconds (process running for 1.676)

从启动日志可以看出,我们只写了一个 main(),其他(Tomcat 启动、Servlet 注册、Bean 扫描)都由框架根据”约定”帮你完成。

Starter 机制

我们用IDEA时发现,快捷构建spring项目,有很多一站式的starter。

Spring Boot 官方和社区提供了各种 spring-boot-starter-*

Starter整合功能
spring-boot-starter-web内嵌 Tomcat + Spring MVC,快速开发 RESTful API
spring-boot-starter-data-jpaJPA + Hibernate,方便操作数据库
spring-boot-starter-data-redisRedis 整合(Lettuce 客户端)
spring-boot-starter-security安全框架 Spring Security
spring-boot-starter-thymeleaf模板引擎 Thymeleaf
spring-boot-starter-amqpRabbitMQ 消息队列
spring-boot-starter-testJUnit5 + Spring Test + MockMvc 等测试工具

拥有这些,我们只需要在 pom.xmlbuild.gradle 里加一个 starter,Spring Boot 会自动帮你把依赖和配置整合好。

Spring Boot 的灵魂在于:

例如:

配置文件 application.yml 里,你只要覆盖关键参数即可:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 123456

开发体验优化

热部署

Hot Deployment是指在不停止或重启应用程序的情况下,将更新后的代码、配置文件或其他资源直接应用到运行中的系统,使其生效

这里不细说具体哪种方案,就说平时测试的热部署。

需要用到spring-boot-devtools

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional> <!-- 防止被下游依赖到生产包 -->
</dependency>

在build标签中配置:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <configuration>
        <addResources>true</addResources>
      </configuration>
    </plugin>
  </plugins>
</build>

如果是在IDEA里面的话,需要把这个”自动构建项目”打开:

IDEA自动构建设置

具体效果就类似Python那些web框架,修改就生效,不过这个方案适用于测试或者小型项目。

模板引擎 - Thymeleaf

Thymeleaf 是现代的服务端Java模板引擎,能够处理HTML、XML、JavaScript、CSS甚至纯文本。

其他重要概念

Properties文件规范

主要记录一下这个文件格式规范

.properties 是一种最简单的配置文件格式,特点如下:

基本语法:

key=value

或者

key: value

(在 Spring Boot 里这两种都可以,推荐 =

注释:#! 开头

# 这是一个注释
! 这也是注释

空格处理: 键和值之间的空格会被忽略

server.port=8080

换行续写:

my.long.property=this is a \
  very long line

结果:this is a very long line

转义字符: 支持 \n(换行)、\t(制表)、\uXXXX(Unicode)等

greeting=Hello\nWorld
chinese=\u4F60\u597D

占位符引用(Spring Boot 扩展)

app.name=MyApp
app.description=${app.name} is a Spring Boot project

总结

通过本文的学习,我们了解了从传统Servlet容器到SpringBoot的演进过程:

  1. 传统方式:需要复杂的XML配置,手动管理Servlet、Filter等组件
  2. Spring框架:提供IoC和AOP等特性,简化了企业级开发
  3. SpringBoot:通过”约定优于配置”的理念,进一步简化了配置和部署

SpringBoot的设计哲学是让开发者能够快速构建生产级别的应用,同时保持Spring的强大功能和灵活性。理解这些核心概念对于现代Java Web开发至关重要。

编辑这篇文章

评论区

使用 GitHub Discussions 驱动,欢迎留言交流。

上一篇
基于TinyStories的LLM-attack & Hackergame-GCG攻击学习
下一篇
Dify&Coze工作流搭建