SPRING/Spring Boot

[SpringBoot] @Conditional 이란?

IT록흐 2023. 4. 29. 19:40
반응형

 

@Conditional 어노테이션이란?

 

컴포넌트의 Bean 등록여부에 조건을 달 수 있게하는 어노테이션이다.

 

스프링은 IOC컨테이너에 객체를 Bean으로 등록하여 사용한다. @Component로 선언된 클래스는 모두 Bean으로 등록되는데 여기에 조건부를 달 수가 있다. 즉,  A경우에 등록을 하되, B경우에는 등록하지 않도록 설정할 수 있다.

 

 

예를 들어보자.

 

 

 

 

Spring기반의 웹사이트가 있다. 여기에 추가 기능을 넣으려고 한다.

 

 

 

 

 

Spring은 추가기능의 설정파일을 읽고 POJO에서 Bean을 IOC컨테이너에 등록한다. 이때 설정파일을 조건부로 읽는다면 필요할 때만 IOC컨테이너에 Bean을 등록할 수 있다. 여기서 @Conditional 어노테이션을 사용한다.

 

 

구현

웹사이트 실행시 옵션으로 -Dmemory=on인 경우만, 메모리량을 보여주는 기능을 추가하려고 한다.

 

 

 

 

간단히 SpringBoot 프로젝트를 생성한다.

 

@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}

 

SpringBoot 프로젝트에 생성되어 있는 DemoApplication 클래스이다. @SpringBootApplication은 컴포넌트스캔 기능을 가지고 있다.  컴포넌트 스캔은 하위 디렉토리에 위치한 @Component로 명시된 클래스를 Bean으로 등록하는 기능이다.

 

 

설정클래스는 @Configuration로 명시되어 있지만, 이것 또한 @Component이다. 그러므로 하위디렉토리에 설정클래스를 위치시킨다.

 

package com.example.demo.config;

import com.example.memory.MemoryController;
import com.example.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(); }
}

 

MemoryConfig는 MemoryController와 MemoryFinder를 Bean으로 등록한다.

 

위 코드는 조건부가 아니다. 컴포넌트 스캔시, 조건을 타지 않는다.

그러므로 웹사이트 실행시 옵션으로 -Dmemory=on인 경우에만 Bean으로 등록하도록 설정해야 한다.

 

package com.example.demo.config;

import com.example.memory.MemoryCondition;
import com.example.memory.MemoryController;
import com.example.memory.MemoryFinder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
@Conditional(MemoryCondition.class) // 조건 추가!
public class MemoryConfig {
    @Bean
    public MemoryController memoryController(){ return new MemoryController(memoryFinder()); }
    @Bean
    public MemoryFinder memoryFinder(){ return new MemoryFinder(); }
}

 

 

@Conditional 어노테이션이 추가되었다.

 

MemoryCondition.class는 Condition 인터페이스를 구현한 구현체이다. Condition 인터페이스는 Boolean을 반환하는 메소드, 한 개만 가지고 있는 함수형 인터페이스이다. 

 

public class MemoryCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String memory = context.getEnvironment().getProperty("memory"); // 옵션값 가져오기
        return "on".equals(memory); // "on"이면 스캔 O , 아니면 스캔 X
    }
}

 

@Conditional(MemoryCondition.class)는 MemoryCondition의 matches 메소드의 return값을 받아 컴포넌트 스캔여부를 판단한다.  "on"이면 스캔되어 IOC컨테이너에 Bean으로 등록되고 on이 아니면 IOC컨테이너에 Bean으로 등록되지 않는다. 

 

그럼 옵션으로 -Dmemory=on을 추가해보자.

 

 

IntelliJ 우측 상단에 Edit Configurations을 클릭한다.

 

 

 

1. Modify Options를 클릭하고 Add VM options를 클린한다. 그리고 -Dmemory=on 옵션을 추가한다.

 

 

 

웹어플리케이션이 실행되면

DemoApplication은 MemoryConfig를 컴포넌트 스캔한다. ( memory 디렉토리는 DemoApplication의 하위디렉토리가 아니므로, 컴포넌트 스캔을 하지 않는다. 그저 POJO이다.

 

MemoryConfig.java

@Configuration
@Conditional(MemoryCondition.class) // 컴포넌트 스캔시 조건검사!
public class MemoryConfig {
    @Bean
    public MemoryController memoryController(){ return new MemoryController(memoryFinder()); }
    @Bean
    public MemoryFinder memoryFinder(){ return new MemoryFinder(); }
}

 

이때, @Conditional에서 조건을 검사한다. MemoryCondition 객체를 생성하여 boolean을 return한다.

 

MemoryCondition.java

public class MemoryCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String memory = context.getEnvironment().getProperty("memory"); // 옵션값 가져오기
        return "on".equals(memory); // "on"이면 스캔 O , 아니면 스캔 X
    }
}

 

ApplicationContext 즉, IOC컨테이너에서 옵션값을 가져온다. memory 속성의 값을 가져와 on 여부를 return한다. True가 반환되면 MemoryConfig.java에 정의된 @Bean은 IOC컨테이너에 등록된다. 

 

그럼  MemoryController 가 Bean으로 등록되므로, 이제 URL로 해당 기능에 접근이 가능해진다.

 

localhost:8080/memory 로 접근하지

사용중인 Memory 양과 최대 사용가능한 Memory양이 출력되었다.

 

추가했던 VM option을 제거해보겠다.

 

MemoryController가 Bean으로 등록되지 않아 접근이 불가능하다.

 

 

이와같이,

@Conditional 어노테이션을 사용하면 조건부로 Bean을 등록할 수 있다.

하지만 Condition 인터페이스의 구현체를 만들어야 한다는 문제가 있다. 

 

SpringBoot는 이를 해결할 다양한 어노테이션을 가지고 있다.

@Configuration
//@Conditional(MemoryCondition.class)
@ConditionalOnProperty(name = "memory", havingValue = "on")
public class MemoryConfig {
    @Bean
    public MemoryController memoryController(){ return new MemoryController(memoryFinder()); }
    @Bean
    public MemoryFinder memoryFinder(){ return new MemoryFinder(); }
}

 

 

@ConditionalOnProperty 어노테이션으로 속성과 속성값을 지정해주면 된다. 자동으로 Condition 구현체가 생성되어 조건을 검사한다. 그럼 이를 실행해보자. 

 

 

 

MemoryCondition 클래스를 구현하지 않아도 이와같이, 조건검사가 이루어졌음을 확인할 수 있다. 

 

 

 


 

참고자료

 

스프링 부트 - 핵심 원리와 활용 - 인프런 | 강의

실무에 필요한 스프링 부트는 이 강의 하나로 모두 정리해드립니다., - 강의 소개 | 인프런

www.inflearn.com

 

 

반응형