Avoid unwanted component scanning of Spring Configuration
I 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 initialization of servlet container in method
AppInitializer.getRootConfigClasses()
- By component scan in class
WebConfig
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.
- I created sample project on Github so that you can play with it.
Reference: | Avoid unwanted component scanning of Spring Configuration from our JCG partner Lubos Krnac at the Lubos Krnac Java blog blog. |