Статьи

Spring @Bean и PropertyPlaceHolderConfigurer

Недавно я был озадачен тем, что, как я думал, будет довольно простой реализацией — рассмотрим следующий файл определения bean-компонента на основе Spring Java (
@ Конфигурация ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package root;
 
...
 
@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig {
 
 @Value("${test.prop}")
 private String attr;
 
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
}

Здесь определяется bean-компонент `sampleService`, этот bean-компонент инициализируется атрибутом, который заполняется с помощью аннотации @Value с использованием строки-заполнителя свойства $ {test.prop}.

Тест для этого заключается в следующем:

01
02
03
04
05
06
07
08
09
10
@ContextConfiguration(classes=SampleConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class ConfigTest {
 @Autowired
 private SampleService sampleService;
 @Test
 public void testConfig() {
  assertThat(sampleService.aMethod(), is("testproperty"));
 }
}

который в текущей реализации SampleConfig завершается неудачей, так как заполнитель $ {test.prop} не разрешается вообще. Стандартное исправление для этого заключается в регистрации PropertySourcesPlaceholderConfigurer, который является BeanFactoryPostProcessor, отвечающим за сканирование всех зарегистрированных определений bean-компонентов и внедрение в разрешенные заполнители. С этим изменением на месте файл @Configuration выглядит так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig {
 @Value("${test.prop}")
 private String attr;
 
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
 
 @Bean
 public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}

Однако после добавления в распознаватель свойств тест все равно не пройден, на этот раз значение, возвращаемое sampleService, равно нулю, даже не значение заполнителя!

Причиной этой проблемы, как выясняется, является то, что в @Configuration, которая внутренне использует аннотации, такие как @Autowired, @Value и @PostConstruct, любые bean-компоненты BeanFactoryPostProcessor должны быть объявлены с помощью статического модификатора. В противном случае содержащийся класс @Configuration создается очень рано, и BeanPostProcessors, отвечающие за разрешение аннотаций, таких как @Value, @Autowired и т. Д., Не могут на него воздействовать.

Это исправление хорошо документировано в javadoc @Bean , кроме того, также регистрируется сообщение, которое предоставляет обходной путь:

1
WARN : org.springframework.context.annotation.ConfigurationClassEnhancer - @Bean method RootConfig.placeHolderConfigurer is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean Javadoc for complete details

Итак, с этим исправлением новая рабочая конфигурация выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@Configuration
@PropertySource("classpath:root/test.props")
public class SampleConfig {
 @Value("${test.prop}")
 private String attr;
 
 @Bean
 public SampleService sampleService() {
  return new SampleService(attr);
 }
 
 @Bean
 public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
  return new PropertySourcesPlaceholderConfigurer();
 }
}

Рекомендации:

Ссылка: Spring @Bean и PropertyPlaceHolderConfigurer от нашего партнера по JCG Биджу Кунджуммен в блоге all and sundry.