Framework/Spring

Spring @ConditionalOnProperty 사용법 및 예제

호형 2022. 4. 13. 19:03

@ConditionalOnProperty 은 단어 그대로 Property의 조건에 따라 작동 하는 bean 이다. 꼭 bean이어야 한다. 예를 들면 우리가 많이 사용하는 StereoType(@Controller, @Service..)은 그것 자체로 bean이어서 이 하위 요소(method level)에 @ConditionalOnProperty 를 붙이면 동작하지 않는다. 

 

사용법

Property란 무엇이냐? Spring 사용시 application.properties나 application.yml에 정의해서 사용하는 값들을 말한다. 

예를 들면 이런 application.yml 이 있다. 

my:
  fruit: banana

my.fruit 이 어떤거냐에 따라서 다른 bean을 생성하고자한다. my.fruit 값이 banana면 banana bean을 생성하고 apple이면 apple bean을 생성하고 싶다. (예제 시나리오 만드는건 너무 어려운것 같다..) 

@ConditionalOnProperty(value = "my.fruit", havingValue = "apple", matchIfMissing = false)
@Bean 
public String apple() {
    return "apple";
}

@ConditionalOnProperty(value = "my.fruit", havingValue = "banana", matchIfMissing = false)
@Bean 
public String banana() {
    return "banana";
}

이런식으로 @ConditionalOnProperty를 붙여서 application.yml에 있는 값을 기준으로 bean을 생성해줄 수 있다. 

my.fruit 값이 banana면 banana bean을 생성하고 apple이면 apple bean을 생성해준다. 

여기서 나오는 @ConditionalOnProperty의 속성값들은 ConditionalOnProperty @interface에서 확인 가능하다. 

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {

	/**
	 * Alias for {@link #name()}.
	 * @return the names
	 */
	String[] value() default {};

	/**
	 * A prefix that should be applied to each property. The prefix automatically ends
	 * with a dot if not specified. A valid prefix is defined by one or more words
	 * separated with dots (e.g. {@code "acme.system.feature"}).
	 * @return the prefix
	 */
	String prefix() default "";

	/**
	 * The name of the properties to test. If a prefix has been defined, it is applied to
	 * compute the full key of each property. For instance if the prefix is
	 * {@code app.config} and one value is {@code my-value}, the full key would be
	 * {@code app.config.my-value}
	 * <p>
	 * Use the dashed notation to specify each property, that is all lower case with a "-"
	 * to separate words (e.g. {@code my-long-property}).
	 * @return the names
	 */
	String[] name() default {};

	/**
	 * The string representation of the expected value for the properties. If not
	 * specified, the property must <strong>not</strong> be equal to {@code false}.
	 * @return the expected value
	 */
	String havingValue() default "";

	/**
	 * Specify if the condition should match if the property is not set. Defaults to
	 * {@code false}.
	 * @return if the condition should match if the property is missing
	 */
	boolean matchIfMissing() default false;

}

쉽게 말하자면 다음과 같다. 

- name (=value) 속성은 application.yml 의 key

- havingValue 속성은 application.yml의 key에 매핑되는 value

- matchIfMissing 속성은 매칭되는게 없으면 bean을 생성할건지 말건지 결정하는 속성 

 

Test

@RestController
public class TestController {
    
    @Autowired
    DefaultListableBeanFactory bf;
    
    @GetMapping("/fruit")
    public String getFruit() {
        
        String myFruit = null;
        for(String n : bf.getBeanDefinitionNames()){
            if("apple".equals(n)) {
                myFruit = "my.fruit : apple";
                break;
            } else if("banana".equals(n)) {
                myFruit = "my.fruit : banana"; 
                break;
            } else {
                myFruit = "my.fruit : unknown"; 
                continue;
            }
        }
        return myFruit;
    }
}

위에서 정의한 application.yml과 @ConditionalOnProperty를 정의한 bean이 잘 동작하는지 간단하게 테스트해보는 코드이다. application.yml 파일에 따라 결과가 잘 나온다.

마지막으로 다시 한번 강조하지만 @ConditionalOnProperty는 꼭 bean에 정의를 해줘야 한다. 

 

끝!