Basically I want to split my application into 2 parts. Each part has it's own security stuff and own @Controller
s. 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 @Controller
s.
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