1. 概述
Spring Boot 支持创建多个独立的 ApplicationContext
并组织成父子层级结构。这种机制在模块化设计、资源共享和配置隔离等场景中非常实用。
本文重点介绍如何使用 Spring Boot 提供的 Fluent Builder API(即 SpringApplicationBuilder
)来构建多层级的应用上下文。我们不会讲解 Spring Boot 基础搭建流程,这部分内容可参考官方文档或相关入门教程。
✅ 核心目标:掌握通过 fluent 风格 API 构建复杂上下文拓扑的能力
⚠️ 注意:本文面向已有 Spring Boot 使用经验的开发者,基础概念不再赘述
2. 应用上下文层级结构(Application Context Hierarchy)
Spring 容器支持上下文之间的父子关系,形成一个树状结构:
- ✅ 子上下文可以访问父上下文中的 Bean
- ❌ 父上下文无法访问子上下文中的 Bean
- ✅ 子上下文可覆盖父上下文中同名 Bean 的定义
- ✅ 一个父上下文可以有多个子上下文,但一个子上下文只能有一个直接父上下文
- ✅ 利用层级隔离,可实现模块间松耦合,避免 Bean 冲突
典型应用场景包括:
- 多租户系统
- 后台管理与前台服务共享核心服务但独立 Web 配置
- 微服务中聚合多个子系统到同一 JVM
3. 使用 SpringApplicationBuilder
API
SpringApplicationBuilder
提供了链式调用风格的 API,便于构建上下文层级:
方法 | 作用说明 |
---|---|
parent() |
设置父上下文 |
child() |
添加子上下文 |
sibling() |
添加兄弟上下文(共用同一个父上下文) |
我们将构建如下结构:
Parent Context (非 Web)
├── Child Context 1 (Web, port=8074)
└── Sibling Context 2 (Web, port=8075)
两个 Web 子应用运行在同一 JVM,共享父上下文中的服务 Bean,但各自拥有独立的接口和配置。
3.1 父上下文:共享服务定义
父上下文用于存放多个子应用共享的 Bean,比如通用业务服务、数据源、安全组件等。
我们先定义一个基础服务接口:
public interface IHomeService {
String getGreeting();
}
实现类注册为 Spring Bean:
@Service
public class HomeService implements IHomeService {
public String getGreeting() {
return "Welcome User";
}
}
配置类启用组件扫描,确保 @Service
被加载:
@Configuration
@ComponentScan("com.example.parent")
public class ParentConfig {}
⚠️ 包路径隔离:为避免组件扫描冲突,每个上下文使用独立包名
3.2 子上下文 1:覆盖父级 Bean
第一个子应用运行在端口 8074
,其配置如下:
配置文件 ctx1.properties
server.port=8074
server.servlet.context-path=/ctx1
spring.application.admin.enabled=false
spring.application.admin.jmx-name=org.springframework.boot:type=Ctx1Rest,name=Ctx1Application
✅ 注意设置了独立的 JMX 名称,防止 MBean 冲突
子上下文配置类
@Configuration
@ComponentScan("com.example.ctx1")
@EnableAutoConfiguration
@PropertySource("classpath:ctx1.properties")
public class Ctx1Config {
@Bean
public IHomeService homeService() {
return new GreetingService();
}
}
这里我们显式定义了一个 homeService
Bean,它会 覆盖 父上下文中的同名 Bean。
自定义服务实现
@Service
public class GreetingService implements IHomeService {
public String getGreeting() {
return "Greetings for the day";
}
}
控制器接口
@RestController
public class Ctx1Controller {
@Autowired
private IHomeService homeService;
@GetMapping("/home")
public String greeting() {
return homeService.getGreeting(); // 返回 "Greetings for the day"
}
}
访问 http://localhost:8074/ctx1/home
将返回子上下文提供的问候语。
3.3 兄弟上下文:继承父级 Bean
第二个子应用运行在端口 8075
,不重新定义 homeService
,而是直接使用父上下文的实现。
配置文件 ctx2.properties
server.port=8075
server.servlet.context-path=/ctx2
spring.application.admin.enabled=false
spring.application.admin.jmx-name=org.springframework.boot:type=WebAdmin,name=SpringWebApplication
配置类
@Configuration
@ComponentScan("com.example.ctx2")
@EnableAutoConfiguration
@PropertySource("classpath:ctx2.properties")
public class Ctx2Config {}
控制器
@RestController
public class Ctx2Controller {
@Autowired
private IHomeService homeService;
@GetMapping("/greeting")
public String getGreeting() {
return homeService.getGreeting(); // 返回 "Welcome User"
}
}
✅ 这里注入的
homeService
来自父上下文
❌ 若父上下文未提供该 Bean,则启动报错
访问 http://localhost:8075/ctx2/greeting
将返回父上下文的默认问候语。
3.4 构建上下文层级
最后,使用 SpringApplicationBuilder
组装整个结构:
public class App {
public static void main(String[] args) {
new SpringApplicationBuilder()
.parent(ParentConfig.class).web(WebApplicationType.NONE)
.child(Ctx1Config.class).web(WebApplicationType.SERVLET)
.sibling(Ctx2Config.class).web(WebApplicationType.SERVLET)
.run(args);
}
}
关键点解析:
链式调用 | 说明 |
---|---|
.parent(ParentConfig.class) |
指定父上下文配置类 |
.web(WebApplicationType.NONE) |
父上下文为非 Web 类型 |
.child(Ctx1Config.class) |
添加第一个子上下文 |
.sibling(Ctx2Config.class) |
添加兄弟上下文(也是子上下文) |
.web(WebApplicationType.SERVLET) |
明确指定为 Servlet Web 应用 |
启动后验证:
- ✅
GET http://localhost:8074/ctx1/home
→"Greetings for the day"
- ✅
GET http://localhost:8075/ctx2/greeting
→"Welcome User"
4. 总结
通过本文的示例,我们掌握了使用 SpringApplicationBuilder
构建复杂上下文层级的核心技巧:
✅ 父子继承:子上下文可复用父上下文的 Bean,减少重复配置
✅ 配置覆盖:子上下文可通过重新定义 Bean 实现定制化逻辑
✅ 兄弟共享:多个子上下文可共用父级资源,实现模块化协作
✅ 隔离安全:不同上下文之间 Bean 不可见,降低耦合风险
💡 实际项目中的建议:
- 使用独立包路径避免
@ComponentScan
扫描错乱 - 注意 JMX 名称冲突问题,尤其是在同一 JVM 多实例场景
- 非 Web 父上下文适合放置通用服务、数据层、工具类等
- Web 子上下文负责接口暴露、MVC 配置、安全控制等
该模式在需要 多接口模块共享核心服务但独立部署配置 的场景下非常实用,是一种简单粗暴又高效的架构手段。
示例代码已托管至 GitHub:https://github.com/example/spring-boot-ctx-fluent