getServletConfigClasses() vs getRootConfigClasses() when extending AbstractAnnotationConfigDispatcherServletInitializer
What is the difference between getServletConfigClasses()
vs getRootConfigClasses()
when extending AbstractAnnotationConfigDispatcherServletInitializer
.
I've been reading a lot sources since this morning but I haven't get any clear understanding on the differences yet :
Please have look at these two configurations :
1).
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { ConServlet.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
....
....
}
The ConServlet.class
is refering to
@EnableWebMvc
@Configuration
@ComponentScan({ "com" })
@Import({ SecurityConfig.class })
public class ConServlet {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
2).
public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
.....
}
the WebConfig.class is refering to
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "....." })
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
I see both ConServlet & WebConfig (more or less) doing the same things like initializating view :
But why :
-
ConServlet is returned in
getRootConfigClasses()
- while WebConfig is returned in
getServletConfigClasses()
I read the documentation
both getRootConfigClasses() & getServletConfigClasses() is for
Specify @Configuration and/or @Component classes to be provided to.. (their differences )
-
the root application context for
getRootConfigClasses()
-
the dispatcher servlet application context for
getServletConfigClasses()
but why then ConServlet & WebConfig doing same things (like initizialising view), maybe I'm the one misunderstood it. What's are actually root context and dispatcher servlets (I know this one) in the simple term/example
Thank you!
A Bit on ApplicationContext
Hierarchies
Spring's ApplicationContext
provides the capability of loading multiple (hierarchical) contexts, allowing each to be focused on one particular layer, such as the web layer of an application or middle-tier services.
One of the canonical examples of using hierarchical ApplicationContext
is when we have multiple DispatcherServlet
s in a web application and we're going to share some of the common beans such as datasources
between them. This way, we can define a root ApplicationContext
that contain all the common beans and multiple WebApplicationContext
s that inherit the common beans from the root context.
In the Web MVC framework, each DispatcherServlet
has its own WebApplicationContext
, which inherits all the beans already defined in the root WebApplicationContext
. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given Servlet
instance.
Typical context hierarchy in Spring Web MVC (Spring Documentation)
If you're living in a single DispatherServlet
world, it is also possible to have just one root context for this scenario:
Single root context in Spring Web MVC (Spring Documentation)
Talk is cheap, Show me the code!
Suppose we're developing a web application and we're going to use Spring MVC, Spring Security and Spring Data JPA. For this simple scenario, we would have at least three different config files. A WebConfig
that contains all our web related configurations, such as ViewResolver
s, Controller
s, ArgumentResolver
s, etc. Something like following:
@EnableWebMvc
@Configuration
@ComponentScan(basePackages = "com.so.web")
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
final boolean DO_NOT_USE_SUFFIX_PATTERN_MATCHING = false;
configurer.setUseSuffixPatternMatch(DO_NOT_USE_SUFFIX_PATTERN_MATCHING);
}
}
Here I'm defining a ViewResolver
to resolve my plain old jsps, poor life decisions, basically. We would need a RepositoryConfig
, which contains all the data access facilities such as DataSource
, EntityManagerFactory
, TransactionManager
, etc. It probably would be like following:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = "com.so.repository")
public class RepositoryConfig {
@Bean
public DataSource dataSource() { ... }
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() { ... }
@Bean
public PlatformTransactionManager transactionManager() { ... }
}
And a SecurityConfig
which contains all the security related stuff!
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception { ... }
@Override
protected void configure(HttpSecurity http) throws Exception { ... }
}
For gluing all these together, we have two options. First, we can define a typical hierarchical ApplicationContext
, by adding RepositoryConfig
and SecurityConfig
in root context and WebConfig
in their child context:
public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RepositoryConfig.class, SecurityConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { WebConfig.class };
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Since we have a single DispatcherServlet
here, we can add the WebConfig
to the root context and make the servlet context empty:
public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { RepositoryConfig.class, SecurityConfig.class, WebConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Further Reading
Skaffman did a great job on explaining ApplicationContext
hierarchies in this answer, which is highly recommended. Also, you can read Spring Documentation.
Root Config Classes are actually used to Create Beans which are Application Specific and which needs to be available for Filters (As Filters are not part of Servlet).
Servlet Config Classes are actually used to Create Beans which are DispatcherServlet specific such as ViewResolvers, ArgumentResolvers, Interceptor, etc.
Root Config Classes will be loaded first and then Servlet Config Classes will be loaded.
Root Config Classes will be the Parent Context and it will create a ApplicationContext
instace. Where as Servlet Config Classes will be the Child Context of the Parent Context and it will create a WebApplicationContext
instance.
In your ConServlet
Configuration, You don't need to specify the @EnableWebMvc
as well the InternalResourceViewResolver
bean as they are only required at the WebConfig
.