Я столкнулся с интересной проблемой переполнения стека. У Бретта Райана была проблема, что конфигурация 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@EnableWebMvcSecuritypublic 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, Любоса Крнака, в |