Notice
Recent Posts
Recent Comments
Link
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

잡동사니를 모아두는 서랍장

PropertySourcesPlaceholderConfigurer로 프로퍼티로 지정하기 본문

Spring

PropertySourcesPlaceholderConfigurer로 프로퍼티로 지정하기

kingkk31 2020. 2. 22. 11:34

프로파일에 따라 프로퍼티가 다르고, 프로퍼티 파일이 외부에 있는 상황일 수도 있는 상황이 있었다.

단순히 파일을 확인해서 긁어 오거나 @PropertySource 를 지정해도 되겠지만 오늘은 PropertySourcesPlaceholderConfigurer를 써보자.

PropertySourcesPlaceholderConfigurer

  • PropertySourcesPlaceholderConfigurer 는 추상 클래스 PlaceholderConfigurerSupport 를 구현한 클래스.
  • PropertySource를 추가하여 @Value 어노테이션의 ${...} 부분에 프로퍼티 값을 주입해준다
  • Bean 으로 등록해서 사용하며, PropertySource 를 여러개 등록할 수 있다.
  • 프로퍼티가 겹칠 땐 먼저 등록된 PropertySource 의 프로퍼티가 우선순위를 가진다.
  • 기본은 파일의 값이 로컬 설정보다 우선순위가 높다. 로컬 설정을 높게 하려면 따로 설정을 바꿔야한다.

사전 배경

대충 상황은 이렇다. "a" 라는 이름의 프로파일이 존재한다(실행 시 VM argument에 기재 : -Dspring.profiles.active=a). 그러므로 a 의 프로퍼티를 읽어와야 하는데 프로퍼티 파일은 외부의 특정 경로에 있을 수도 있고(\profile-test\a.properties) 없으면 내부의 파일을 읽는다(application-a.properties).

 

a 프로퍼티 파일

[내부 - classpath 내의 resource 밑의 application-a.properties]

spring.jackson.default-property-inclusion = NON_NULL

property.num = 123

property.a = inner_a  



[외부 - \profile-test\a.properties]

spring.jackson.default-property-inclusion = NON_NULL

property.num = 456

property.a = outer_a

일단 외부 설정 파일이 없는 상황이라고 생각하자.

 

단순히 요청이 들어오면 a 프로퍼티 내용을 출력해주는 API가 있다.

[컨트롤러]
@RestController
public class ControllerProfile {   
	@Autowired  
    private ServiceProfile serviceProfile;   
    
    @GetMapping(value = "/profile")  
    public void getProfile() {    
    	this.serviceProfile.printProfile();  
    }
}
 

[인터페이스]
public interface ServiceProfile {  
	public void printProfile();
}    


[ServiceProfile 을 구현한 서비스]
@Slf4j
@Profile("a")
@Service
public class ServiceProfileAImpl implements ServiceProfile {   
	@Autowired  private ConfigA configA;   
    @Override  public void printProfile() {    
    	log.info("property.a : " + this.configA.getPropertyA());    
        log.info("property.num : " + this.configA.getPropertyNum());  
    } 
}
 

[Config]
@Getter
@Setter
@Component
public class ConfigA {  
	@Value("${property.a}")  
    private String propertyA;   
    @Value("${property.num}")  
    private Integer propertyNum;
}

이 상황에서 실행시키면 당연히 외부에 파일이 있든 없든 상관없이 내부의 프로퍼티 파일 내용이 찍힌다

2020-02-18 09:40:53 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.a : inner_a

2020-02-18 09:40:53 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.num : 123

 

PropertySourcesPlaceholderConfigurer 적용

PropertySourcesPlaceholderConfigurer 를 빈으로 등록한다. 기본 properties 를 읽은 후 프로파일에 맞는 properties를 읽어온다.

순서는 먼저 외부 파일을 읽어오는 시도를 하고 실패 시 내부 파일을 읽는다.

@Slf4j
@Configuration
@EnableConfigurationProperties
public class PropertyPlaceholderConfig {
	public static void loadExternalPropertySource(final MutablePropertySources propertySources, final String name) {
    	PropertiesPropertySourceLoader sourceLoader = new PropertiesPropertySourceLoader();
        Resource fileResource = new FileSystemResource(String.format("/profile-test/%s.properties", name));
        try {
        	propertySources.addLast(sourceLoader.load(name, fileResource).get(0));
        } catch (Exception e) {
        	log.error("Can't load external properties. : " + e.getMessage());
            loadInnerPropertySource(propertySources, name);
        }  
    }
    
    public static void loadInnerPropertySource(final MutablePropertySources propertySources, final String name) {
    	String propertiesName = (!"default".equals(name)) ? String.format("application-%s.properties", name) : "application.properties";
        PropertiesPropertySourceLoader sourceLoader = new PropertiesPropertySourceLoader();
        Resource classPathResource = new ClassPathResource(propertiesName);
        
        try {
        	propertySources.addLast(sourceLoader.load(name, classPathResource).get(0));
        } catch (Exception e) {
        	log.error("Can't load internal properties. : ", e);
        }
    }
    
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    	PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
        MutablePropertySources propertySources = new MutablePropertySources();
        
        // 공통 properties
        loadInnerPropertySource(propertySources, "default");
        // 프로파일의 properties
        loadExternalPropertySource(propertySources, System.getProperty("spring.profiles.active"));
        configurer.setPropertySources(propertySources);
        configurer.setIgnoreResourceNotFound(true);
        return configurer;
    }
}

 

먼저 외부의 파일이 없는 상황에선 exception이 발생하고 내부 파일을 읽으므로 결과가 동일하다.

[내부 a 프로퍼티 파일]
2020-02-18 09:48:17 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.a : inner_a
2020-02-18 09:48:17 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.num : 123

 

이제 /profile-test 밑에 a.properties 파일을 만들면 외부 파일을 읽으므로 다른 결과가 나온다.

아래와 같이 ConfigA의 필드에 주입된 값들이 달라진 걸 확인할 수 있다.

[외부 a 프로퍼티 파일]
2020-02-18 09:49:12 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.a : outer_a
2020-02-18 09:49:12 [http-nio-30005-exec-1] INFO  c.t.t.p.service.ServiceProfileAImpl - property.num : 456

 

 (티스토리 코드블럭 서식 왜저러냐 작성할 땐 맞았는데 저장하면 달라져...)

Comments