1. 자동구성
1). @Conditional
- 특정 조건에서만 해당 기능을 활성화 할 수 있도록 하는 것
@Slf4j
public class MemoryCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String memory = context.getEnvironment().getProperty("memory");
log.info("memory = {}",memory);
return "on".equals(memory);
}
}
@Configuration
@ConditionalOnProperty(name = "memory",havingValue = "on")
public class MemoryConfig {
@Bean
public MemoryController memoryController(){
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder(){
return new MemoryFinder();
}
}
2). ImportSelector
- 정적인 방법: @Import(클래스)
- 동적인 방법: @Import(ImportSelector)
public class HelloImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"hello.selector.HelloConfig"};
}
}
@Test
void staticConfig(){
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(StaticConfig.class);
HelloBean bean = ac.getBean(HelloBean.class);
}
@Test
void selectorConfig(){
AnnotationConfigApplicationContext ac =
new AnnotationConfigApplicationContext(SelectorConfig.class);
HelloBean bean = ac.getBean(HelloBean.class);
}
@Configuration
@Import(HelloImportSelector.class)
public static class SelectorConfig{
}
@Configuration
@Import(HelloConfig.class)
public static class StaticConfig{
}
3. 외부설정
1). 외부 설정 방법
- OS 환경변수: 해당 OS를 사용하는 모든 프로세스에서 사용
- 자바 시스템 속성: 자바에서 지원하는 외부설정, 해당 JVM안에서 사용
- 자바 커맨드 라인 인수: main(args) 메서드에서 사용
- 외부파일: 프로그램에서 외부 파일을 직접 읽어서 사용
Map<String, String> envMap = System.getenv();
String dburl = System.getenv("DBURL");
Properties properties = System.getProperties();
String url = System.getProperty("url");
String username = System.getProperty("username");
String password = System.getProperty("password");
ApplicationArguments appArgs = new DefaultApplicationArguments(args);
Set<String> optionNames = appArgs.getOptionNames();
List<String> username = appArgs.getOptionValues("username");
@Slf4j
@Component
public class CommandLineBean {
private final ApplicationArguments arguments;
public CommandLineBean(ApplicationArguments arguments) {
this.arguments = arguments;
}
2). Environment
- 이 방법들 모두 키밸류 형식으로 읽는것이다. 그래서 추상화를 이용했다
- 모든 외부설정을 Environment를 이용한다
@Slf4j
@Component
public class EnvironmentCheck {
private final Environment env;
public EnvironmentCheck(Environment env) {
this.env = env;
}
@PostConstruct
public void init() {
String url = env.getProperty("url");
String username = env.getProperty("username");
}
3). 파일로 설정
application-dev.properties
application-prod.properties
만들어서 Run/Debug Configurations에서 --spring.profiles.active=dev 넣고 실행
spring.config.activate.on-profile=dev
url=prod.db.com
username=prod_user
password=prod_pw
#--- >> 이걸로 구분
spring.config.activate.on-profile=prod
url=dev.db.com
username=dev_user
password=dev_pw
(1). 우선순위
- 위에서 아래로 순서대로 읽으면서 값을 설정한다. 이때 기존 데이터가 있으면 덮어쓴다
- 만약 dev,prod둘다 있다고 치면 디폴트 값을 저장했다가 dev 값들로 바껴버리고 최종적으로 prod값들로 변경된다
url=local.db.com
username=local_user
password=local_pw
#---
spring.config.activate.on-profile=dev
url=prod.db.com
username=prod_user
password=prod_pw
#---
spring.config.activate.on-profile=prod
url=dev.db.com
username=dev_user
password=dev_pw
#---
url=hello.db.com
4. 외부설정 읽기
1). 스프링이 지원하는 다양한 외부 설정 조회 방법
- Environment
- @Value
- @ConfigurationProperties
my.datasource.url=local.db.com
my.datasource.username=username
my.datasource.password=password
my.datasource.etc.max-connection=1
my.datasource.etc.timeout=3500mx
my.datasource.etc.options=CACHE,ADMIN
String url = env.getProperty("my.datasource.url");
Integer maxConnection = env.getProperty("my.datasource.etc.max-connection", Integer.class);
Duration timeout = env.getProperty("my.datsource.etc.timeout", Duration.class);
@Value("${my.datasource.url}")
private String url;
@Value("${my.datasource.username}")
private String username;
@Value("${my.datasource.password}")
private String password;
@Value("${my.datasource.etc.maxConnection}")
private int maxConnection;
@Value("${my.datasource.etc.timeout}")
private Duration timeout;
@Value("${my.datasource.etc.options}")
private List<String> options;
@Value("${my.datasource.etc.maxConnection:2}")
- @ConfigurationProperties => Validation, Default 사용가능
@Data
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertyV1 {
private String url;
private String username;
private String password;
private Etc etc;
@Data
public static class Etc{
private int maxConnection;
private Duration timeout;
private List<String> options = new ArrayList<>();
}
}
@Slf4j
@EnableConfigurationProperties(MyDataSourcePropertyV1.class)
public class MyDataSourceConfigV1 {
private final MyDataSourcePropertyV1 properties;
public MyDataSourceConfigV1(MyDataSourcePropertyV1 properties) {
this.properties = properties;
}
@Bean
public MyDataSource dataSource(){
return new MyDataSource(properties.getUrl(),properties.getUsername(),...)
}
}
@SpringBootApplication
@ConfigurationPropertiesScan
public class ExternalReadApplication {
public static void main(String[] args) {
SpringApplication.run(ExternalReadApplication.class, args);
}
}
@Getter
@ConfigurationProperties("my.datasource")
public class MyDataSourcePropertyV2 {
@NotEmpty
private String url;
private String username;
private String password;
private Etc etc;
public MyDataSourcePropertyV2(String url, String username, String password, Etc etc) {
this.url = url;
this.username = username;
this.password = password;
this.etc = etc;
}
@Getter
public static class Etc{
private int maxConnection;
private Duration timeout;
private List<String> options = new ArrayList<>();
public Etc(int maxConnection, Duration timeout, @DefaultValue("default") List<String> options) {
this.maxConnection = maxConnection;
this.timeout = timeout;
this.options = options;
}
}
}
2). @Profile
- 스프링 빈 등록되는 시점에 profile등록
- Spring.config.activate.on-profile: prod 똑같이 profile로 된걸 쓴다
@Slf4j
@Configuration
public class PayConfig {
@Bean
@Profile("default")
public LocalPayClinet localPayClinet(){
return new LocalPayClient();
}
@Bean
@Profile("prod")
public ProdPayClinet localPayClinet(){
return new ProdPayClient();
}
}