多模块 Starter 最佳实践(强烈推荐!!!)
目录
一、背景
二、目录和示意图
1、多模块 Maven Starter 的工程结构示意图
2、Starter 使用示意图
3、可视化的多模块 Starter 依赖与 Bean 注入流程图
三、相关代码
1、父工程 my-feature-parent/pom.xml
2、核心模块 my-feature-core
2.1 pom.xml
2.2 核心类 RequestLogger.java
3、自动配置模块 my-feature-spring-boot-autoconfigure
3.1 pom.xml
3.2 RequestLoggerProperties.java
3.3 RequestLoggerAutoConfiguration.java
3.4 自动配置声明 AutoConfiguration.imports
3.5 IDE 配置提示 additional-spring-configuration-metadata.json
4、Starter 聚合模块 my-feature-spring-boot-starter
4.1 pom.xml
5、Demo 测试项目 demo-spring-boot-app
5.1 pom.xml
5.2 application.yml
5.3 Controller 示例 TestController.java
6、使用方法
6.1 在父工程目录执行
6.2 启动 demo-spring-boot-app
6.3 测试接口
6.4 控制台输出
四、总结
一、背景
前面的文章我介绍了单模块的 Spring Boot 项目 设计一个完整可用的 Spring Boot Starter来实现 Starter。本文主要介绍下多模块 Maven 工程来实现Starter。
其实你当然可以写一个 单模块的 Spring Boot 项目 来实现 Starter,
但为什么大多数公司 / 社区 Starter(包括 Spring 官方的 starter)都推荐 多模块 Maven 工程 呢?我来拆一下原因:
特性 / 维度 | 单模块 Starter | 多模块 Maven Starter(推荐) |
---|---|---|
工程结构 | 一个 Spring Boot 项目,逻辑 + 配置混在一起 | 多模块:core(逻辑) + autoconfigure(自动配置) + starter(聚合依赖) |
逻辑复用性 | 核心逻辑与 Spring Boot 强绑定,不容易在非 Spring Boot 项目使用 | core 独立,既可以在 Spring Boot,也可在普通 Java 项目使用 |
依赖污染 | 容易把不必要的 Spring Boot 依赖传递给用户项目 | core 不依赖 Spring Boot,autoconfigure 依赖可选,starter 聚合,用户依赖干净 |
扩展性 | 扩展功能容易让 Starter 臃肿 | 可以独立拆模块(比如 MyBatis 插件、Web 拦截器、监控适配) |
测试方便性 | 单元测试与集成测试混杂,调试复杂 | core 可以单元测试,autoconfigure 可以 Spring Boot 测试,demo 应用单独集成验证 |
与官方 Starter 一致性 | 结构不一致,用户上手可能不熟悉 | 模仿 Spring 官方模式,用户熟悉且规范化 |
开发复杂度 | 简单,适合临时内部使用 | 结构稍复杂,需要多模块管理,但更专业 |
IDE 支持 | 测试和运行方便,但逻辑/配置混杂 | 各模块清晰,IDE 可单独加载模块,更易管理依赖和提示 |
二、目录和示意图
1、多模块 Maven Starter 的工程结构示意图
my-feature-parent/ ← 父工程(可选,用于统一版本管理)├── pom.xmlmy-feature-core/ ← 核心逻辑模块(不依赖 Spring Boot)├── pom.xml└── src/main/java/com/example/myfeature/core/└── RequestLogger.java ← 核心逻辑类└── src/test/java/... ← 核心逻辑单元测试my-feature-spring-boot-autoconfigure/ ← 自动配置模块├── pom.xml└── src/main/java/com/example/myfeature/autoconfigure/├── RequestLoggerProperties.java ← 配置属性类└── RequestLoggerAutoConfiguration.java ← 自动配置类└── src/main/resources/META-INF/├── spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports ← 声明自动配置类└── additional-spring-configuration-metadata.json ← IDE 配置提示补充my-feature-spring-boot-starter/ ← Starter 聚合模块(无源码)├── pom.xml└── (只声明依赖 core + autoconfigure)demo-spring-boot-app/ ← 测试 / 示例项目├── pom.xml└── src/main/java/com/example/demo/└── TestController.java ← 注入 RequestLogger 测试使用└── src/main/resources/application.yml└── myfeature.logger.enable-body: true└── myfeature.logger.max-body-length: 500
说明
-
core
-
只放业务逻辑,独立于 Spring Boot,可复用。
-
可单独写单元测试,不依赖 Spring Boot 启动。
-
-
autoconfigure
-
放自动配置类 +
@ConfigurationProperties
。 -
资源文件:
-
AutoConfiguration.imports
→ 声明自动装配类 -
additional-spring-configuration-metadata.json
→ 补充 IDE 提示
-
-
-
starter
-
聚合
core
+autoconfigure
,用户只需引依赖即可。
-
-
demo-spring-boot-app
-
用来测试 Starter 是否开箱即用
-
配置 application.yml,注入 Bean 验证功能
-
优势
-
职责清晰:逻辑、自动配置、依赖聚合分开
-
依赖干净:core 不依赖 Spring Boot,starter 聚合而不污染用户项目
-
扩展性强:以后新增插件、功能模块可以单独拆模块
-
IDE 支持友好:有
metadata
文件,配置项自动提示
2、Starter 使用示意图
业务项目├── pom.xml│ └── 引入 my-feature-spring-boot-starter├── application.yml│ └── myfeature.logger.enable-body: true│ └── myfeature.logger.max-body-length: 500└── src/main/java/com/example/demo/└── TestController.java└── @Autowired RequestLogger loggermy-feature-spring-boot-starter├── 依赖 core + autoconfiguremy-feature-core└── RequestLogger.java ← 核心逻辑类my-feature-spring-boot-autoconfigure├── RequestLoggerProperties.java ← 读取配置└── RequestLoggerAutoConfiguration.java ← 自动装配 @Bean└── META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports└── com.example.myfeature.autoconfigure.RequestLoggerAutoConfigurationSpring Boot 启动流程:1. 扫描 classpath,读取 AutoConfiguration.imports2. 加载 RequestLoggerAutoConfiguration3. 创建 RequestLogger Bean,并注入 RequestLoggerProperties(读取 application.yml 配置)4. 业务项目中 @Autowired RequestLogger 成功注入
流程说明
-
引入 Starter →
core
+autoconfigure
被依赖 -
Spring Boot 启动 → 自动扫描
AutoConfiguration.imports
文件 -
加载自动配置类 → 根据
@ConfigurationProperties
绑定配置 -
创建 Bean → 业务代码中可以直接
@Autowired RequestLogger
使用
3、可视化的多模块 Starter 依赖与 Bean 注入流程图
┌─────────────────────────────┐
│ 业务项目 (Spring Boot) │
│ │
│ application.yml │
│ ┌─────────────────────┐ │
│ │ myfeature.logger.* │ │
│ │ enable-body: true │ │
│ │ max-body-length:500 │ │
│ └─────────────────────┘ │
│ │
│ TestController.java │
│ ┌─────────────────────┐ │
│ │ @Autowired │ │
│ │ RequestLogger │◀────┐
│ └─────────────────────┘ │
└─────────────┬───────────────┘│▼
┌─────────────────────────────┐
│ my-feature-spring-boot-starter │
│ (聚合模块) │
│ ┌───────────────────────┐ │
│ │ 依赖 core + autoconfigure │
│ └───────────────────────┘ │
└─────────────┬───────────────┘│▼
┌─────────────────────────────┐
│ my-feature-core │
│ RequestLogger.java │
│ (核心逻辑类) │
│ log(String body) │
└─────────────┬───────────────┘│ 被自动配置类引用▼
┌─────────────────────────────┐
│ my-feature-spring-boot-autoconfigure │
│ RequestLoggerProperties.java │
│ - @ConfigurationProperties │
│ - 绑定 application.yml 配置 │
│ RequestLoggerAutoConfiguration.java│
│ - @Configuration │
│ - @EnableConfigurationProperties │
│ - @Bean + @ConditionalOnMissingBean │
│ META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports │
│ - 声明自动配置类 │
│ additional-spring-configuration-metadata.json │
│ - IDE 自动提示 │
└─────────────┬───────────────┘│ Spring Boot 启动扫描▼
┌─────────────────────────────┐
│ Spring Boot 容器 │
│ 1. 扫描 AutoConfiguration.imports │
│ 2. 加载 RequestLoggerAutoConfiguration │
│ 3. 创建 RequestLogger Bean │
│ 并注入 RequestLoggerProperties │
│ 4. Bean 注入到 TestController │
└─────────────────────────────┘│▼
┌─────────────────────────────┐
│ Controller 使用 RequestLogger │
│ logger.log(body) │
│ 输出日志 │
└─────────────────────────────┘
流程说明
-
业务项目
-
只配置
application.yml
-
使用
@Autowired
注入 Bean
-
-
starter
-
聚合 core + autoconfigure
-
无源码,仅依赖声明
-
-
core
-
核心逻辑类,不依赖 Spring Boot
-
可在非 Spring Boot 项目复用
-
-
autoconfigure
-
自动配置类 + 配置属性类
-
AutoConfiguration.imports
声明自动加载 -
additional-spring-configuration-metadata.json
增强 IDE 提示
-
-
Spring Boot 容器
-
扫描自动配置类
-
创建 Bean 并注入配置属性
-
注入到 Controller 使用
-
三、相关代码
1、父工程 my-feature-parent/pom.xml
<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.0http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>my-feature-parent</artifactId><version>1.0.0</version><packaging>pom</packaging><modules><module>my-feature-core</module><module>my-feature-spring-boot-autoconfigure</module><module>my-feature-spring-boot-starter</module><module>demo-spring-boot-app</module></modules><properties><java.version>17</java.version><spring.boot.version>3.3.5</spring.boot.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><pluginManagement><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>${spring.boot.version}</version></plugin></plugins></pluginManagement></build>
</project>
2、核心模块 my-feature-core
2.1 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.example</groupId><artifactId>my-feature-parent</artifactId><version>1.0.0</version></parent><artifactId>my-feature-core</artifactId><dependencies><!-- 核心业务依赖 --></dependencies>
</project>
2.2 核心类 RequestLogger.java
package com.example.myfeature.core;/*** 核心逻辑类:记录请求日志* 核心模块不依赖 Spring Boot,任何 Java 项目都可复用*/
public class RequestLogger {// 是否记录请求体private final boolean enableBody;// 请求体最大长度,超过截断private final int maxBodyLength;/*** 构造方法* @param enableBody 是否启用请求体记录* @param maxBodyLength 请求体最大长度*/public RequestLogger(boolean enableBody, int maxBodyLength) {this.enableBody = enableBody;this.maxBodyLength = maxBodyLength;}/*** 打印请求体* @param body 请求内容*/public void log(String body) {if (!enableBody) {System.out.println("Request logged (body disabled)");return;}// 截断超过长度的内容String truncated = body.length() > maxBodyLength? body.substring(0, maxBodyLength) + "...": body;System.out.println("Request body: " + truncated);}
}
3、自动配置模块 my-feature-spring-boot-autoconfigure
3.1 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.example</groupId><artifactId>my-feature-parent</artifactId><version>1.0.0</version></parent><artifactId>my-feature-spring-boot-autoconfigure</artifactId><dependencies><dependency><groupId>com.example</groupId><artifactId>my-feature-core</artifactId><version>${project.version}</version></dependency><!-- Spring Boot自动配置支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><optional>true</optional> <!-- 关键:标记为可选 --></dependency><!-- 配置元数据生成 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency></dependencies>
</project>
3.2 RequestLoggerProperties.java
package com.example.myfeature.autoconfigure;import org.springframework.boot.context.properties.ConfigurationProperties;/*** 配置属性类,用于绑定 application.yml 或 application.properties 中的配置** @ConfigurationProperties 注解说明:* - prefix="myfeature.logger" 表示绑定配置前缀* 例如:myfeature.logger.enable-body* - Spring Boot 会自动将配置注入到这个类的属性中*/
@ConfigurationProperties(prefix = "myfeature.logger")
public class RequestLoggerProperties {/*** 是否启用请求体记录*/private boolean enableBody = true;/*** 请求体最大长度*/private int maxBodyLength = 1000;// getter & setterpublic boolean isEnableBody() { return enableBody; }public void setEnableBody(boolean enableBody) { this.enableBody = enableBody; }public int getMaxBodyLength() { return maxBodyLength; }public void setMaxBodyLength(int maxBodyLength) { this.maxBodyLength = maxBodyLength; }
}
3.3 RequestLoggerAutoConfiguration.java
package com.example.myfeature.autoconfigure;import com.example.myfeature.core.RequestLogger;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 自动配置类** @Configuration 注解说明:* - 表示这是一个 Spring 配置类** @EnableConfigurationProperties 注解说明:* - 激活并注册 @ConfigurationProperties 的 Bean* - 将 RequestLoggerProperties 注入到 Spring 容器*/
@Configuration
@EnableConfigurationProperties(RequestLoggerProperties.class)
public class RequestLoggerAutoConfiguration {/*** Bean 定义方法** @Bean 注解说明:* - 将方法返回的对象注册为 Spring 容器中的 Bean** @ConditionalOnMissingBean 注解说明:* - 如果容器中已有相同类型 Bean,则不创建* - 避免用户自己定义 Bean 时被覆盖*/@Bean@ConditionalOnMissingBeanpublic RequestLogger requestLogger(RequestLoggerProperties props) {// 从配置属性中获取参数return new RequestLogger(props.isEnableBody(), props.getMaxBodyLength());}
}
3.4 自动配置声明 AutoConfiguration.imports
路径:src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容:
com.example.myfeature.autoconfigure.RequestLoggerAutoConfiguration
3.5 IDE 配置提示 additional-spring-configuration-metadata.json
路径:src/main/resources/META-INF/additional-spring-configuration-metadata.json
内容:
{"properties": [{"name": "myfeature.logger.enable-body","type": "java.lang.Boolean","description": "Enable request body logging","defaultValue": true},{"name": "myfeature.logger.max-body-length","type": "java.lang.Integer","description": "Maximum length of request body to log","defaultValue": 1000}]
}
注解 | 作用 |
---|---|
@ConfigurationProperties(prefix="...") | 绑定配置文件属性到 Java 类 |
@EnableConfigurationProperties(...) | 激活 ConfigurationProperties,并注入容器 |
@Configuration | 声明这是一个 Spring 配置类 |
@Bean | 将方法返回对象注册为 Spring Bean |
@ConditionalOnMissingBean | 如果容器已有相同类型 Bean,则不创建(用户可覆盖默认 Bean) |
4、Starter 聚合模块 my-feature-spring-boot-starter
4.1 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.example</groupId><artifactId>my-feature-parent</artifactId><version>1.0.0</version></parent><artifactId>my-feature-spring-boot-starter</artifactId><dependencies><!-- 引入自动配置模块 --><dependency><groupId>com.example</groupId><artifactId>my-feature-spring-boot-autoconfigure</artifactId><version>${project.version}</version></dependency><!-- 必需依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency></dependencies>
</project>
注意:starter 本身不需要源码,只负责聚合依赖。
5、Demo 测试项目 demo-spring-boot-app
5.1 pom.xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.example</groupId><artifactId>my-feature-parent</artifactId><version>1.0.0</version></parent><artifactId>demo-spring-boot-app</artifactId><dependencies><dependency><groupId>com.example</groupId><artifactId>my-feature-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
</project>
5.2 application.yml
myfeature:logger:enable-body: truemax-body-length: 500
5.3 Controller 示例 TestController.java
package com.example.demo;import com.example.myfeature.core.RequestLogger;
import org.springframework.web.bind.annotation.*;/*** 测试 Controller* 验证 Starter 是否正确注入 Bean*/
@RestController
@RequestMapping("/test")
public class TestController {// RequestLogger Bean 会被自动注入private final RequestLogger logger;public TestController(RequestLogger logger) {this.logger = logger;}/*** 测试接口:打印请求体*/@PostMappingpublic String log(@RequestBody String body) {logger.log(body);return "ok";}
}
6、使用方法
6.1 在父工程目录执行
mvn clean install
6.2 启动 demo-spring-boot-app
mvn spring-boot:run -pl demo-spring-boot-app
6.3 测试接口
curl -X POST http://localhost:8080/test -d "Hello Starter!"
6.4 控制台输出
Request body: Hello Starter!
四、总结
这个示例完全覆盖了:
-
多模块结构(core / autoconfigure / starter / demo)
-
自动配置 + properties + AutoConfiguration.imports
-
IDE 智能提示(additional-spring-configuration-metadata.json)
-
demo 项目测试