@Conditional은 Spring Bean을 조건에 맞춰 등록할때 사용됩니다.
dev, staging, production 상황 별로 등록해야 되는 Spring Bean이 다를수가 있습니다.
개발 환경에서만 작동해야 되고 실제 운영 환경에서는 작동하면 안될 기능이 있는 등 Bean등록에 조건이 필요할때 사용됩니다.
실습해볼 예제는 Memory를 가져오는 class를 Bean으로 등록하여 호출하면 메모리 사용량을 보여주는 예제 입니다.
@Slf4j
public class MemoryFinder {
@PostConstruct
public void init() {
log.info("init MemoryFinder");
}
public Memory get() {
// Runtime.getRuntime() JVM 에서 메모리 정보를 실시간으로 조회
long max = Runtime.getRuntime().maxMemory();
long total = Runtime.getRuntime().totalMemory();
long free = Runtime.getRuntime().freeMemory();
long used = total - free;
return new Memory(used, max);
}
}
메모리 사용량을 조회해오는 core 기능을 가진 class입니다.
여기서 Bean으로 등록하지 않고 Config 설정을 통해 직접 등록해주는 방식을 사용하겠습니다.
@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;
}
}
MemoryFinder를 주입받아 호출하는 용도로 사용되는 RestController입니다.
@Configuration
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
MemoryFinder를 Bean으로 직접 등록해주고 MemoryController에도 주입해주어 직접 등록해줍니다.
이렇게 Bean들을 등록하게 된다면 production에서 사용하고 싶지 않아도 Bean들이 등록되어 사용할 수 있는 상황이 만들어집니다.
이때 조건을 통해 Bean을 만들기 위해 Conditional이라는 Annotaion을 사용해 조건문을 만들어 해결할 수 있습니다.
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
Spring에는 Condition이라는 Interface가 존재하고 해당 인터페이스의 구현체를 구현해주면 손쉽게 조건별로 Bean 등록을 할 수 있습니다.
@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);
}
}
Condition이라는 interface를 상속받아 Memory 빈 등록에 조건을 달아줄 MemoryCondition을 구현해줍니다.
property에 "memory"의 key가 "on"이라면 빈 등록을 해주는 조건입니다.
@Configuration
@Conditional(MemoryCondition.class)
public class MemoryConfig {
@Bean
public MemoryController memoryController() {
return new MemoryController(memoryFinder());
}
@Bean
public MemoryFinder memoryFinder() {
return new MemoryFinder();
}
}
직접 Memory 관련 Class들을 등록해주는 Configuration에 Condition을 구현한 Class를
@Conditional 어노테이션에 넣어주면 적용이 완료됩니다.
해당 프로젝트 property에 memory=on을 넣어줘야 Memory 관련 Class들이 작동하는것을 확인할 수 있고
실행 환경 등 변경에 맞게 유연하게 Bean등록을 변경할 수 있게 되었습니다.
reference: