Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot with multiple DispatcherServlet, each having their own @Controllers

Tags:

Basically I want to split my application into 2 parts. Each part has it's own security stuff and own @Controllers. The @Services should be accessible from both parts.

So I thought, I should get 2 DispatcherServlet. One listening to /admin/* and the second listening to everything else ( / ). Each of those will have its own AnnotationConfigWebApplicationContext so I can have separate component scan for the @Controllers.

And because Spring Boot provides one DispatcherServlet listening on / out of the box, I thought, I can just add a second one:

@Configuration
public class MyConfig {
    @Bean(name="myDS")
    public DispatcherServlet myDS(ApplicationContext applicationContext) {
        AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
        webContext.setParent(applicationContext);
        webContext.register(MyConfig2.class);
        // webContext.refresh();
        return new DispatcherServlet(webContext);
    }

    @Bean
    public ServletRegistrationBean mySRB(@Qualifier("myDS") DispatcherServlet dispatcherServlet) {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet);
        servletRegistrationBean.addUrlMappings("/admin/*");
        servletRegistrationBean.setName("adminServlet");
        return servletRegistrationBean;
    }
}

The MyConfig2 class, only has @Configuration and @ComponentScan. Within the same package is a @Controller.

When starting the application, I can see, that the second servlet mapping is getting registered, but the @Controller is not. Additionally I can now access all @Controllers from / and /admin.


Any idea how I can get this working?

like image 902
Benjamin M Avatar asked Jun 05 '15 15:06

Benjamin M


People also ask

Can we have multiple DispatcherServlet?

You can have as many DispatcherServlets as you want. Basically what you need to do is duplicate the configuration and give the servlet a different name (else it will overwrite the previous one), and have some separate configuration classes (or xml files) for it.

Why do we need multiple dispatcher servlet in Spring?

Strictly seperating user and admin functionality. Or one for plain Spring MVC and another for Spring Web Flow. If there are major configuration differences for some controllers. (We actually used the Spring MVC and Spring Web Flow seperation so that we could add this without affecting the already exising configs).

Can we have more than one dispatcher servlet in web xml?

A web application can define any number of DispatcherServlet instances. Each servlet will operate in its own namespace, loading its own application context with mappings, handlers, etc. Only the root application context as loaded by ContextLoaderListener, if any, will be shared.

Does Spring boot have DispatcherServlet?

One of the main features of Spring Boot is autoconfiguration. The Spring Boot autoconfiguration registers and configures the DispatcherServlet automatically. Therefore, we don't need to register the DispatcherServlet manually.


1 Answers

I got it working somehow!

Here's my Package Layout:

test.foo.
         FooConfig.java
         FooController.java
test.bar.
         BarConfig.java
         BarController.java
test.app.
         Application.java
         MyService.java
src/main/resources/application.properties

Application.java:

@SpringBootApplication(exclude=DispatcherServletAutoConfiguration.class)
public class Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
    @Bean
    public ServletRegistrationBean foo() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();   
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(FooConfig.class);
        dispatcherServlet.setApplicationContext(applicationContext);
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/foo/*");
        servletRegistrationBean.setName("foo");
        return servletRegistrationBean;
    }
    @Bean
    public ServletRegistrationBean bar() {
        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
        applicationContext.register(BarConfig.class);
        dispatcherServlet.setApplicationContext(applicationContext);
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/bar/*");
        servletRegistrationBean.setName("bar");
        return servletRegistrationBean;
    }
}
  • The exclude does prevent Spring Boot from creating its own DispatcherServlet with / mapping. You can remove that line, if you want that mapping or define your own.
  • You can add servletRegistrationBean.setLoadOnStartup(1) if you want to have your Servlets initialized on application start. Else it will wait for the first request for that servlet.
  • It's important to set servletRegistrationBean.setName(...), else the servlets will override each other.

FooConfig.java & BarConfig.java:

@Configuration @ComponentScan @EnableWebMvc
public class FooConfig { }
  • @EnableWebMvc will enable the component scan. Without it, it won't find the @Controller class.

The Controller and Service code is not important. You just have to know, that if you have @RequestMapping("/foo") inside FooController, the request must be GET /foo/foo because the Servlet's URL mapping is /foo/*. It's not possible to call the URL GET /foo because the Servlet URL mapping needs a / at the end of its path (in other words: GET /foo will look for a Servlet with / mapping!), though @RequestMapping("") must be called via GET /foo/. And of course it was not possible to use /foo or /foo* as Servlet mapping (or I just did not find the correct settings for that)

Scope: The Controllers can't see each other, though it's not possible to @Autowired them in each other. Also the Service can't @Autowired any of the Controllers. But the Controllers can @Autowired the Service.

Though it's a classical parent child context hierarchy.

The only "bad" thing is, that we need @EnableMvcConfig and don't get the auto configured sugar from Spring boot within the context. The parent context is getting auto configured. I put some database stuff within the application.properties and did a query inside MyService which got called by FooController and it worked flawlessly! :)

I hope this may help some people!

like image 105
Benjamin M Avatar answered Sep 21 '22 08:09

Benjamin M