从 Java 到 Spring:用一个可运行项目梳理现代开发边界

2026-09-21 33 预计阅读时间: 1 分钟
来源: spring.io AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

围绕“Jev、Java 与 Spring”展开的讨论,核心价值不只是认识某个工具或人物,而是重新观察 Java 语言、JVM 生态与 Spring 框架之间如何协作。由于来源摘要没有提供具体对话内容,下面不推断节目中的观点,而是沿着标题涉及的 Java 和 Spring,给出一套可以直接实践的工程方法。

Java 负责表达,Spring 负责装配

在现代 Spring 应用中,两者的职责边界越来越清晰:

  • Java 提供类型系统、记录类、异常处理、并发模型和模块化能力。
  • Spring 管理对象生命周期、依赖注入、Web 请求、配置、数据访问与可观测性。
  • 构建工具负责解析依赖、运行测试并生成可部署产物。

这意味着应用代码不必处处依赖框架。业务规则可以写成普通 Java 类,只有 HTTP、数据库或消息系统等边界需要接入 Spring。这样的结构更容易测试,也能降低升级框架时的改造成本。

例如,一个价格计算器没有必要继承任何 Spring 类型:

package com.example.demo.pricing;

import java.math.BigDecimal;

public final class PriceCalculator {

    public BigDecimal applyDiscount(BigDecimal price, int percent) {
        if (price.signum() < 0) {
            throw new IllegalArgumentException("price must not be negative");
        }
        if (percent < 0 || percent > 100) {
            throw new IllegalArgumentException("percent must be between 0 and 100");
        }

        return price.multiply(BigDecimal.valueOf(100L - percent))
                .divide(BigDecimal.valueOf(100));
    }
}

Spring 只需要在系统边界调用它,而不是侵入它的每一行逻辑。

搭建一个最小可运行的 Spring Boot 服务

可以用 Java 21 和 Spring Boot 3.x 建立一个小型 HTTP 服务。运行前请确认 java -version 显示 JDK 21,并把下面文件放进对应目录。

项目结构如下:

demo/
├── pom.xml
└── src/main/java/com/example/demo/
    ├── DemoApplication.java
    └── GreetingController.java

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>

    <properties>
        <java.version>21</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

应用入口:

package com.example.demo;

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

@SpringBootApplication
public class DemoApplication {

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

HTTP 控制器使用 Java record 表达不可变响应:

package com.example.demo;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    @GetMapping("/greetings")
    public Greeting greet(
            @RequestParam(defaultValue = "developer") String name) {
        return new Greeting("Hello, " + name + "!", Runtime.version().feature());
    }

    public record Greeting(String message, int javaVersion) {}
}

启动并调用接口:

cd demo
mvn spring-boot:run

打开另一个终端:

curl 'http://localhost:8080/greetings?name=Spring'

预期会得到类似结果:

{"message":"Hello, Spring!","javaVersion":21}

这个例子刻意保持简单,但它展示了一个重要方向:使用 Java 自身的语言能力定义数据和规则,让 Spring 专注于 HTTP 映射、序列化与应用启动。

不要让“能启动”代替工程验证

Spring Boot 的自动配置提高了开发速度,也可能掩盖隐式行为。团队至少应该检查以下几项:

  1. JDK 与 Spring Boot 的兼容范围:升级 JDK 前查看当前 Spring Boot 版本的支持矩阵,不要只依据本地编译是否成功。
  2. 依赖由 BOM 统一管理:尽量不要给 Spring 管理的常用依赖单独指定版本,否则容易形成不兼容组合。
  3. 边界输入必须校验:HTTP 参数、JSON 请求体和配置值都来自系统外部,不能假设它们始终正确。
  4. 启动测试不能缺席:至少保留一个能够加载 Spring 上下文的测试,尽早发现 Bean 冲突和配置错误。
  5. 生产配置不得写死:数据库密码、令牌和证书应通过环境变量或密钥管理系统注入。

可以加入一个最小启动测试:

package com.example.demo;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

    @Test
    void contextLoads() {
    }
}

运行全部验证:

mvn clean verify

采用时应关注什么

Java 与 Spring 的组合并不要求所有代码都“Spring 化”。更稳妥的做法是:领域模型使用普通 Java,外部适配器使用 Spring,依赖版本交给 Spring Boot 管理,并通过测试保护升级路径。

准备把这种方式引入现有项目时,可以按下面的清单推进:

  • 明确生产环境的 JDK 基线,并在 CI 中使用同一主版本。
  • 盘点手工声明版本的依赖,确认是否可以交回 Spring Boot 管理。
  • 优先使用构造器注入,避免隐藏依赖关系。
  • 把核心规则从 Controller、Repository 和配置类中拆出来。
  • 每次升级先阅读迁移说明,再运行单元测试、上下文测试和接口测试。
  • 对启动时间、内存和吞吐量进行实际测量,不根据框架印象做结论。

框架负责减少重复劳动,Java 负责提供稳定的语言基础,而工程团队仍然要对边界、版本和运行结果负责。这三者的关系理顺之后,Spring 项目才能在快速开发之外保持可维护性。


相关推荐