29

I created a spring boot application with a parent context (services) and child context (spring-webmvc controllers):

@Configuration
public class MainApiApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .parent(Services.class)
                .child(ApiOne.class, MainApiApplication.class)
                .run(args);
    }

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        return new TomcatEmbeddedServletContainerFactory();
    }

}

Now I want to add another client context (and DispatcherServlet) for my ApiTwo.class configuration. I think I have to do two things:

  1. Move the servletContainer (thus the MainApiApplication.class configuration) out of the child context and
  2. add a path mapping /one/ -> ApiOne.class and /two/ ApiTwo.class

What is the spring boot way to do it?

Jan
  • 2,803
  • 6
  • 36
  • 57

2 Answers2

38

As @josh-ghiloni already said, you need to register a ServletRegistrationBean for every isolated web context you want to create. You need to create an application context from a xml or java config class. You can use @Import and @ComponentScan annotation to add shared services to the parent context. Here is an example:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;


//@ComponentScan({"..."})
//@Import({})
public class Starter {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Starter.class, args);
    }

    @Bean
    public ServletRegistrationBean apiV1() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();

        XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
        applicationContext.setConfigLocation("classpath:/META-INF/spring/webmvc-context.xml");
        dispatcherServlet.setApplicationContext(applicationContext);

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/api/1/*");
        servletRegistrationBean.setName("api-v1");

        return servletRegistrationBean;
    }

    @Bean
    public ServletRegistrationBean apiV2() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();

        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(ResourceConfig.class);
        dispatcherServlet.setApplicationContext(applicationContext);

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/api/2/*");
        servletRegistrationBean.setName("api-v2");
        return servletRegistrationBean;
    }
}
ksokol
  • 8,035
  • 3
  • 43
  • 56
  • I originally wanted to split the bounty because @josh-ghiloni posted the initial idea but your post is more complete. Because the purpose of SO is to have one conclusive answer to one question i grant it to you. Thanks! The only missing part in your example is to add the Services.class context, could you add it @ksokol? – Jan Mar 27 '15 at 14:08
  • I upvoted @josh-ghiloni to indicate his correct answer. To add services to the parent context you need to add `@Import({ServiceConfig1.class, ServiceConfig2.class})` or `@ComponentScan("my.package.service")`. Take a look at the first to lines in my example. – ksokol Mar 27 '15 at 16:09
  • 3
    what is the ResourceConfig.class ? – Arpan Dec 26 '19 at 05:49
14

Create a ServletRegistrationBean that declares the servlet and its mappings. You will probably also want to exclude DispatcherServletAutoConfiguration from the autoconfigurations called, because it will register a DispatcherServlet at / and override yours

EDIT Despite my comment below saying you might not need this, unless you need your APIs running on separate ports (and it doesn't sound like you do), Dave Syer, one of the authors of Spring Boot, answered a very similar question here: Configure multiple servletcontainers/servlets with spring boot

Community
  • 1
  • 1
Josh Ghiloni
  • 1,260
  • 8
  • 19
  • Can you give an example including the ApiOne/Two.class contexts? Also, where should I place the ServletRegistrationBean, in the parent context where currently my Services are registered? – Jan Mar 19 '15 at 18:53
  • 1
    As I read your question, I'm not sure you need multiple DispatcherServlets. What's the reasoning behind that? You can easily have two Api `Controller`s living in harmony with each other. You can just add `@RequestMapping("/one")` to the ApiOne class and `@RequestMapping("/two")` to the ApiTwo class. – Josh Ghiloni Mar 19 '15 at 20:46
  • 1
    They have conflicting error handlers and message converter configurations. I'd prefer to separate them from each other as I did it in a web.xml based project years ago. – Jan Mar 19 '15 at 21:48
  • I see -- you might want to read Dave's answer I linked then, that might help you. – Josh Ghiloni Mar 19 '15 at 21:51