잡동사니를 모아두는 서랍장
PropertySourcesPlaceholderConfigurer로 프로퍼티로 지정하기 본문
프로파일에 따라 프로퍼티가 다르고, 프로퍼티 파일이 외부에 있는 상황일 수도 있는 상황이 있었다.
단순히 파일을 확인해서 긁어 오거나 @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
(티스토리 코드블럭 서식 왜저러냐 작성할 땐 맞았는데 저장하면 달라져...)
'Spring' 카테고리의 다른 글
Request Body를 따로 읽고 싶을 때 (0) | 2020.04.21 |
---|---|
Request Body를 인터셉터에서 읽고 싶을 때 (0) | 2020.04.20 |
스프링의 간단한 Async 처리 (0) | 2020.02.27 |
인터셉터와 필터 차이 (0) | 2020.02.23 |
스프링 프로퍼티 우선 순위 (0) | 2020.02.07 |