How to define isolated web contexts in a modular Spring boot application?

Given the Spring Boot application, which consists of a bootstrap module and two or more isolated business modules, each of which provides a business domain-specific REST API, and each of which uses an independent isolated document storage to save data, what should I do ? about setting up such an application so that:

  • The bootstrap module defines the parent context (not the web) that provides certain shared resources to the base modules (global configuration, object mappers, etc.).
  • Each business module provides its REST controllers on the same port, but with a different context path. Ideally, I want to be able to determine the base path for each module (for example, / api / catalog, / api / orders) and separately define a subpath in each controller.
  • Each business module defines its own repository configuration (for example, different MongoDB settings for each module)

To isolate the contexts of individual business modules (which allows me to manage independent repository configurations in each module), I tried to use the context hierarchies available in SpringApplicationBuilder to isolate the contexts of each of the individual business modules:

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); } } 

however, since each module contains a configuration class annotated with @EnableAutoConfiguration, this forces Spring Boot to try to run two independent built-in servlet containers, each of which tries to connect to the same port:

 @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; } } 

The Spring Boot documentation on contextual hierarchies says that a parent context cannot be a web context, so I was a bit confused about how to split the built-in servlet container between isolated child contexts.

I created a minimal GitHub project to illustrate this:

+7
source share
1 answer
  • Create a configuration class for the child1 context of the business module

     package com.child1; @Configuration @ComponentScan(basePackages = {"com.child1a", "com.child1b"}) public class Child1Configuration { } 
  • Create a configuration class for the child2 context of the business module

     package com.child2; @Configuration @ComponentScan(basePackages = {"com.child2a", "com.child2b"}) public class Child2Configuration { } 
  • Create a configuration class for the parent context of the bootstrap module. Specify a scan component for beans that will be shared by child contexts.

     package com.parent; @Configuration @ComponentScan(basePackages = {"com.parent1", "com.root"}) public class ParentConfiguration { } 
  • Create a SpringBootApplication class with two dispatcher beans, one for each business module. Create an application context for each servlet and set the context created by the boot application as root. Essentially, Spring will insert context into the ApplicationContext parameter of @Bean methods.

     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; } } 
0
source

All Articles