Я столкнулся с интересной проблемой переполнения стека. У Бретта Райана была проблема, что конфигурация Spring Security была инициализирована дважды. Когда я изучал его код, я обнаружил проблему. Позвольте мне показать, показать код.
У него довольно стандартное Spring приложение (не использующее Spring Boot). Используется более современная конфигурация сервлета Java, основанная на AbstractAnnotationConfigDispatcherServletInitializer
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
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[]{ "/" }; } } |
Как видите, есть два класса конфигурации:
-
SecurityConfig
— содержит конфигурацию Spring Security -
WebConfig
— основная конфигурация контейнера IoC Spring
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
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" ); } } |
01
02
03
04
05
06
07
08
09
10
11
|
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 { } |
Обратите внимание на компонент сканирования в WebConfig
. Это сканирующий пакет, где расположены все три класса. Когда вы запускаете это в контейнере сервлета, текст «Spring Security init…» записывается в консоль дважды. Это означает, что конфигурация SecurityConfig
загружается дважды. Было загружено:
- При инициализации контейнера сервлета в методе
AppInitializer.getRootConfigClasses()
- По компонентному сканированию в классе
WebConfig
Почему? Я нашел это объяснение в документации Spring :
Помните, что классы
@Configuration
мета-аннотированы@Component
, поэтому они являются кандидатами для сканирования компонентов!
Так что это особенность Spring, и поэтому мы хотим избежать сканирования компонентов Spring @Configuration
используемого конфигурацией @Configuration
. Бретт Райан независимо нашел эту проблему и показал свое решение в упомянутом вопросе переполнения стека:
1
2
3
4
5
6
7
8
|
@ComponentScan (basePackages = "com.acme.app" , excludeFilters = { @Filter (type = ASSIGNABLE_TYPE, value = { WebConfig. class , SecurityConfig. class }) }) |
Мне не нравится это решение. Аннотация слишком многословна для меня. Также некоторые разработчики могут создать новый класс @Configuration
и забыть включить его в этот фильтр. Я бы предпочел указать специальный пакет, который будет исключен из сканирования компонентов Spring.
- Я создал пример проекта на Github, чтобы вы могли поиграть с ним.
Ссылка: | Избегайте нежелательного сканирования компонентов Spring Configuration от нашего партнера JCG, Любоса Крнака, в |