跳过正文
Spring Boot 4 技术栈纵览:Starter 粒度、MVC 版本协商与安全演进
  1. 文章/

Spring Boot 4 技术栈纵览:Starter 粒度、MVC 版本协商与安全演进

·4493 字·9 分钟
NeatGuyCoding
作者
NeatGuyCoding

Spring Boot 4 技术栈纵览:Starter 粒度、MVC 版本协商与安全演进
#

摘要:Spring Boot 4 与 Spring Framework 7 组合下,依赖可按能力拆成更细的 Starter,Web 出站客户端可与服务端 MVC 依赖分离;同一代码库可在 Spring MVC 中启用内置 API 版本解析,并与 Spring Data JDBC、RestClient / 声明式 @HttpExchange 客户端协同。Spring Security 7 侧重可叠加的 Customizer<HttpSecurity>、一次性令牌登录、WebAuthn 与注解式多因子模型;配合 JVM AOT 与 Spring Data JDBC 的编译期仓储片段,可将启动路径向「训练—缓存—回放」方向推进,但参数需与 JDK、模块开关严格对齐。下文按依赖层与安全边界展开,示例坐标以当前 Initializr 与参考手册为准;配图中所见的 Boot 4.1.0 (SNAPSHOT) 与稳定线 4.0.x 并存时,以所选 BOM 为准。

图中 Initializr 文案可见依赖说明:Spring Boot integration for RestClient and RestTemplate to make HTTP requests,以及 PostgreSQL Driver 段落的 A.JDBC and R2DBC driver that allows Java programs to connect to a PostgreSQL database

图中勾选列表含 GraalVM Native Support 说明文字 Support for compiling Spring applications to native executables using the GraalVM native-image,以及 Spring Modulith 条目 Support for building modular monolithic applications


1. Spring Boot 4:Starter 重组与出站 HTTP 依赖分离
#

(1) 原理与动机
Boot 4 将「按技术拆 Starter」作为常态:Classpath 上仅引入所需技术的 starter,可减少无关自动配置类的装载面。典型诉求是:构建基于 Servlet 的 Web 应用与「只做出站 HTTP 调用」可选用不同 starter,避免在无服务端场景拖入嵌入式 Web 容器相关配置。

(2) 实现抓手

  • Spring Initializr 依赖元数据 将依赖 ID 映射到具体 Maven 坐标;Initializr 界面仍可能出现 spring-boot-starter-web 等历史文案与 Boot 4 命名演进并存的现象,工程应以生成器输出的 spring-boot-starter-webmvcspring-boot-starter-restclient(依赖 ID 常为 spring-restclient)等为准。
  • Spring Boot 4.0 迁移指南 — Starters 说明 starter 集合更一致、多数技术有专用 starter 及配套 test starter。
  • Modulith:org.springframework.modulith:spring-modulith-starter-core 由 BOM 管理,可在同一单体中约束模块边界。

(3) 怎么用

<!-- 片段示意:仅出站客户端(坐标以 Initializr 生成为准) -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-restclient</artifactId>
</dependency>

spring-boot-starter-restclientRestClient.Builder bean 的装配细节见 Boot 参考手册「调用 REST 服务」一章。

Mermaid diagram 1


