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?
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.
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).
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.
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.
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;
}
}
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.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.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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With