스프링 프레임워크 핵심기술을 공부하고 정리하는 포스트입니다.


SpEL(Spring Expression Language)

   SpELSpring Expression Language의 줄임말로 여기서 Expression Language, 줄여서 EL은 객체 그래프를 조회하거나 조작하는 기능을 제공한다. 예시로 JSP에서 EL을 볼 수 있다.

스프링에서는 EL의 기능과 비슷하지만, 메소드 호출을 지원하고 문자열 템플릿 기능도 제공해주는 EL이 프로젝트 전반에 걸쳐서 필요했다. 이러한 필요에 의해서 만들어진 것이 바로 SpEL이며 스프링 코어단에 추가 되었다.

SpEL은 스프링 3.0부터 지원하는데 스프링 코어단에 추가되었다고 코어단에서만 사용되는 것이 아니라, 시큐리티, 데이터 등등 프로젝트 전체에 사용된다.

   AppRunner를 만들고 SpEL이 어떻게 생겼는지 보도록하자.

참고로 ApplicationRunner라는 인터페이스를 구현하면 SpringBoot Application이 실행된 후에 AppRunner 안에 있는 코드들이 바로 실행이 된다.

▶ @Value 애노테이션

   SpEL이 사용되는 곳 중에 하나는 @Value 애노테이션이다. @Value 애노테이션을 선언한 후에 표현식이나 프로퍼티를 사용할 수 있다.

package me.gracenam.demospring51;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    @Value("#{1 + 1}")
    int value;

    @Value("#{'hello ' + 'world'}")
    String greeting;

    @Value("#{1 eq 1}")
    boolean trueOrFalse;

    @Value("hello")
    String hello;

    @Value("${my.value}")
    int myValue;

    @Value("#{${my.value} eq 100}")
    boolean isMyValue100;

    @Value("#{'spring'}")
    String spring;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("===========================");
        System.out.println(value);
        System.out.println(greeting);
        System.out.println(trueOrFalse);
        System.out.println(hello);
        System.out.println(myValue);
        System.out.println(isMyValue100);
        System.out.println(spring);
    }
}
my.value = 100


코드에서 @Value 애노테이션 뒤에 붙은 #{ }표현식을 사용하고, ${ }프로퍼티를 사용한다. ${my.value}는 application.properties라는 프로퍼티에 등록된 값을 읽어오는 것이다. 그리고 #{${my.value} eq 100}은 표현식 내부에 프로퍼티를 사용한 것인데 이처럼 표현식 안에 프로퍼티를 가질 수는 있지만 프로퍼티 안에 표현식을 사용하는 것은 허용되지 않는다.

#{'spring'}은 리터럴을 표현한 것인데 표현식 내에 문자열을 입력하면 그 문자열이 리터럴 그대로 전달되는 것이다. 그 외에도 Arrays, List, Maps, Indexers 등이 지원된다. 더 자세한 것은 Reference를 참고하자.

▶ Bean 참고

   다른 방법은 Bean을 참고하는 것이다. Sample이라는 Bean을 만들어서 data = 200이라는 정보를 입력했다.

package me.gracenam.demospring51;

import org.springframework.stereotype.Component;

@Component
public class Sample {

    private int data = 200;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

}
package me.gracenam.demospring51;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    @Value("#{sample.data}")
    int sampleDate;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("===========================");
        System.out.println(sampleDate);
    }
}


이처럼 빈에 있는 값을 가져와서 출력할 수 있다.

   SpEL을 지원하는 기능들이 더 있는데

  • @ConditionalOnExpression 애노테이션
    • @ConditionalOn 애노테이션은 선택적으로 빈을 등록하거나 빈 설정 파일을 읽어들일 때 사용하는 애노테이션인데, 이 경우에 Expression 기반으로 빈을 선별할 수 있다.
  • 스프링 시큐리티
    • 메소드 시큐리티, @PreAuthorize, @PostAuthorize, @PreFilter, @PostFilter
    • 시큐리티에서 사용되는 함수들은 EvaluationContext1에서 오는 것이다. EvaluationContext로 Bean을 만들어주면 빈이 제공하는 함수들을 쓸 수 있다.
  • 스프링 데이터
    • 스프링 데이터의 @Query 애노테이션 같은 경우에도 메서드에서 받은 파라미터 인자에 들어있는 필드 값을 참조하거나 인덱스 기반으로 참조해서 사용할 수 있다.
  • Thymeleaf
    • 스프링 프로젝트 뿐만 아니라 Thymeleaf라는 뷰 템플릿 엔진에서도 SpEL을 지원한다.

등이 있다.

   마지막으로 SpEL의 기반을 이해하기 위한 두 클래스 중 ExpressionParser의 예제를 살펴보자.

package me.gracenam.demospring51;

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;

@Component
public class AppRunner implements ApplicationRunner {

    @Override
    public void run(ApplicationArguments args) throws Exception {
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("2 + 100");
        Integer value = expression.getValue(Integer.class);
        System.out.println("====================");
        System.out.println(value);
    }
}

필요에 따라서 ExpressionParser를 직접 작성해서 사용할 수 있다. parser.parseExpression()의 경우 이미 자체가 Expression이기 때문에 괄호 안에 들어갈 값만 입력해주면 된다. 이렇게 한 후 출력을 해보면 값이 나오는 것을 볼 수 있다.



Reference


  1. StandardEvaluationContext context = new StandardEvaluationContext(bean)