Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hyeonsik/spring-boot-project/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation('org.springframework.boot:spring-boot-starter-web')
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package tobySpringBoot.config;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Conditional(MyOnClassCondition.class)
public @interface ConditionalMyOnClass {
String value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package tobySpringBoot.config;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(MyConfigurationPropertiesImportSelector.class)
public @interface EnableMyConfigurationProperties {
Class<?> value();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package tobySpringBoot.config;

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) // marking하는 역할이므로
@Component
public @interface MyConfigurationProperties {
String prefix();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tobySpringBoot.config;

import org.springframework.context.annotation.DeferredImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.MultiValueMap;

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() };
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package tobySpringBoot.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;

import java.util.Map;

public class MyOnClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalMyOnClass.class.getName());
String value = (String) annotationAttributes.get("value");
return ClassUtils.isPresent(value, context.getClassLoader());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package tobySpringBoot.config.autoconfig;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils;
import tobySpringBoot.config.ConditionalMyOnClass;
import tobySpringBoot.config.MyAutoConfiguration;

@MyAutoConfiguration
@ConditionalMyOnClass("org.eclipse.jetty.server.Server")
public class JettyWebServerConfig {
@Bean("jettyWebServerFactory") // bean name은 기본적으로 method 명을 따라간다. 동일 이름이 있으면 충돌이 날 수 있으니 이름을 구분!!
@ConditionalOnMissingBean
public ServletWebServerFactory servletWebServerFactory() {
return new JettyServletWebServerFactory();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package tobySpringBoot.config.autoconfig;

import org.springframework.context.annotation.Bean;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import tobySpringBoot.config.MyAutoConfiguration;
import tobySpringBoot.config.MyConfigurationProperties;

@MyAutoConfiguration
public class PropertyPlaceholderConfig {
@Bean
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package tobySpringBoot.config.autoconfig;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import tobySpringBoot.config.MyAutoConfiguration;
import tobySpringBoot.config.MyConfigurationProperties;

import java.util.Map;

@MyAutoConfiguration
public class PropertyPostProcessorConfig {
@Bean
BeanPostProcessor propertyPostProcessor(Environment env) {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 우리가 만든 marker annotation만 이를 적용할 것이다. (MyConfigurationProperties)
MyConfigurationProperties annotation = AnnotationUtils.findAnnotation(bean.getClass(), MyConfigurationProperties.class);
if (annotation == null) return bean;

Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(annotation); // 애노테이션에 있는 모든 value를 찾는다.
String prefix = (String) attrs.get("prefix"); // 그 중 prefix 로 시작하는 녀석을 확인한다.

return Binder.get(env).bindOrCreate(prefix, bean.getClass());
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package tobySpringBoot.config.autoconfig;

import org.springframework.beans.factory.annotation.Value;
import tobySpringBoot.config.MyConfigurationProperties;

// 그저 데이터를 가지고 있는 데이터 홀더 클래스임 -> 그러니 그냥 게터세터로
@MyConfigurationProperties(prefix = "server")
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;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package tobySpringBoot.config.autoconfig;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.*;
import tobySpringBoot.config.ConditionalMyOnClass;
import tobySpringBoot.config.EnableMyConfigurationProperties;
import tobySpringBoot.config.MyAutoConfiguration;

@MyAutoConfiguration
@ConditionalMyOnClass("org.apache.catalina.startup.Tomcat")
@EnableMyConfigurationProperties(ServerProperties.class)
public class TomcatWebServerConfig {
@Bean
public ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory();
@Bean("tomcatWebServerFactory")
@ConditionalOnMissingBean // 이미 동일한 타입의 빈이 등록되어있는가? 그렇지 않다면 빈으로 등록해줘라! 라는 condition
public ServletWebServerFactory servletWebServerFactory(ServerProperties properties) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setContextPath(properties.getContextPath());
factory.setPort(properties.getPort());
return factory;
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package tobySpringBoot.learn;

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import tobySpringBoot.config.MySpringBootApplication;

@MySpringBootApplication
public class LearnApplication {
public static void main(String[] args) {
MySpringApplication.run(LearnApplication.class, args);
}
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
tobySpringBoot.config.autoconfig.PropertyPlaceholderConfig
tobySpringBoot.config.autoconfig.PropertyPostProcessorConfig
tobySpringBoot.config.autoconfig.TomcatWebServerConfig
tobySpringBoot.config.autoconfig.JettyWebServerConfig
tobySpringBoot.config.autoconfig.DispatcherServletConfig
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

server.contextPath=/app
server.port=9090
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package tobySpringBoot.study;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.*;
import org.springframework.core.type.AnnotatedTypeMetadata;

import java.beans.BeanProperty;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Map;

//import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

public class ConditionalTest {
@Test
void conditional() {
// // true
// ApplicationContextRunner contextRunner = new ApplicationContextRunner();
// contextRunner.withUserConfiguration(Config1.class)
// .run(context -> {
// assertThat(context).hasSingleBean(MyBean.class);
// assertThat(context).hasSingleBean(Config1.class);
// });
//
// // false
// new ApplicationContextRunner().withUserConfiguration(Config1.class)
// .run(context -> {
// assertThat(context).doesNotHaveBean(MyBean.class);
// assertThat(context).doesNotHaveBean(Config1.class);
// });

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Conditional(BooleanCondition.class)
@interface BooleanConditional {
boolean value();
}

@Configuration
@BooleanConditional(true)
static class Config1 { // <- static으로 선언하면 inner class 처럼 취급되지 않는다. 대신 상위 클래스가 마치 package 역할을 한다.
@Bean
MyBean myBean() {
return new MyBean();
}
}

@Configuration
@BooleanConditional(false)
static class Config2 {
@Bean
MyBean myBean() {
return new MyBean();
}
}

static class MyBean {
}

static class BooleanCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BooleanConditional.class.getName());
return (Boolean) annotationAttributes.get("value");
}
}

}