yml
을 이용해서 application.yml
로 지정한다.YamlPropertiesFactoryBean
PropertiesFactoryBean
YamlPropertySourceLoader
PropertiesPropertySourceLoader
EXTERNAL_USERNAME/EXTERNAL_PASSWORD
환경 변수 혹은 external.username/external.password
프라퍼티가 존재하면 그 값을 사용하고 그게 아니면 :
뒤에 지정된 값을 사용한다.testing: my: username: ${external.username:system} password: ${external.password:test}
@Value
로 값을 읽는 경우에만 지정 가능하며, @ConfigurationProperties
를 사용할 때는 안 된다. 혼란스럽기 때문에 안 사용하는게 나을듯 하다.testing: my: username: ${external.username:user} password: ${external.password:#{' test'}}
@Value("${testing.my.username}") private String username; @Value("${testing.my.password}") private String password;
application-profilename.yml
로 파일을 만들 수도 있고, ---
로 구분해서 프로필을 지정할 수도 있다.application.yml
에서 profile을 지정하지 않은 프라퍼티는 공통 디폴트 값 역할을 한다. 각 프로필에서 오버라이드하면 변경되고 아니면 디폴트 값이 사용된다.a: b: c: default value --- spring.profiles: local # 기본값 그대로 사용 --- spring.profiles: develop a: b: c: develop-value --- spring.profiles: production a: b: c: production-value
application.yml
spring.profiles: production spring.profiles.include: a,b # 혹은 spring.profiles: production spring: profiles: include: - a - b - c
production
profile 에서 자동으로 a
, b
, c
프로필도 활성화 시키는 역할을 한다.application-a.yml
, application-b.yml
형태로 만들어서 resources
에 두면 이를 읽게 된다.a
,b
,c
라는 프로필이 모르는 사이에 활성화 된다.spring.profiles: local
이런식으로 특정 프로파일을 지정하고 설정해도 먹지 않고 무시된다. a-develop
이런식으로 별도 프로필을 만들고 active: a-develop
처럼 더 추가해줘야만 한다.application.yml
이어야만 include 된 설정 안에서의 Profile 구분이 올바로 작동한다. 애초에 최초 시작 설정 파일을 application-prod.yml
로 한 상태에서 include는 그 안의 Profile 구분이 무시된다.env
객체에 addFirst()
혹은 addLast()
를 해주고 있는데, 이게 꼭 필요한지 단순히 PropertySource
객체를 리턴하는 것만으로 충분한지 확인 필요.ApplicationContextInitializer
로 만드는데 꼭 Initializer로 만들어야만 하는가?// 일반 Bean으로 만들어 주입 @Autowired private ConfigurableEnvironment env; @Bean @Order(-1) PropertySource consumerPropertySource() throws IOException { YamlPropertySourceLoader loader = new YamlPropertySourceLoader(); PropertySource propertySource = loader.load("consumers", new ClassPathResource("consumer.yml"), "develop"); env.getPropertySources().addLast(propertySource); return propertySource; }
// ApplicationContextInitializerApplicationContextInitializer 구현 @Override public void initialize(ConfigurableApplicationContext applicationContext) { try { Resource resource = applicationContext.getResource("classpath:application.yml"); YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader(); PropertySource<?> yamlTestProperties = sourceLoader.load("yamlTestProperties", resource, null); applicationContext.getEnvironment().getPropertySources().addLast(yamlTestProperties); } catch (IOException e) { throw new RuntimeException(e); } }
Property
객체로 적재할 수 있다. PropertySource
로 등록되는 것은 아니므로 헷갈리면 안된다.@Value(“#propertyObjectName['key']”)
형태로 사용가능해 진다.@PropertySource( value = "classpath:foo.yml", factory = YamlPropertySourceFactory.class )
public class YamlPropertySourceFactory implements PropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) throws IOException { YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); factory.setResources(encodedResource.getResource()); Properties properties = factory.getObject(); return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties); } }
@EnableConfigurationProperties
가 설정돼 있어야 한다.compile "org.springframework.boot:spring-boot-configuration-processor"
@ConfigurationProperties
애노테이션이 붙은 클래스에 직접 @Configuration
을 붙여서 생성하거나, 별도로 @Bean
으로 생성하면 자동으로 값이 차서 Bean 으로 생성된다.javax.validation
애노테이션을 걸어주면 자동 validation이 작동한다.@Component public class LoadedConfigFileListener implements ApplicationListener<ApplicationReadyEvent>, Ordered { @Override public void onApplicationEvent(ApplicationReadyEvent event) { MutablePropertySources propertySources = event.getApplicationContext().getEnvironment().getPropertySources(); Iterator<PropertySource<?>> propertySourceIterator = propertySources.iterator(); propertySourceIterator.forEachRemaining(propertySource -> log.info("Successfully loaded: \"{}\" ({}) into application context", propertySource.getName(), propertySource.getSource())); } @Override public int getOrder() { return ConfigFileApplicationListener.DEFAULT_ORDER + 1; } }
ContextRefreshEvent
를 받는 @EventListener
Bean을 등록해주고, 거기서 로깅하면 된다.@Component public class PropertyLogger { private static final Logger LOGGER = LoggerFactory.getLogger(PropertyLogger.class); @EventListener public void handleContextRefresh(ContextRefreshedEvent event) { final Environment env = event.getApplicationContext().getEnvironment(); LOGGER.info("====== Environment and configuration ======"); LOGGER.info("Active profiles: {}", Arrays.toString(env.getActiveProfiles())); final MutablePropertySources sources = ((AbstractEnvironment) env).getPropertySources(); StreamSupport.stream(sources.spliterator(), false) .filter(ps -> ps instanceof EnumerablePropertySource) .map(ps -> ((EnumerablePropertySource) ps).getPropertyNames()) .flatMap(Arrays::stream) .distinct() .filter(prop -> !(prop.contains("credentials") || prop.contains("password"))) .forEach(prop -> LOGGER.info("{}: {}", prop, env.getProperty(prop))); LOGGER.info("==========================================="); } }
spring.config.additional-location=classpath:/custom-config,file:./custom-config
spring.application.json={“keyname”:“value”}
SPRING_APPLICATION_JSON={“keyname”:“value”}
META-INF/spring.factories
에 등록해야 한다.org.springframework.boot.env.EnvironmentPostProcessor= com.baeldung.environmentpostprocessor.PriceCalculationEnvironmentPostProcessor
100ms
형태로 시간 단위를 문자로 지정하면 된다.ns
for nanosecondsus
for microsecondsms
for millisecondss
for secondsm
for minutesh
for hoursd
for days