๐งพ[์คํ๋ง ๋ถํธ - ์ดํด์ ์๋ฆฌ] ์ธ๋ถ ์ค์ ์ ํ์ฉํ๋ ์๋ ๊ตฌ์ฑ
์คํ๋ง์ Environment ์ถ์ํ
์คํ๋ง์ Environment ์ถ์ํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๊ฐ์ง ํ๊ฒฝ ์ ๋ณด ๋ชจ๋ธ์ธ profile๊ณผ properties ๋ฅผ ์ ๊ณตํ๋ค.
์๋ ๊ตฌ์ฑ ์ ๋ณด์ ์ผ๋ถ ๋ด์ฉ์ ๋ณ๊ฒฝํ๊ฑฐ๋ ์ค์ ํด์ผํ ๋ Environment๋ฅผ ํตํด์ ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ ธ์ ํ์ฉํ ์ ์๋ค. ์ปค์คํฐ ๋น ๋ฑ๋ก์ ํ๋ ๋ฐฉ๋ฒ์ ๋นํด์ ๊ฐ๋จํ๊ฒ ์๋ ๊ตฌ์ฑ์ ๋ํดํธ ์ค์ ์ ๋ณ๊ฒฝํ๋ ๊ฒ ๊ฐ๋ฅํ๋ค.
ํ๋กํผํฐ ์ ๋ณด๋ ์์คํ ํ๋กํผํฐ, ํ๊ฒฝ ๋ณ์, ์๋ธ๋ฆฟ ํ๋ผ๋ฏธํฐ, JNDI ๋ฑ์์ ์ฐ์ ์์์ ๋ฐ๋ผ์ ๊ฐ์ ธ์จ๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์์ @PropertySource๋ก ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ ธ์ฌ ๋์์ ์ง์ ํ ์ ์๋ค.
์คํ๋ง ๋ถํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก application.properties, application.xml, application.yml ๋ฑ์ ํ์ผ์์ ํ๋กํผํฐ๋ฅผ ์ฝ์ด์ค๋ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ค.
์๋ ๊ตฌ์ฑ์ Environment ํ๋กํผํฐ ์ ์ฉ
์คํ๋ง ๋ถํธ์ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ธฐํ ์์ ์ด ๋๋๊ณ ๋๋ฉด ์คํ๋๋ ์ฝ๋๋ฅผ ๋ง๋ค ๋ ApplicationRunner ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ ์ค๋ธ์ ํธ ๋๋ ๋๋ค์์ ๋น์ผ๋ก ๋ฑ๋กํ๋ฉด ๋๋ค.
@Bean
ApplicationRunner applicationRunner(Environment environment) {
return args -> {
String name = environment.getProperty("my.name");
System.out.println("my.name: " + name);
};
}
์๋ ๊ตฌ์ฑ ํด๋์ค์ ๋ฉ์๋์๋ Environment๋ฅผ ์ฃผ์ ๋ฐ์์ ๋น ์์ฑ์ผ๋ก ์ง์ ํ ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory(Environment env) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(env.getProperty("contextPath"));
return factory;
}
@Value์ PropertySourcesPlaceholderConfigurer
@Value ์ ๋ ธํ ์ด์ ์ ์๋ฆฌ๋จผํธ๋ก ์นํ์(placeholder)๋ฅผ ์ง์ ํ๊ณ ์ปจํ ์ด๋ ์ด๊ธฐํ์ ํ๋กํผํฐ ๊ฐ์ผ๋ก ์ด๋ฅผ ๋์ฒดํ ์ ์๋ค.
@Value์ ์นํ์๋ฅผ ํ๋กํผํฐ ๊ฐ์ผ๋ก ๊ต์ฒดํ๋ ค๋ฉด PropertySourcesPlaceholderConfigurer ํ์ ์ ๋น์ ๋ฑ๋กํด์ค์ผ ํ๋ค. PropertySourcesPlaceholderConfigurer๋ ๋น ํฉํ ๋ฆฌ์ ํ์ฒ๋ฆฌ๊ธฐ๋ก ๋์ํด์ ์ด๊ธฐ ๊ตฌ์ฑ ์ ๋ณด์์ ์นํ์๋ฅผ ์ฐพ์์ ๊ต์ฒดํ๋ ๊ธฐ๋ฅ์ ๋ด๋นํ๋ค.
PropertySourcesPlaceholderConfigurer๋ ์๋ ๊ตฌ์ฑ ๋น์ผ๋ก ๋ฑ๋ก๋๊ฒ ํ๋ค.
@MyAutoConfiguration
public class PropertyPlaceholderConfig {
@Bean PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
ํ๋กํผํฐ ํด๋์ค์ ๋ถ๋ฆฌ
์๋ ๊ตฌ์ฑ์ ์ ์ฉํ ํ๋กํผํฐ์ ๊ฐฏ์๊ฐ ๋ง์์ง๊ณ ํ๋กํผํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ก์ง์ด ์ถ๊ฐ๋์ผ ํ๋ค๋ฉด, ํ๋กํผํฐ๋ฅผ ๋ค๋ฃจ๋ ๊ธฐ๋ฅ์ ๋ณ๋์ ํด๋์ค๋ก ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค.
๊ธฐ๋ณธ์ ์ธ ํ๋กํผํฐ ํด๋์ค๋ ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ง๊ณ ์๋ ๋จ์ํ ํด๋์ค๋ก ์์ฑํ ์ ์๋ค.
public class ServerProperties {
private String contextPath;
private int port;
public String getContextPath() {
return contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = contextPath;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
์ด ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๋ ์๋ ๊ตฌ์ฑ ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ค. Environment์์ ํ๋กํผํฐ ๊ฐ์ ๊ฐ์ ธ์ ์ค๋ธ์ ํธ์ ์ฃผ์ ํ๋ ๊ฒ์ ์คํ๋ง๋ถํธ์ Binder ํด๋์ค๋ฅผ ์ด์ฉํ๋ฉด ํธ๋ฆฌํ๋ค.
@MyAutoConfiguration
public class ServerPropertiesConfig {
@Bean
public ServerProperties serverProperties(Environment environment) {
return Binder.get(environment).bind("", ServerProperties.class).get();
}
}
ํ๋กํผํฐ ํด๋์ค๋ก ๋ง๋ ๋น์ ์๋ ๊ตฌ์ฑ ๋น์ ๋ง๋ค ๋ ์ฃผ์ ๋ฐ์์ ์ฌ์ฉํ๋ค.
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory(ServerProperties properties) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(properties.getContextPath());
factory.setPort(properties.getPort());
return factory;
}
ํ๋กํผํฐ ๋น์ ํ์ฒ๋ฆฌ๊ธฐ ๋์
ํ๋กํผํฐ ํด๋์ค๋ฅผ ๋น ๋ฑ๋ก์ ์ํ ์๋ ๊ตฌ์ฑ์ ๋ฐ๋ก ๋ง๋๋ ํ์ํ ๊ณณ์์ @Import ํด์ ์ฌ์ฉํ ์ ์๋ค.
@MyConfigurationProperties ๋ผ๋ ๋ง์ปค ์ ๋ ธํ ์ด์ ์ ๋ง๋ค๊ณ , ์ด๋ฅผ BeanPostProcessor๋ฅผ ๋ง๋ค์ด์ ๋น ์ค๋ธ์ ํธ ์์ฑ ํ์ ํ์ฒ๋ฆฌ ์์ ์ ์งํ์ํฌ ์ ์๋ค.
public interface BeanPostProcessor {
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
๋ง์ปค ์ ๋ ธํ ์ด์ ์ ์ฐพ์์ ํ๋กํผํฐ๋ฅผ ์ฃผ์ ํ๋ ๊ธฐ๋ฅ์ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ๋ง๋ค๊ณ ์๋ ๊ตฌ์ฑ์ผ๋ก ๋ฑ๋ก๋๊ฒ ํ๋ค.
@MyAutoConfiguration
public class PropertyPostProcessorConfig {
@Bean BeanPostProcessor propertyPostProcessor(Environment env) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
MyConfigurationProperties annotation = findAnnotation(bean.getClass(), MyConfigurationProperties.class);
if (annotation == null) return bean;
Map<String, Object> attrs = getAnnotationAttributes(annotation);
String prefix = (String) attrs.get("prefix");
return Binder.get(env).bindOrCreate(prefix, bean.getClass());
}
};
}
}
๋ง์ปค ์ ๋ ธํ ์ด์ ์ prefix ์๋ฆฌ๋จผํธ๋ฅผ ์ง์ ํ๊ฒ ํ๊ณ , ์ด๋ฅผ ์ด์ฉํด์ ํ๋กํผํฐ ์ด๋ฆ ์์ ์ ๋์ด๋ฅผ ๋ถ์ด๋๋ก ํ ์ ์๋ค.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Component
public @interface MyConfigurationProperties {
String prefix();
}
์ ๋ ธํ ์ด์ ๊ณผ ImportSelector๋ฅผ ์กฐํฉํด์ ์ ๋ ธํ ์ด์ ์ ์๋ฆฌ๋จผํธ ๊ฐ์ผ๋ก ์ง์ ํ ํด๋์ค๋ฅผ ๋น์ผ๋ก ๋ฑ๋กํ๋ ๋ฐฉ๋ฒ๋ ๊ฐ๋ฅํ๋ค.
public class MyConfigurationPropertiesImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
MultiValueMap<String, Object> attr = importingClassMetadata.getAllAnnotationAttributes(EnableMyConfigurationProperties.class.getName());
Class propertyClass = (Class) attr.getFirst("value");
return new String[] {propertyClass.getName()};
}
}
์ด๋๋ ๊ธฐ๋ณธ ์๋ฆฌ๋จผํธ ํ์ ์ Class ํ์ ์ผ๋ก ๋ง๋ค์ด ์ฌ์ฉํ๋ค.
๋๊ธ๋จ๊ธฐ๊ธฐ