2. Spring Data JDBC 与只读 HTTP API(/dogs
#

(1) 原理与动机
Spring Data JDBC 以聚合根为中心映射表行,适合中小型域模型快速暴露 CRUD。Java record 在存在与列名匹配的非零参构造器时,可按 JDBC 映射文档 的构造器绑定规则实例化实体。

(2) 实现抓手

  • 仓储接口继承 ListCrudRepository
  • 控制器侧使用 Spring MVC 常规注解(@GetMapping 等)暴露 JSON。

(3) 怎么用

interface DogRepository extends ListCrudRepository<Dog, Integer> {}

@RestController
class DogsController {
  private final DogRepository dogs;
  DogsController(DogRepository dogs) { this.dogs = dogs; }
  @GetMapping("/dogs")
  Iterable<Dog> all() { return dogs.findAll(); }
}
curl -s http://localhost:8080/dogs

浏览器地址栏与 JSON 片段中出现 localhost:8080/dogs[{"id":45,"name":"Prancer","owner":"josh","description":"A demonic, neurotic, man hating, animal hating


3. Spring MVC:内置 API 版本协商
#

(1) 原理与动机
在同一部署内并行维护多版本处理器时,需要在请求解析阶段确定版本、拒绝非法版本并可对接弃用提示。Spring Framework 7 提供统一的 ApiVersionConfigurer 编程入口;Boot 则用 spring.mvc.apiversion.* 将常见策略映射为应用属性。

(2) 实现抓手

机制(文档用语)Boot 属性键(附录中出现)
请求头spring.mvc.apiversion.use.header
查询参数spring.mvc.apiversion.use.query-parameter

完整键名见 Boot Application Properties;策略总览见 MVC API 版本解析

(3) 怎么用

spring.mvc.apiversion.default=1.1
# 自定义头名仅为演示约定;生产环境请与网关及客户端统一命名
spring.mvc.apiversion.use.header=X-Dogs-Version

application.properties 编辑区可见 spring.mvc.apiversion.default=1.1 与关于 spring.mvc.apiversion.use.header 的 IDE 提示行 (Use the HTTP header th

curl -s http://localhost:8080/dogs
curl -s -H "X-Dogs-Version: 1.0" http://localhost:8080/dogs

默认版本与显式头在两套请求中的路由差异,可通过 @RequestMapping 等映射注解上的版本约束观察;属性值类型以 Boot 属性附录为准。


4. RestClient 与声明式 @HttpExchange 客户端
#

(1) 原理与动机
出站调用既可命令式构造 RestClient,也可用带 @HttpExchange / @GetExchange 的接口由 HttpServiceProxyFactory 生成代理,以便用类型契约约束远程调用。Framework 7 起 RestTemplate 已标记弃用,新项目优先 RestClient

(2) 实现抓手

(3) 怎么用
客户端接口(示例:第三方 HTTP API,URI 请替换为真实端点):

interface CatFactsClient {
  @GetExchange("https://example.catfacts/api")
  CatFacts facts();
}

服务端聚合(避免「只写请求不写处理侧」):

@RestController
class CatsController {
  private final CatFactsClient client;
  CatsController(CatFactsClient client) { this.client = client; }
  @GetMapping("/cats")
  CatFacts cats() { return client.facts(); }
}

源码编辑区可见 @Import 参数片段 (CatFactsClient.class) 以及 import org.springframework.web.service.annotation 等行。

同项目文件中可见 import org.springframework.web.service.registry 与注解 (CatFactsClient.class) 靠近 SpringApplication.run


5. BeanRegistrar 程序化注册 Bean 与 JSpecify @NullMarked
#

(1) 原理与动机
静态 @Bean 方法不足以表达「按环境或循环批量注册」时,BeanRegistrarregister(BeanRegistry, Environment) 中增量声明 Bean,典型搭配 @Import(MyRegistrar.class)。与此同时,org.springframework.beans.factory 包级 @NullMarked 将默认空安全语义收紧为「非空除非 @Nullable」,与 JSpecify @NullMarked 定义 一致。

(2) 实现抓手

  • BeanRegistrarBeanRegistry.registerBean(...)
  • JSpecify 入门站点以 jspecify.dev 为准(注解语义以源码/JavaDoc 为准)。

(3) 怎么用

@Configuration
@Import(DynamicBeans.class)
class AppConfig {}

class DynamicBeans implements BeanRegistrar {
  @Override
  public void register(BeanRegistry registry, Environment env) {
    for (int i = 0; i < 4; i++) {
      registry.registerBean(MyRunner.class);
    }
  }
}
record MyRunner() {}

调试窗口上方源码可见 registry.registerBean(MyRunner.class) 以及日志行 Tomcat started on port 8080 (http) with context path '/'

结构视图列出 BeanRegistrarBeanRegistry,下部源码窗口含 public @interface NullMarked 与句子 For a comprehensive introduction to JSpecify, please see jspecify.org


6. Spring Security 7:JDBC 用户存储与运行期边界
#

(1) 原理与动机
Servlet 栈下,JdbcUserDetailsManagerUserDetailsManager 语义上扩展 JdbcDaoImpl,便于以数据库行驱动认证主体。引入 Security 并不自动等价于某种固定用户模型;需显式提供 UserDetailsService / UserDetailsManagerSecurityFilterChain

(2) 实现抓手

(3) 怎么用

@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
  return http.authorizeHttpRequests(a -> a.anyRequest().authenticated())
      .httpBasic(Customizer.withDefaults())
      .build();
}
curl -s -o /dev/null -w "%{http_code}\n" -u user:pass http://localhost:8080/actuator/health

控制台日志含 Global AuthenticationManager configured with UserDetailsService bean with name jdbcUserDetailsManagerTomcat started on port 8080 (http)


7. 可叠加的 Customizer<HttpSecurity>:一次性令牌登录与 WebAuthn
#

(1) 原理与动机
Spring Security 7 允许以 Customizer<HttpSecurity> Bean 增量拼装 DSL,而不必整体替换默认过滤器链形态。一次性令牌登录(OTT)将魔法链接式登录纳入 oneTimeTokenLogin,默认交互路径包含 /login/ott。WebAuthn / Passkeys 则在 http.webAuthn 上配置 RP 名称、RP ID 与 allowedOrigins,以匹配浏览器 ceremony 的来源约束。

(2) 实现抓手

  • OTT:DSL、DefaultOneTimeTokenSubmitPageGeneratingFilter 相关描述见同一手册页。
  • Passkeys:依赖可选 spring-security-webauthn(Initializr 映射见 依赖 JSON)。

(3) 怎么用

@Bean
Customizer<HttpSecurity> httpSecurityCustomizer() {
  return http -> http
      .webAuthn(w -> w.rpName("spring").rpId("localhost")
          .allowedOrigins("http://localhost:8080"))
      .oneTimeTokenLogin(ott -> ott.tokenGenerationSuccessHandler((req, res, token) -> {
        res.setContentType("text/plain;charset=UTF-8");
        res.getWriter().println("please go to http://localhost:8080/login/ott?token="
            + token.getTokenValue());
      }));
}

源码窗口可见 return HttpSecurity http — http.oneTimeTokenLogin( OneTimeTokenLoginConfigurer< 与控制台输出 please go to http: //localhost:8080/login/ott?token=aa26d038-edf0-420f

同一配置区域可见链式片段 http.webAuthn(Customizer<HttpSecurity> httpSecurityCustomizer(),以及控制台重复的 please go to http: //localhost:8080/login/ott?token=aa26d038-edf0-420f


8. 多因子认证:@EnableMultiFactorAuthentication
#

(1) 原理与动机
注解模型将「密码因子 + 额外因子(如 OTT)」映射为框架识别的 FactorGrantedAuthority,缺因子时可导向已配置的 OTT 登录页。文档明确密码认证默认附带 FactorGrantedAuthority.PASSWORD_AUTHORITY;配图 OCR 中出现的噪声片段 ONETIME TOKEN_AUTHORITY 与官方常量命名并不一致,以手册与 FactorGrantedAuthority 为准

(2) 实现抓手

(3) 怎么用

@EnableMultiFactorAuthentication(authorities = { FactorGrantedAuthority.OTT_AUTHORITY })
@Configuration
class MfaConfiguration {}

浏览器端为多步交互;因子与 UserDetails 中 authority 的对应关系需在自有用户模型中落地。

IDE 中并列打开 class CatsControllerclass SecurityConfiguration,以及类型提示中的 Customizer <HttpSecurity>HttpSecurity 继承层次片段。


9. JVM AOT、Spring Data JDBC 仓储片段与 Leyden 语境
#

(1) 原理与动机
Boot 的 JVM AOT 处理 旨在用预先计算的初始化与缓存缩短启动路径;Spring Data JDBC 在 AOT 模式下可生成仓储实现片段(官方文档描述命名模板为 <Repository FQCN>Impl__Aot,并强调属内部优化)。OpenJDK Project Leyden 汇总多份 AOT 相关 JEP,与「训练运行—生成缓存—回放」同属一类优化范畴。示例运行日志中曾出现 AOT 缓存与模块属性不一致报错:需在 dump 与 runtime 阶段对齐 jdk.module.addmods 等 JVM 参数。

(2) 实现抓手

  • Spring Data JDBC — AOT Repositories(Asciidoc 源码)spring.aot.enabledspring.aot.repositories.enabledspring.aot.jdbc.repositories.enabled,以及「提供 JdbcDialect 以避免方言探测引发过早数据库访问」的建议。具体方言类型名应以所选数据库模块文档为准;若口头示例提到特定类名而公开手册未列出同名片段,应视为未在公开章节逐字核实
  • 生成类截图中的 DogRepositoryImpl__AotRepository 与文档模板在字面后缀上可能因版本演进不完全一致,以当前构建产物为准。

(3) 怎么用

public interface DogRepository extends ListCrudRepository<Dog, Integer> {
  Collection<Dog> findByName(String name);
}

构建与 JVM 参数须对照 Boot AOT 手册与 JDK 发行说明逐步校准。

生成源码中出现 public class DogRepositoryImpl__AotRepository extends AotRepositoryFragmentSupport 与方法 public Collection<Dog> findByName(String name)

终端输出含 Reading AOTConfiguration app.aot.config and writing AOTCache app.aot 以及 [error][aot] Mismatched values for property jdk.module.addmods

Mermaid diagram 2


10. 工程落地:脚手架解压与依赖树核对
#

(1) 原理与动机
示例工程从 Zip 解压后经 Maven Wrapper 构建;IDE 依赖树用于核对 Spring Framework 补丁版本、Jackson 主版本与 Modulith、Security 组件坐标,避免「本地可运行、CI 缺坐标」类问题。

(2) 实现抓手

  • 通用:unzip./mvnw -q -DskipTests package
  • BOM:spring-boot-dependencies POM 中的 jackson-bom.version 等属性可与依赖树交叉验证。

(3) 怎么用

unzip -q adoptions-springio.zip && cd adoptions-springio
./mvnw -q -DskipTests package

终端解压日志可见 creating: adoptions-springioinflating: adoptions-springio/pom.xml [binary]

Maven 依赖树列表中含 org.springframework:spring-core:7.0.6 与多行 org.springframework.modulith:spring-modulith-s


参考与延伸阅读
#

相关文章

用 Kotlin 表达力加固 Spring Boot 测试:断言、夹具与响应式边界

·4328 字·9 分钟
Spring Boot 与 Kotlin 在 JVM 上互操作成熟,团队常先在 src/test 引入 Kotlin,把扩展函数、默认参数、类型安全 DSL 与 Kotest 等断言风格用在集成测试与 MockMvc 场景中,以降低样板代码并收紧失败信息。与此同时,Java Builder、静态工具重载与 Project Reactor 的 StepVerifier 仍有各自的认知成本;文中按依赖层次归纳常见动机、可对齐的公开 API,以及需注意的语义边界(例如 JVM 泛型擦除、响应式校验是否真正订阅完成)。

遗留 Servlet 应用渐进接入 Spring Boot:构建、自动配置与 WAR 双模式

·5393 字·11 分钟
大规模迁移 Spring Boot 前,应先有可重复的集成验证与可控的依赖基线;随后按「Starter → 自动配置排障 → 外置容器内的 Spring 上下文 → 过渡期 Holder → Bean 化 → Servlet 注解化 → 可执行 WAR」分层推进。下文按依赖与运行时层次组织,并对照官方参考手册区分「演示型」引导路径与手册主推路径;个别行为(例如仅启动非 Web 上下文时的进程生命周期)若官方未逐句界定,则保留工程层面的不确定性说明。

Spring 工程上的 AI 编码代理:实时链路、可验证闭环与上下文治理

·6462 字·13 分钟
面向已在 JVM/Web 栈上交付服务的工程师,本文从一类典型 Spring Boot + Kotlin 实时互动应用出发,梳理「数据库信号 → 响应式 SSE → 浏览器」的数据路径,并把人机协作拆成可核对的三层:编译与测试闭合、可版本化的项目记忆(CLAUDE.md / 规则 / Skills)、工具调用路径上的 Hooks 与 MCP。后半部分讨论无规格迭代导致的测试与状态机缺口、结构化澄清(Interview)如何把导航与安全决策写进规格,以及长对话中跨切面步骤被静默丢弃的现象与分段执行思路。