MyAotoConfiguration.Imports
@Conditional
Class 조건 체크
@Conditional
@Bean
조건 체크
Configuration
클래스가 적용이 되면 하위 @Bean
메서드의 조건 체크MissingBean = false
@Bean
구성정보가 있다면 사용MissingBean = true
@MyAutoConfiguration
+ @Bean
구성 정보StandardEnvironment
StandardServletEnvironment
@PropertySource
- 커스텀하게 프로퍼티를 추가할 수 있는 애너테이션ApplicationRunner
인터페이스를 이용하면 된다.@FunctionalInterface
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
run
메서드를 호출한다.
@MySpringBootApplication
public class HellobootApplication {
@Bean
ApplicationRunner applicationRunner(Environment env) {
return args -> {
String name = env.getProperty("my.name");
System.out.println("my.name: " + name);
};
}
public static void main(String[] args) {
SpringApplication.run(HellobootApplication.class, args);
}
}
setter
메서드를 통해 변경할 수 있다.@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory() {
TomcatServletServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath("/app");
return factory;
}
}
@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory(Environment env) {
TomcatServletServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(env.getProperty("contextPath");
return factory;
}
}
@Value
어노테이션으로 프로퍼티 파일의 env 값을 주입받을 수 있다.@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Value("${contextPath}")
String contextPath;
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory(Environment env) {
TomcatServletServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(this.contextPath);
return factory;
}
}
@MyAutoConfiguration
public class PropertyPlaceholderConfig {
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
PropertySourcesPlaceholderConfigurer
는 BeanFactoryPostProcessor
의 구현체다.
MyAutoConfiguration.Imports
에 추가해준다.tobyspring.config.autoconfig.DispatcherServletConfig
tobyspring.config.autoconfig.TomcatWebServerConfig
tobyspring.config.autoconfig.JettyWebServerConfig
tobyspring.config.autoconfig.PropertyPlaceholderConfig
@Value
어노테이션을 너무 많이 정의해야 한다.
@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Value("${contextPath}")
String contextPath;
@Value("${port:8080}") // 디폴트값이 필요한 경우
int port;
// ... 더 많은 프로퍼티?
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory(Environment env) {
TomcatServletServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(this.contextPath);
factory.setContextPath(this.port);
return factory;
}
}
@Getter @Setter
public class ServerProperties {
private String contextPath;
private int port;
}
@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory(ServerProperties properties) {
TomcatServletServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(properties.getContextPath());
factory.setContextPath(properties.getPort());
return factory;
}
}
ServerProperties
에 값을 채우고, 스프링 빈으로 만들 필요가 있다.
Binder
를 통해 프로퍼티 이름과 클래스의 필드 이름을 매칭하여 바인딩할 수 있다.import org.springframework.boot.context.properties.bind.Binder;
@MyAutoConfiguration
public class ServerPropertiesConfig {
@Bean
public ServerProperties serverProperteis(Environment environment) {
return Binder.get(environment).bind("", ServerProperteis.class).get();
}
}
Binder
를 통한 Config
클래스를 같이 계속 만들어야 하는 건 번거롭다.
Config
클래스도 Conditional
에 따라 조건을 다르게 구성하는 작업도 복잡해질 수 있다.ServerPropertiesConfig
없이 처리할 수 없을까?@Component
어노테이션으로 ServerProperteis
를 빈으로 만들고 TomcatWebServerConfig
에서 ServerProperteis
를 @Import
하면 빈 주입은 가능하다.
ServerProperties
내부에 프로퍼티 값을 바인딩 하지는 못한다.ServerProperties
에 Binder
를 이용해 프로퍼티를 바인딩하는 로직은 빈 후처리기로 처리할 수 있다.@MyAutoConfiguration
public class PropertyPostProcessorConfig {
@Bean
BeanPostProcessor propertyPostProcessor(Environmnet 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;
return Binder.get(env).bindOrCreate("", bean.getClass());
}
}
}
}
@Getter @Setter
@MyConfigurationProperties // 내부에 @Component도 존재
public class ServerProperties {
private String contextPath;
private int port;
}
port
같은 프로퍼티 이름은 다른 곳에서도 많이 쓰는 이름이라 그냥 사용하면 겹칠 수도 있다.prefix
를 붙여 구분하려면 아래와 같이 어노테이션의 attribute
로 설정하는 방법이 있다.
application.properties
에도 server.contextPath
, server.port
로 적어줘야 한다.@Getter @Setter
@MyConfigurationProperties(prefix = "server")
public class ServerProperties {
private String contextPath;
private int port;
}
prefix
를 다룰 수 있도록 한다.@MyAutoConfiguration
public class PropertyPostProcessorConfig {
@Bean
BeanPostProcessor propertyPostProcessor(Environmnet 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());
}
};
}
}