dependencies {
implementation 'org.springframework.boot::spring-boot-starter-web'
}
dependencies {
implementation 'org.springframework.boot::spring-boot-starter-web'
implementation 'org.springframework.boot::spring-boot-starter-jetty'
}
TomcatServletWebServerFactory 빈 이름과 충돌을 피하기 위해 이름을 따로 지정@MyAutoConfiguration
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
public ServletWebServerFactory servrWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
@MyAutoConfiguration
public class JettyWebServerConfig {
@Bean("jettyWebServerFactory")
public ServletWebServerFactory servletWebServerFactory() {
return new JettyServletWebServerFactory();
}
}
tobyspring.config.MyAutoConfiguration.imports 내부에도 jetty 설정을 추가tobyspring.config.autoconfig.DispatcherServletConfig
tobyspring.config.autoconfig.TomcatWebServerConfig
tobyspring.config.autoconfig.JettyWebServerConfig
ServletWebServerFactory가 존재하기 때문@Conditional을 사용할 수 있다.@Conditional은 Condition 인터페이스 구현체 클래스를 엘리먼트로 가진다.@MyAutoConfiguration
@Conditional(JettyWebserverConfig.JettyCondition.class)
public class JettyWebServerConfig {
@Bean("jettyWebServerFactory")
public ServletWebServerFactory servletWebServerFactory() {
return new JettyServletWebServerFactory();
}
// boolean 반환 여부에 따라 빈을 띄우거나 띄우지 않음
static class JettyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true
}
}
}
TomcatWebServerConfig에는 반대로 false를 반환하는 Condition 구현체를 @Conditional로 등록하면 Jetty 서버가 뜨게 된다.
true르 반환하면 이전과 똑같은 이유로 애플리케이션이 뜨지 않는다.Tomcat.java 클래스 자체가, Jetty의 경우 Server.java 클래스가 jetty.server 패키지 하위에 존재한다.Spring에는 ClassUtils 클래스가 존재하는데 이걸 사용해서 특정 클래스가 존재하는지를 알 수 있다.@MyAutoConfiguration
@Conditional(TomcatWebServerConfig.TomcatCondition.class)
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
public ServletWebServerFactory servrWebServerFactory() {
return new TomcatServletWebServerFactory();
}
static class TomcatCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent("org.apache.catalina.startup.Tomcat",
context.getClassLoader());
}
}
}
@MyAutoConfiguration
@Conditional(JettyWebserverConfig.JettyCondition.class)
public class JettyWebServerConfig {
@Bean("jettyWebServerFactory")
public ServletWebServerFactory servletWebServerFactory() {
return new JettyServletWebServerFactory();
}
static class JettyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent("org.eclipse.jetty.server.Server",
context.getClassLoader());
}
}
}
dependencies {
implementation 'org.springframework.boot::spring-boot-starter-web' {
exclude group: 'org.springframework.boot', module: 'sppring-boot-starter-tomcat'
}
implementation 'org.springframework.boot::spring-boot-starter-jetty'
}
Conditional을 메타 어노테이션으로 만들어 사용할 수 있다.
@Rentention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(MyOnClassCondition.class)
public @interface ConditionalMyOnClass {
String value(); // 있는지 체크할 클래스 이름
}
public class MyOnClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> attrs = metadata.getAnnotationAttribute(ConditionalMyOnClass.class());
String value = (String) attrs.get("value");
return ClassUtils.isPresent("value", context.getClassLoader());
}
}
@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
public ServletWebServerFactory servrWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
@MyAutoConfiguration
@ConditionalMyOnClass("org.eclipse.jetty.server.Server")
public class JettyWebServerConfig {
@Bean("jettyWebServerFactory")
public ServletWebServerFactory servletWebServerFactory() {
return new JettyServletWebServerFactory();
}
}
tobyspring.helloboot 패키지 하위에 WebServerConfiguration 클래스를 만든다.
@Configuration(proxyBeanMethods = false)
public class WebServerConfiguration {
@Bean
ServletWebServerFacotry customerWebServerFacotry() {
TomcatServletWebServerFactory sf = new TomcatServletWebServerFactory();
sf.setPort(9090);
return sf;
}
}
@ConditionalOnMissingBean로 이미 동일 타입 빈이 존재하면 뜨지 않도록 설정할 수 있다.
DefaultImportSelecter를 사용했는데 이는 사용자 구성 정보가 로딩이 된 뒤 자동 구성 정보를 구성한다.@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
public class TomcatWebServerConfig {
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean
public ServletWebServerFactory servrWebServerFactory() {
return new TomcatServletWebServerFactory();
}
}
@Profile도 @Conditional 애노테이션이다.@Conditional(ProfileCondition.class)
public @interface Profile {
// ...
}
@Conditional 애노테이션과 Condition을 제공한다.
@ConditionalOnClass@ConditionalOnMissingClass@Configuration 클래스 레벨에서 사용하지만 @Bean 메서드에도 적용 가능
@Bean 메서드에만 적용하면 불필요하게 @Configuration 클래스가 빈으로 등록되기에 클래스 레벨 사용을 우선하면 좋다.@ConditionalOnBean@ConditionalOnMissingBean@Configuration 클래스 적용 순서가 중요
@Configuration 클래스 레벨의 @ConditionalOnClass와 @Bean 메서드 레벨의 @ConditionalOnMissingBean 조합이 대표적@ConditionalOnProperty
false가 아니면 포함 대상이 된다.@ConditionalOnResource
@ConditionalOnWebApplication@ConditionalOnNotWebApplication@ConditionalOnExpression