如何在模块化 Spring Boot 应用程序中定义隔离的 Web 上下文?
Posted
技术标签:
【中文标题】如何在模块化 Spring Boot 应用程序中定义隔离的 Web 上下文?【英文标题】:How to define isolated web contexts in a modular Spring Boot application? 【发布时间】:2016-10-06 04:33:29 【问题描述】:假设一个 Spring Boot 应用程序由一个引导模块和两个或多个隔离的业务模块组成——每个模块都公开特定于业务域的 REST API,并且每个模块都使用独立的、隔离的文档存储来进行数据持久性,如何我要不要配置这样的应用程序,这样:
引导模块定义了一个父上下文(非 Web),它为底层模块(全局配置、对象映射器等)提供某些共享资源 每个业务模块都在同一端口上公开其 REST 控制器,但使用不同的上下文路径。理想情况下,我希望能够为每个模块(例如 /api/catalog、/api/orders)定义一个基本路径,并在每个控制器中单独定义子路径。 每个业务模块定义自己的存储库配置(例如,每个模块的不同 MongoDB 设置)为了隔离各个业务模块的上下文(这允许我管理每个模块中的独立存储库配置),我尝试使用 SpringApplicationBuilder 中可用的上下文层次结构来隔离每个单独的业务模块的上下文:
public class Application
@Configuration
protected static class ParentContext
public static void main(String[] args) throws Exception
new SpringApplicationBuilder(ParentContext.class)
.child(products.config.ModuleConfiguration.class)
.web(true)
.sibling(orders.config.ModuleConfiguration.class)
.web(true)
.run(args);
但是,由于每个模块都包含一个使用 @EnableAutoConfiguration 注释的配置类,这会导致 Spring Boot 尝试启动两个独立的嵌入式 servlet 容器,每个容器都尝试绑定到同一个端口:
@Configuration
@EnableAutoConfiguration
public class WebApplicationConfiguration
@Value("$api.basePath:/api")
protected String apiBasePath;
@Bean
public DispatcherServlet dispatcherServlet()
return new DispatcherServlet();
@Bean
public ServletRegistrationBean dispatcherServletRegistration()
ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(),
apiBasePath + "/products/*");
registration.setName(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
return registration;
有关上下文层次结构的 Spring Boot 文档指出父上下文不能是 Web 上下文,所以我对如何在隔离的子上下文之间共享嵌入式 servlet 容器有点迷茫。
我创建了一个最小的GitHub 项目来说明这一点:
【问题讨论】:
【参考方案1】:为业务模块child1的上下文创建配置类
package com.child1;
@Configuration
@ComponentScan(basePackages = "com.child1a", "com.child1b")
public class Child1Configuration
为业务模块child2的上下文创建配置类
package com.child2;
@Configuration
@ComponentScan(basePackages = "com.child2a", "com.child2b")
public class Child2Configuration
为引导模块父上下文创建一个配置类。指定组件扫描要由子上下文共享的 bean
package com.parent;
@Configuration
@ComponentScan(basePackages = "com.parent1", "com.root")
public class ParentConfiguration
使用两个调度程序 servlet bean 创建 SpringBootApplication 类,每个业务模块一个。为每个 servlet 创建应用程序上下文,并将引导应用程序创建的上下文设置为 root。基本上spring会将上下文注入@Bean方法的ApplicationContext参数中。
package com.parent;
@SpringBootApplication
public class Application
public static void main(String[] args)
ApplicationContext context = SpringApplication.run(Application.class, args);
@Bean
public ServletRegistrationBean child1(ApplicationContext parentContext)
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDetectAllHandlerMappings(false);
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.setParent(parentContext);
applicationContext.register(Child1Configuration.class);
applicationContext.refresh();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new
ServletRegistrationBean(dispatcherServlet, true, "/child1/*");
servletRegistrationBean.setName("child1");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
@Bean
public ServletRegistrationBean child2(ApplicationContext parentContext)
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDetectAllHandlerMappings(false);
AnnotationConfigWebApplicationContext applicationContext = new
AnnotationConfigWebApplicationContext();
applicationContext.setParent(parentContext);
applicationContext.register(Child2Configuration.class);
applicationContext.refresh();
dispatcherServlet.setApplicationContext(applicationContext);
ServletRegistrationBean servletRegistrationBean = new
ServletRegistrationBean(dispatcherServlet, true, "/child2/*");
servletRegistrationBean.setName("child2");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
【讨论】:
以上是关于如何在模块化 Spring Boot 应用程序中定义隔离的 Web 上下文?的主要内容,如果未能解决你的问题,请参考以下文章
如何在多模块spring boot应用程序gradle中只访问不同模块的一个类?
如何在我的 Maven Spring Boot 多模块项目的测试中引用兄弟模块?
如何在spring-boot中添加多个application.properties文件?
如何使用 CommandLineJobRunner 调用嵌入在 Spring Boot 应用程序中的 Spring Batch 作业