Avoid Unwanted Component Scanning of Spring Configuration
Join the DZone community and get the full member experience.
Join For FreeI came through interesting problem on Stack Overflow. Brett Ryan had problem that Spring Security configuration was initialized twice. When I was looking into his code I spot the problem. Let me show show the code.
He has pretty standard Spring application (not using Spring Boot). Uses more modern Java servlet Configuration based on Spring’s AbstractAnnotationConfigDispatcherServletInitializer
.
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SecurityConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
As you can see, there are two configuration classes:
SecurityConfig
– holds Spring Security configurationWebConfig
– main Spring’s IoC container configuration
package net.lkrnac.blog.dontscanconfigurations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("Spring Security init...");
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "net.lkrnac.blog.dontscanconfigurations")
public class WebConfig extends WebMvcConfigurerAdapter {
}
Pay attention to the component scanning in WebConfig
. It is scanning package where all three classes are located. When you run this on servlet container, text “Spring Security init…” is written to console twice. It mean mean SecurityConfig
configuration is loaded twice. It was loaded
- During creation of root context in method
AppInitializer.getRootConfigClasses()
- By component scan in class
WebConfig
. This instance is created as part of servlet context creation in methodAppInitializer.getServletConfigClasses().
Why? I found this explanation in Spring’s documentation:
Remember that @Configuration
classes are meta-annotated with @Component
, so they are candidates for component-scanning!
So this is feature of Spring and therefore we want to avoid component scanning of Spring @Configuration
used by Servlet configuration. Brett Ryan independently found this problem and showed his solution in mentioned Stack Overflow question:
@ComponentScan(basePackages = "com.acme.app",
excludeFilters = {
@Filter(type = ASSIGNABLE_TYPE,
value = {
WebConfig.class,
SecurityConfig.class
})
})
I don’t like this solution. Annotation is too verbose for me. Also some developer can create new @Configuration
class and forget to include it into this filter. I would rather specify special package that would be excluded from Spring’s component scanning.
Even better solution for this problem would be for me not to define separate contexts and rather use only servlet context as described in Spring Reference Documentation. Far most optimal solution is using Spring Boot with embedded servlet container, where you don’t need to define AbstractAnnotationConfigDispatcherServletInitializer
at all.
I created sample project on Github so that you can play with it.
Published at DZone with permission of Lubos Krnac, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments