如何在模块化Spring Boot应用程序中定义隔离的Web上下文?

时间:2016-06-06 14:07:13

标签: spring-mvc spring-boot

鉴于Spring Boot应用程序由一个引导程序模块和两个或多个独立的业务模块组成 - 每个模块都公开了特定于业务域的REST API,并且每个模块都使用独立的,独立的文档存储来实现数据持久性,如何配置这样的应用程序:

  • 引导程序模块定义父上下文(非Web),它为底层模块(全局配置,对象映射器等)提供某些共享资源。
  • 每个业务模块在同一端口上公开其REST控制器,但使用不同的上下文路径。理想情况下,我希望能够为每个模块定义基本路径(例如/ api / catalog,/ api / orders),并分别定义每个控制器中的子路径。
  • 每个业务模块都定义了自己的存储库配置(例如,每个模块的不同MongoDB设置)

为了隔离各个业务模块的上下文(允许我在每个模块中管理独立的存储库配置),我尝试使用SpringApplicationBuilder中提供的上下文层次结构来隔离每个业务模块的上下文: / p>

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 个答案:

答案 0 :(得分:0)

  • 为业务模块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;
    }
    }