[Spring] PropertySourcesPlaceholderConfigurer 로 설정한 properties 는 Environment.getProperty 로 읽을 수 없다.

식빵·2023년 10월 14일
0

spring-legacy-configure

목록 보기
3/9
post-thumbnail

spring legacy (= spring boot 를 사용하지 않는 환경) project 고도화 작업을 하는 중이여서 기존 소스에 기능을 추가하는 작업 도중 정말 어이없는 곳에서 삽질을 했는데, 이를 기록한 글입니다.



일단 기존 spring 설정에서 PropertySourcesPlaceholderConfigurer 가 아래처럼 있었습니다.

<bean id="egov.propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  <property name="locations">
    <list>
      <value>classpath:/workframe/props/project-${spring.profiles.active}.properties</value>
    </list>
  </property>
</bean>



이 PropertySourcesPlaceholderConfigurer 가 읽은 properties 값들을
저는 Environment 클래스를 통해서 조회하려 했습니다.

public class ProjectWebApplicationInitializer implements WebApplicationInitializer {

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		// Spring ServletContextListener 설정
		XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
		rootContext.setConfigLocations("classpath*:config/spring/xml/**/context-*.xml");
		rootContext.refresh();
		rootContext.start();

		// test 로 출력해봄
		System.out.println(rootContext.getEnvironment().getProperty("project.batch.directory"));
        
	}
}

properties 파일에 있는 특정 key 로 값을 조회하여 특정 작업을 더 수행해야 되서,
해당 값을 getProperty("project.batch.directory") 메소드로 조회해봤습니다.
그런데 계속 null 이 나오는 겁니다 😂



Environment 는 모든 설정값들을 추상화한 클래스의 역할을 하니,
당연히 조회가 될 거라고 생각했습니다. 그런데 아래와 같은 stackoverflow 글을
읽고 제가 큰 착각을 했다는 걸 알았습니다.

https://stackoverflow.com/questions/14169834/spring-3-1-environment-does-not-work-with-user-property-files

PropertySourcesPlaceholderConfigurer reads property files directly(as it was done by PropertyPlaceholderConfigurer in Spring 3.0 times), it's just a postprocessor which does not change the way properties are used in the Spring context - in this case properties are only available as bean definition placeholders.

It's the PropertySourcesPlaceholderConfigurer who uses Environment and not vice versa.

Property sources framework works on the application context level, while property placeholder configurers only provide the functionality to process placeholders in the bean definitions. To use property source abstraction you should use @PropertySource annotation i.e. decorate your configuration class with something like @PropertySource("classpath:SpringConfig.properties")

I believe that you can do the same thing programmatically, i.e. you can get the container's ConfigurableEnvironment before the context was refreshed, modify its MutablePropertySources(you need first to get AbstractApplicationContext environment property via context.getEnvironment() ) via getPropertySources().addFirst(new ResourcePropertySource(new ClassPathResource(
"SpringConfig.properties"))); but it's unlikely what you want to do - if you already have a @Configuration annotated class, decorating it with @PropertySource("classpath:SpringConfig.properties") is much simpler.

As for the PropertySourcesPlaceholderConfigurer instance - it will fetch property sources automatically(as it implements EnvironmentAware) from its application context so you need just to register a default instance of it.

For the examples of custom property source implementation see http://blog.springsource.org/2011/02/15/spring-3-1-m1-unified-property-management/

요약하자면!

  • PropertySourcesPlaceholderConfigurerEnvironment 를 참조는 하지만,
    EnvironmentPropertySourcesPlaceholderConfigurer 를 참조하지는 않는다.

  • EnvironmentProperties 파일을 검색하도록 하고 싶다면 @PropertySource 를 사용하라!

  • @PropertySource 대신 코드로 설정하고 싶다면 아래처럼 하면 된다.

context.getEnvironment().getPropertySources()
		.addFirst(new ResourcePropertySource(new ClassPathResource("SpringConfig.properties")));



Environment 을 너무 믿은 제 잘못이였습니다 😥
일단 당장 급해서 아래처럼 문제를 해결해봤습니다.

XmlWebApplicationContext rootContext = new XmlWebApplicationContext();
rootContext.setConfigLocations("classpath*:config/spring/xml/**/context-*.xml");
rootContext.refresh();
rootContext.start();

String profile = rootContext.getEnvironment().getProperty("spring.profiles.active");
System.out.println(profile);

try {

	ResourcePropertySource resourcePropertySource 
    	= new ResourcePropertySource(
        	new ClassPathResource("workframe/props/project-" + profile + ".properties")
		  );
	System.out.println(resourcePropertySource.getProperty("datamng.batch.directory"));
    
} catch (IOException e) {
	throw new RuntimeException(e);
}
  • 예시이므로 그냥 위처럼 작성했지만
    이렇게 지저분하게 초기화 코드 내에 복잡한 로직을 쓰는 거 보다는
    해당 로직을 메소드로 빼든, 객체로 빼든해야겠죠?
profile
백엔드를 계속 배우고 있는 개발자입니다 😊

0개의 댓글