Spring Boot依赖其他内嵌Tomcat应用的配置与Bean访问方案
该需求完全可实现,根据实际诉求分两类方案处理,不需要强行启动两个web容器也能正常调用App2的Bean。
启动崩溃的核心原因是:Spring Boot会自动扫描classpath下所有符合条件的自动配置类,当你把App2作为依赖引入后,App2自带的web服务自动配置(内嵌Tomcat、server配置加载逻辑)会被App1的主Spring上下文直接加载,两个web服务配置抢同一个上下文资源,自然会出现端口冲突、配置覆盖问题。
这种场景下你本质是把App2当成一个提供Bean的普通Jar包用,根本不需要启动App2自己的内嵌Tomcat,按以下步骤配置即可:
- 第一步:排除App2带来的web自动配置,阻止Tomcat重复初始化。直接在App1的启动类上添加web自动配置排除项:
@SpringBootApplication( // 替换成你自己的App1、App2的根包路径,确保能扫到App2的Bean scanBasePackages = {"com.yourapp.app1", "com.yourapp.app2"}, exclude = {ServletWebServerFactoryAutoConfiguration.class} ) public class App1Application { public static void main(String[] args) { SpringApplication.run(App1Application.class, args); } }
如果扫包路径不好控制,直接用@Import显式导入App2的核心配置类/启动类即可,避免漏扫Bean:
@SpringBootApplication(exclude = {ServletWebServerFactoryAutoConfiguration.class}) @Import(com.yourapp.app2.App2Application.class) // 替换为App2的启动类 public class App1Application { public static void main(String[] args) { SpringApplication.run(App1Application.class, args); } }
- 第二步:解决配置文件冲突。App2自带的
application.properties会被Spring Boot默认加载,很可能覆盖App1的server.port等配置。你可以直接在App1的配置文件中显式指定仅加载自身配置:
# App1的application.properties中添加 spring.config.location=classpath:/application.properties
如果App2是你自己维护的,更推荐把App2的配置项统一加专属前缀(比如app2.xxx),不要占用server.*这类Spring Boot通用web配置前缀。
- 可选优化:如果你改不了App2的代码,直接在Maven引入App2依赖时排除内嵌Tomcat依赖,从根源上避免Tomcat启动冲突:
<dependency> <groupId>com.yourapp</groupId> <artifactId>app2</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency>
配置完成后App2的所有Bean都会被注册到App1的Spring上下文中,直接用@Autowired注入即可使用,和你自己在App1里写的Bean没有区别。
如果业务要求App1、App2都作为独立web服务在同一个JVM里运行(比如要同时对外提供两个端口的接口),需要把两个应用的Spring上下文完全隔离,不能让App2的配置被App1的主上下文扫描到:
- 第一步:调整App1的扫包范围,不要覆盖App2的包路径,也不要用
@Import导入App2的配置类,避免App2的web配置被主上下文加载。 - 第二步:在App1中编写独立配置类,用子上下文的方式拉起App2的独立容器,指定专属配置和端口:
@Configuration public class App2LaunchConfig { @Bean public ConfigurableApplicationContext app2ApplicationContext() { return new SpringApplicationBuilder(App2Application.class) .child(App2Application.class) // 指定App2独立的配置文件,和App1配置隔离 .properties("spring.config.name=app2") .web(WebApplicationType.SERVLET) // 显式指定App2的端口,避免冲突 .properties("server.port=8081") .run(); } }
- 第三步:在App1的resources目录下新建
app2.properties,存放App2专属的所有配置,不要和App1的application.properties混放。
注意:这种方案下两个容器的Bean是完全隔离的,App1不能直接用@Autowired注入App2的Bean,需要通过上面注册的app2ApplicationContext实例调用getBean()方法手动获取。
- 不要直接把完整的Spring Boot应用当普通工具Jar包引入不做任何配置,Spring Boot的自动配置是classpath维度全局生效的,只要在依赖里就会被主上下文加载,必然出现配置冲突。
- 单纯为了复用Bean优先选方案一,没有额外的容器开销,配置维护成本最低。
内容的提问来源于stack exchange,提问作者markcasper

