일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 자바의 정석 기초편 ch1
- 스프링 mvc1 - 스프링 mvc
- 코드로 시작하는 자바 첫걸음
- 스프링 mvc2 - 로그인 처리
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch2
- 스프링 고급 - 스프링 aop
- 자바의 정석 기초편 ch14
- 스프링 mvc2 - 타임리프
- 자바의 정석 기초편 ch13
- 2024 정보처리기사 수제비 실기
- 타임리프 - 기본기능
- 자바의 정석 기초편 ch7
- 게시글 목록 api
- 자바의 정석 기초편 ch6
- jpa - 객체지향 쿼리 언어
- 2024 정보처리기사 시나공 필기
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch5
- 스프링 db2 - 데이터 접근 기술
- 스프링 mvc1 - 서블릿
- 자바의 정석 기초편 ch8
- 자바의 정석 기초편 ch11
- 스프링 mvc2 - 검증
- 스프링 입문(무료)
- jpa 활용2 - api 개발 고급
- @Aspect
- 자바의 정석 기초편 ch3
- 자바의 정석 기초편 ch4
- Today
- Total
나구리의 개발공부기록
자동 구성(Auto Configuration), 프로젝트 설정 및 예제 만들기, 자동 구성 확인, 스프링 부트의 자동 구성, 자동 구성 직접만들기 - 기반 예제, @Conditional, @Conditional - 다양한 기능 본문
자동 구성(Auto Configuration), 프로젝트 설정 및 예제 만들기, 자동 구성 확인, 스프링 부트의 자동 구성, 자동 구성 직접만들기 - 기반 예제, @Conditional, @Conditional - 다양한 기능
소소한나구리 2024. 12. 2. 15:10출처 : 인프런 - 스프링 부트 - 핵심 원리와 활용(유료) / 김영한님
유료 강의이므로 정리에 초점을 두고 코드는 일부만 인용
1. 프로젝트 설정 및 예제 만들기
1) 프로젝트 설정
(1) 프로젝트 설정 순서
- 제공된 프로젝트를 사용하였으나 build.gradle을 확인하여 직접 생성해도 상관없음
(2) build.gradle 확인
- 스프링 부트에서 롬복, 스프링 웹, H2 DB, JDBC API 라이브러리를 선택하여 생성
- 테스트 코드에서 롬복을 사용할 수 있도록 설정 추가
plugins {
id 'org.springframework.boot' version '3.0.2'
id 'io.spring.dependency-management' version '1.1.0'
id 'java'
}
group = 'hello'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//테스트에서 lombok 사용
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
}
tasks.named('test') {
useJUnitPlatform()
}
2) 예제 만들기
- JdbcTemplate을 사용하여 회원 데이터를 DB에 저장하고 조회하는 간단한 기능의 예제
(1) Member
- member패키지 생성하여 작성
- memberId, name 필드가 있는 회원 객체
- 기본 생성자와 member, name을 포함하는 생성자가 있으며 롬복의 @Data를 활용
package hello.member;
@Data
public class Member {
private String memberId;
private String name;
public Member() {}
public Member(String memberId, String name) {
this.memberId = memberId;
this.name = name;
}
}
(2) DbConfig
- config패키지를 생성하여 작성
- JdbcTemplate을 사용하여 회원 데이터를 DB에 보관하고 관리할 수 있도록 DataSource, TransactionManager, JdbcTemplate을 스프링빈으로 직접 등록
- DB는 별도의 외부 DB가 아니라 JVM 내부에서 동작하는 메모리 DB를 사용
- JdbcTransactionManager는 DataSourceTransactionManager의 업그레이드 버전으로 같은 기능에 예외 변환 기능이 보강된 것임
package hello.config;
@Slf4j
@Configuration
public class DbConfig {
@Bean
public DataSource dataSource() {
log.info("dataSource 빈 등록");
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setJdbcUrl("jdbc:h2:mem:test"); // 메모리모드로 사용
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
@Bean
public TransactionManager transactionManager() {
log.info("transactionManager 빈 등록");
return new JdbcTransactionManager(dataSource());
}
@Bean
public JdbcTemplate jdbcTemplate() {
log.info("jdbcTemplate 빈 등록");
return new JdbcTemplate(dataSource());
}
}
(3) MemberRepository
- member패키지에 작성
- JdbcTemplate을 사용하여 회원을 관리하는 리포지토리
- DbConfig에서 JdbcTemplate을 빈으로 등록했기 때문에 바로 주입받아서 사용할 수 있음
- initTable: 보통 리포지토리에 테이블을 생성하는 스크립트를 두지는 않지만 예제를 단순화 하기 위해서 사용하였음
package hello.member;
@Repository
public class MemberRepository {
public final JdbcTemplate template;
public MemberRepository(JdbcTemplate template) {
this.template = template;
}
// 예제를 간단하기 하기위해 테이블 생성 코드를 작성, 보통은 create 문을 직접 작성하지 않음
public void initTable() {
template.execute("create table member(member_id varchar primary key, name varchar)");
}
// 실무에서는 NamedParameterJdbcTemplate 사용
public void save(Member member) {
template.update("insert into member(member_id, name) values (?,?)",
member.getMemberId(),
member.getName());
}
public Member find(String memberId) {
return template.queryForObject("select member_id, name from member where member_id=?",
BeanPropertyRowMapper.newInstance(Member.class),
memberId);
}
public List<Member> findAll() {
return template.query("select member_id, name from member",
BeanPropertyRowMapper.newInstance(Member.class));
}
}
(4) MemberRepositoryTest
- MemberRepository의 기능을 테스트하는 코드
- @Transactional을 사용하여 트랜잭션 기능을 적용(해당 기능을 사용하려면 TransactionManager가 스프링 빈으로 등록되어있어야 함)
- 테이블을 생성하고 조회해서 기존 데이터와 같은지 간단히 검증
- 실행해보면 테스트가 성공하고 로그에 DbConfig가 정상적으로 동작하며 히카리 풀도 정상적으로 동작하는 것을 확인할 수 있음
package hello.member;
@SpringBootTest
class MemberRepositoryTest {
@Autowired MemberRepository memberRepository;
@Transactional
@Test
void memberTest() {
Member member = new Member("idA", "memberA");
memberRepository.initTable();
memberRepository.save(member);
Member findMember = memberRepository.find(member.getMemberId());
assertThat(findMember.getMemberId()).isEqualTo(member.getMemberId());
assertThat(findMember.getName()).isEqualTo(member.getName());
memberRepository.save(member2);
List<Member> members = memberRepository.findAll();
assertThat(members.size()).isEqualTo(2);
assertThat(members).contains(member1, member2);
}
}
(5) 정리
- 회원 데이터를 DB에 보관하고 관리하기 위해 직접 빈으로 등록한 JdbcTemplate, DataSource, TransactionManager들이 모두 사용되었음
- DB에 데이터를 보관하고 관리하기 위해 이런 객체들을 스프링 빈으로 등록해야하는 번거로움이 있는데, 만약 DB를 사용하는 다른 프로젝트를 진행한다면 이러한 객체들을 계속 스프링 빈으로 등록해주는 반복작업이 필요함
- 이를 스프링 부트가 해결해줌
2. 자동 구성 확인
1) 스프링 빈 등록 확인
(1) DbConfigTest
- JdbcTemplate, DataSource, TransactionManager가 스프링 컨테이너에 잘 등록되었는지 확인
- 해당 빈들을 DbConfig설정을 통해 스프링 컨테이너에 등록했기 때문에 null이면 안되며 사실 @Autowired는 의존관계에 주입에 실패하면 오류가 발생하도록 기본 설정이 되어있음(이해를 돕기 위해 테스트 코드를 작성한 것임)
- 실행해보면 테스트가 정상적으로 모두 통과되고 의존관계 주입이 정상처리되어 로그들이 모두 출력된 것을 확인할 수 있음
package hello.config;
@Slf4j
@SpringBootTest
class DbConfigTest {
@Autowired DataSource dataSource; // javax.sql 꺼를 사용해야함
@Autowired TransactionManager transactionManager;
@Autowired JdbcTemplate jdbcTemplate;
@Test
void checkBean() {
log.info("dataSource = {}", dataSource);
log.info("transactionManager = {}", transactionManager);
log.info("jdbcTemplate = {}", jdbcTemplate);
assertThat(dataSource).isNotNull();
assertThat(transactionManager).isNotNull();
assertThat(jdbcTemplate).isNotNull();
}
}
2) 빈 등록 제거
(1) DbConfig 수정
- DbConfig에서 빈 등록을 제거하는 두가지 방법: @Configuration을 주석처리, @Bean을 주석처리
- @Configuration을 주석처리하면 해당 설정 파일 자체가 스프링이 컴포넌트 스캔의 대상이 아니게 되고 @Bean을 주석처리하면 해당 코드가 스프링 빈으로 등록하지 않음
- 여기서는 간단히 @Configuration을 주석처리
@Slf4j
//@Configuration
public class DbConfig {
// ... 기존 코드 동일
}
(2) DbConfigTest 재실행
- checkBean 테스트가 정상적으로 통과되지만 기존에 수동빈으로 작성했던 빈 등록 로그는 보이지 않음
- 심지어 출력 로그에 JdbcTemplate와 DataSource, TransactionManager의 빈들이 존재하는 것이 로그로 확인되는데, 사실 이 빈들은 모두 스프링 부트가 자동으로 등록해준 것임
3. 스프링 부트의 자동 구성
1) 자동 구성 살짝 알아보기
(1) 스프링 설정 지옥 해결
- 스프링 부트는 자동 구성(Auto Configuration)이라는 기능을 제공하는데, 설정 지옥에 빠져 일반적으로 자주 사용하는 수 많은 빈들을 직접 등록했던 것을 자동으로 등록해주는 기능임
- 자동 구성 덕분에 개발자는 반복적이고 복잡한 빈 등록과 설정을 최소화 하고 애플리케이션 개발을 빠르게 시작할 수 있게됨
- 스프링 부트는 spring-boot-autoconfigure라는 프로젝트 안에서 수 많은 자동 구성을 제공하며 스프링 부트 스타터라이브러리를 사용하면 해당 라이브러리 안에 autoconfigure가 들어있음
** 주의!
- 여기서 모든 것을 깊이있게 이해하지 않고 대략 어떻게 동작하는지 감을 잡을 수 있는 정도면 충분함
- 진행하면서 자동 구성과 관련된 부분들을 단계적으로 풀어나감
(2) JdbcTemplateAutoConfiguration
- @AutoConfiguration: 자동 구성을 사용하려면 해당 애노테이션을 등록해야함
자동 구성도 내부에 @Configuration이 있어서 빈을 등록하는 자바 설정 파일로 사용할 수 있음 - after = DataSourceAutoConfiguration.class: 자동 구성이 실행되는 순서를 지정할 수 있는데, JdbcTemplate은 DataSource가 필요하기 때문에 DataSource를 자동으로 등록해주는 DataSourceAutoConfiguration.Class 다음에 실행하도록 설정 되어 있음
- @ConditionalOnClass({DataSource.class, JdbcTemplate.class}): IF문과 유사한 기능으로 이런 클래스가 있는 경우에만 설정이 동작하도록 되어있음
JdbcTemplate은 DataSource, JdbcTemplate라는 클래스가 있어야 동작할 수 있으므로 만약 없으면 여기 있는 설정들이 모두 무효화되고 빈도 등록되지 않음 - @ConditionalOnXxx 시리즈: 자동 구성의 핵심이므로 뒤에서 자세히 설명
- @Import: 스프링에서 자바 설정을 추가할 때 사용함
@AutoConfiguration(after = {DataSourceAutoConfiguration.class})
@ConditionalOnClass({DataSource.class, JdbcTemplate.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({JdbcProperties.class})
@Import({DatabaseInitializationDependencyConfigurer.class, JdbcTemplateConfiguration.class, NamedParameterJdbcTemplateConfiguration.class})
public class JdbcTemplateAutoConfiguration {
public JdbcTemplateAutoConfiguration() { }
}
(3) JdbcTemplateConfiguration
- JdbcTemplateAutoConfiguration의 @Import의 대상인 JdbcTemplateConfiguration도 함께 살펴보면 익히 진행해 봤던 수동 빈을 등록하던 형태를 볼 수 있음
- @Configuration: 자바 설정 파일로 사용됨
- @ConditionalOnMissingBean(JdbcOperations.class): JdbcOperations 빈이 없을 때 동작하도록 설정
- JdbcTemplate의 부모인터페이스가 바로 JdbcOperations인데 쉽게 이야기하면 JdbcTemplate이 빈으로 등록되어 있지 않는 경우에만 동작하도록 되어있음
- 만약 이런 기능이 없다면 내가 직접 등록한 JdbcTemplate과 자동 구성이 등록하는 JdbcTemplate이 중복 등록되는 문제가 발생할 수 있으므로 개발자가 특별한 사정에 의해 직접 빈을 등록하면 개발자가 등록한 빈을 사용하고 자동 구성은 동작하지 않음
- 자동 구성이 동작하면 JdbcTemplate이 몇가지 설정을 거쳐서 빈으로 등록되는 것을 확인할 수 있음
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean({JdbcOperations.class})
class JdbcTemplateConfiguration {
JdbcTemplateConfiguration() {
}
@Bean
@Primary
JdbcTemplate jdbcTemplate(DataSource dataSource, JdbcProperties properties) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcProperties.Template template = properties.getTemplate();
jdbcTemplate.setFetchSize(template.getFetchSize());
jdbcTemplate.setMaxRows(template.getMaxRows());
if (template.getQueryTimeout() != null) {
jdbcTemplate.setQueryTimeout((int)template.getQueryTimeout().getSeconds());
}
return jdbcTemplate;
}
}
(4) 자동 등록 설정
- 다음과 같은 자동 구성 기능들이 다음 빈들을 등록해줌
- JdbcTemplateAutoConfiguration: JdbcTemplate
- DataSourceAutoConfiguration: DataSource
- DataSourceTransactionManagerAutoConfiguration: TransactionManager
- 그래서 개발자가 직접 빈을 등록하지 않아도 JdbcTemplate, DataSource, TransactionManager가 스프링 빈으로 등록된 것임
(5) 스프링 부트가 제공하는 자동 구성(AutoConfiguration)
- 공식 링크
- 스프링 부트는 수 많은 자동 구성을 제공하고 spring-boot-autoconfigure에 자동 구성을 모아둠
- 스프링 부트 프로젝트를 사용하면 spring-boot-autoconfigure라이브러리는 기본적으로 사용됨
(6) 자동 구성 기능의 두가지 개념
- 스프링 부트가 제공하는 자동 구성 기능을 이해하려면 다음 두 가지 개념을 이해해야함
- @Conditional: 특정 조건에 맞을 때 설정이 동작하도록 함
- @AutoConfiguration: 자동 구성이 어떻게 동작하는지 내부 원리 이해
2) Auto Configuration 용어, 자동 설정? 자동 구성?
- 보통 두 용어로 번역되어 사용됨
(1) 자동 설정
- Configuration이라는 단어가 컴퓨터 용어에서는 환경 설정 즉, 적성이라는 뜻으로 자주 사용됨
- AutoConfiguratin은 크게 보면 빈들을 자동으로 등록하여 스프링이 등록하는 환경을 자동으로 설정해주기 때문에 자동설정이라는 용어도 맞음
(2) 자동 구성
- Configuration이라는 단어는 구성, 배치라는 뜻도 있음
- 예를 들어 컴퓨터에서 CPU, 메모리등을 배치해야 컴퓨터가 동작하는데 이렇게 배치하는 것을 구성이라하는데, 스프링도 스프링 실행에 필요한 빈들을 적절하게 배치해야 함
- 자동 구성은 스프링 실행에 필요한 빈들을 자동으로 배치해주는 것임
(3) 정리
- 즉, 자동 설정, 자동 구성 두 용어 모두 맞다고 볼 수 있으며 자동 설정은 넓게 사용되는 의미이고, 자동 구성을 실행에 필요한 컴포넌트 조각을 자동으로 배치한다는 더 좁은 의미에 가깝다고 볼 수 있음
- 강의에서는 Auto Configuration은 자동 구성이라는 단어로 주로 사용하고, Configuration을 단독으로 사용될 때는 설정이라는 단어로 주로 사용함
4. 자동 구성 직접만들기 - 기반 예제
1) 기반 예제 작성
- 자동 구성에 대해 자세히 알아보기 위한 실시간으로 자바 메모리 사용랴을 웹으로 확인하는 간단한 예제 작성
(1) Memory
- 사용중인 메모리를 뜻하는 used와 최대 메모리를 뜻하는 max를 가지고있는 클래스
- used가 max를 넘게되면 메모리 부족 오류가 발생하도록 할 예정
** 주의
- hello 패키지 하위가 아닌 java하위(hello상위)에 memory패키지를 생성 후 작성
- 이후에 다른곳으로 편하게 옮기기 위해 작성(hello와는 전혀 다른 기능을 가짐)
package memory;
public class Memory {
private long used;
private long max;
public Memory(long used, long max) {
this.used = used;
this.max = max;
}
public long getUsed() {
return used;
}
public long getMax() {
return max;
}
@Override
public String toString() {
return "Memory{" +
"used=" + used +
", max=" + max +
'}';
}
}
(2) MemoryFinder
- JVM에서 메모리 정보를 실시간으로 조회하는 기능
- max: JVM이 사용할 수 있는 최대 메모리, 이 수치를 넘어가면 OOM이 발생함
- total: JVM이 확보한 전체 메모리(JVM은 처음부터 max까지 다 확보하지 않고 필요할 때 마다 조금씩 확보함)
- free: total중에 사용하지 않은 메모리(JVM이 확보한 전체 메모리 중에 사용하지 않은 것)
- used: JVM이 사용중인 메모리를 계산
package memory;
@Slf4j
public class MemoryFinder {
public Memory get() {
long max = Runtime.getRuntime().maxMemory(); // max
long total = Runtime.getRuntime().totalMemory(); // total
long free = Runtime.getRuntime().freeMemory(); // free
long used = total - free; // used
return new Memory(used, max);
}
@PostConstruct
public void init() {
log.info("init memoryFinder");
}
}
(3) MemoryController
- 메모리 정보를 조회하는 컨트롤러
package memory;
@Slf4j
@RestController
@RequiredArgsConstructor
public class MemoryController {
private final MemoryFinder memoryFinder;
@GetMapping("/memory")
public Memory system() {
Memory memory = memoryFinder.get();
log.info("memory={}", memory);
return memory;
}
}
(4) MemoryConfig
- 설정파일이므로 설정파일을 모아둔 hello.config에 작성
- memoryController, memoryFinder를 빈으로 등록
package hello.config;
import memory.MemoryController;
import memory.MemoryFinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
(5) 애플리케이션 실행
- 실행 후 localhost:8080/memory에 접속해보면 메모리 사용량을 json 형태로 실시간으로 확인할 수 있음
5. @Conditional
1) @Conditional 설명
(1) 적용 상황
- 앞서 만든 메모리 조회 기능을 항상 사용하는 것이 아니라 특정 조건일 때만 해당 기능이 활성화 되도록 한다고 가정
- 여기서의 핵심은 소스 코드를 고치지 않고 이런 것이 가능해야한다는 점이며, 프로젝트를 빌드해서 나온 동일한 빌드 파일을 개발 서버에도 배포하고, 운영서버에도 배포해야 함
- ex) 개발 서버에서 확인 용도로만 해당 기능을 사용하고 운영 서버에서는 해당 기능을 사용하지 않을 때 등등...
- 같은 소스 코드인데 특정 상황일 때만 특정 빈들을 등록해서 사용하도록 도와주는 기능이 바로 @Conditional이며 이 기능은 스프링 부트 자동 구성에서 자주 사용함
- 이름 그대로 특정 조건을 만족하는가, 만족 하지 않는가를 구별하는 기능임
(2) Condition 살펴보기
- @Conditional 기능을 사용하려면 먼저 Condition인터페이스를 구현해야 함
- matches()메서드가 true를 반환하면 조건에 만족해서 동작하고, false를 반환하면 동작하지 않음
- ConditionContext: 스프링 컨테이너, 환경 정보등을 담고 있음
- AnnotatedTypeMetadata: 애노테이션 메타 정보를 담고 있음
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
2) 구현
(1) 구현 목적
- Condition인터페이스를 구현해서 자바 시스템 속성이 memory=on 이라고 되어 있을 때만 메모리 기능이 동작하도록 작성
#VM Options
#java -Dmemory=on -jar project.jar
(2) MemoryCondition
- 상속받는 Condition의 패키지명 주의
- 환경 정보에 memory=on이라고 되어있는 경우에만 true를 반환하도록 설정
- 환경 정보와 관련된 부분은 뒤에서 아주 자세히 다룰 예정임
package memory;
@Slf4j
public class MemoryCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// -Dmemory=on
String memory = context.getEnvironment().getProperty("memory");
log.info("memory={}", memory);
return "on".equals(memory);
}
}
(3) MemoryConfig - 수정
- @Conditional(MemoryCondition.class): 해당 애노테이션의 적용으로 MemoryConfig의 적용 여부는 @Conditional에 지정한 MemoryCondition의 조건에 따라 달라짐
- MemoryCondition의 matches()를 실행해보고 그 결과가 true이면 MemoryConfig는 정상 동작하여 작성된 빈들이 등록됨
- MemoryCondition의 matches()의 실행결과가 false이면 MemoryConfig는 무효화가되며 빈들도 등록되지 않음
@Configuration
@Conditional(MemoryCondition.class) // 추가
public class MemoryConfig {
// @Bean 코드들은 동일
}
(4) 그냥 실행
- 아무 조건을 주지 않고 실행 후 localhost:8080/memory에 접속하면 오류페이지가 뜨는데, memory=on설정을 하지 않았으므로 MemoryConfig의 빈들이 등록되지 않았기 때문임
- MemoryCondition에서 출력된 로그를 보면 memory=null이 출력되어 결과가 false가 되는 것을 확인할 수 있음
(5) 옵션 주고 실행
- Run/Debug Configurations -> MOdify options -> Java의 Add VM options 활성화 -> -Dmemory=on 입력
- 다시 실행하여 url에 들어가보면 정상상적으로 실시간 메모리 사용량이 출력됨
- 출력 로그를 보면 memory=on이 출력고 init memoryFinder 로그도 출력되어 정상적으로 MemoryFinder가 동작하는 것을 확인할 수 있음
** 참고
- 스프링이 로딩 되는 과정은 복잡하여 MemoryCondition이 여러번 호출될 수 있는데 이 부분은 크게 중요하지 않으므로 넘어가도 됨
- 스프링은 외부 설정을 추상화하여 Environment로 통합해 다양한 외부 환경 설정을 Environment하나로 읽어 들일 수 있음(getEnvironment() 사용), 해당 내용은 매우 중요하고 자주 사용하므로 뒤에서 자세히 다룸
#VM Options
#java -Dmemory=on -jar project.jar
-Dmemory=on
#Program arguments
# -- 가 있으면 스프링이 환경 정보로 사용
#java -jar project.jar--memory=on
--memory=on
#application.properties
#application.properties에 있으면 환경 정보로 사용
memory=on
6. @Conditional - 다양한 기능
1) 스프링이 제공하는 구현체
(1)MemoryConfig - 수정
- 직접 만든 @Conditional(...)를 주석 처리
- @ConditionalOnProperty(name = "memory", havingValue = "on")을 추가하면 환경 정보가 memory=on이라는 조건에 맞으면 동작하고 그렇지 않으면 동작하지 않음
- 기존에 memory=on 설정을 해두었기 때문에 다시 애플리케이션을 실행해보변 정상적으로 기능이 동작하는 것을 확인할 수 있음
@Configuration
//@Conditional(MemoryCondition.class)
@ConditionalOnProperty(name = "memory", havingValue = "on") // 추가
public class MemoryConfig {
// @Bean 등록 코드는 유지
}
(2) @ConditionalOnProperty 구조 일부
- @ConditionalOnProperty의 내부를 보면 직접 만든 것과 동일하게 내부에 @Conditional을 가지고 있음
- 그리고 그 안에 Condition인터페이스를 구현한 OnPropertyCondition을 가지고 있음
// 다른 애노테이션 생략
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
// ... 다른 코드 생략
}
2) @ConditionalOnXxx
(1) 설명
- 스프링은 @Conditional과 견련하여 개발자가 편리하게 사용할 수 있도록 수 많은 @ConditionalOnXxx를 제공함
- 공식 메뉴얼
- 이름이 직관적이기 때문에 바로 이해가 될 것이며 @ConditionalOnXxx는 주로 스프링 부트 자동 구성에 사용됨
- JdbcTemplateAutoConfiguration, DataSourceTransactionManagerAutoConfiguration, DataSourceAutoConfiguration 등의 자동 구성 클래스들을 열어서 소스 코드를 확인해보면 @ConditionalOnXxx가 아주 많이 사용되는 것을 확인할 수 있음
(2) @ConditionalOnClass, @ConditionalOnMissingClass
- 클래스가 있는 경우 동작
- 나머지는 그 반대
(3) @ConditionalOnBean, @ConditionalOnMissingBean
- 빈이 등록되어 있는 경우 동작
- 나머지는 그 반대
(4) @ConditionalOnProperty
- 환경 정보가 있는 경우 동작
(5) @ConditionalOnResource
- 리소스가 있는 경우 동작
(6) @ConditionalOnWebApplication, @ConditionalOnNotWebApplication
- 웹 애플리케이션인 경우 동작
(7) @ConditionalOnExpression
- SpEL 표현식에 만족하는 경우 동작
** 참고
- @Conditional 자체는 스프링 부트가 아니라 스프링 프레임 워크의 기능임(원래는 스프링 부트꺼였는데 스프링으로 넘어감)
- 스프링 부트는 이 기능을 확장해서 @ConditionalOnXxx를 제공함