[스프링] SpEL

스프링 표현 언어(SpEL, Spring Expression Language)(이하 SpEL)란 런타임에 객체 그래프(object graph)를 탐색하는 표현 언어이다. 객체의 프로퍼티 조회 및 설정, 메서드 실행, 연산 수행, 표현식(expression) 실행, 빈 참조 등의 기능을 제공한다. SpEL은 OGNL, MVEL, JBoss EL과 같은 자바 표현 언어를 대체한다. 일반적으로 표현 언어(expression language)는 표현식 문자열을 파싱 및 평가(evaluation)하여 값으로 변환하는 역할을 수행한다. 표현식 평가 과정에서 연산 수행, 객체의 프로퍼티 접근 및 할당, 객체의 메서드 호출 등의 과정이 일어나게 된다. SpEL은 스프링 프레임워크 기반 프로젝트 내에서 표현식 평가 기능을 제공하지만, 스프링 프레임워크와 독립적으로 사용할 수도 있다.

SpEL 관련 클래스와 인터페이스는 org.springframework.expression 패키지에 존재한다. 핵심 인터페이스는 다음과 같다.

  • ExpressionParser: 표현식 문자열을 파싱한다.
  • Expression: 표현식 문자열을 평가한다.
  • EvaluationContext: 표현식 문자열을 평가하여 프로퍼티, 메서드, 필드를 확인하고 타입 변환을 수행하기 위해 사용되는 컨텍스트이다. EvaluationContext는 표현식이 실행되는 컨텍스트를 의미하며 표현식 평가 중에 참조가 발견되면, 이 컨텍스트에서 참조가 해결(resolve)된다.
  • PropertyAccessor: 객체의 프로퍼티로부터 값을 읽거나 프로퍼티에 값을 설정한다.


ExpressiongetValue() 메서드는 표현식 평가 결과값을 조회하며, setValue() 메서드는 표현식 평가 결과값을 특정값으로 설정한다. getValue()setValue() 메서드는 EvaluationContext를 인자로 받을 수 있다. 이 경우 주어진 컨텍스트 상에서 표현식을 평가하여 결과값을 반환하거나, 인자값을 표현식으로 설정한다.

표현식 평가 시 어떤 EvaluationContext의 구현체를 사용하는가에 따라 SpEL의 기능이 다르게 구성된다. 예를 들어, SimpleEvalutionContext는 데이터 바인딩을 목적으로 하며, 자바 타입 및 생성자 참조, 빈 참조 등은 제외한다. SpEL의 전체 기능이 필요하지 않은 경우에 SimpleEvalutionContext를 사용한다. StandardEvaluationContext는 SpEL의 모든 기능을 제공하며 고도로 구성 가능하다.

SpEL의 일반적인 사용 예는 XML 또는 어노테이션 기반 빈 정의(bean definition)와 SpEL을 통합하는 것이다. SpEL을 사용하여 빈 인스턴스의 메타정보를 담고 있는 BeanDefinition에 접근할 수 있다. 표현식의 형태는 #{표현식문자열}이다. 스프링에서 애플리케이션 컨텍스트의 모든 빈은 빈 이름을 가진 미리 정의된 변수로 사용 가능하다. 예를 들어, 런타임 환경에 접근하기 위한 environment 이름의 빈(org.springframework.core.env.Environment 타입)과 systemPropertiessystemEnvironment 이름의 빈(Map<String, Object> 타입)과 같은 표준 컨텍스트 빈이 여기에 해당된다.

또다른 대표적인 예로 특정 인스턴스(루트 객체)에 대해 표현식을 평가하는 것이 있다. 다음과 같이 인스턴스의 프로퍼티에 접근하여 값을 조회하거나 값에 대한 연산을 수행할 수 있다.

MyObject myObject = new MyObject();
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("프로퍼티명");
String name = (String) exp.getValue(myObject);


스프링 배치의 @JobScope, @StepScope와 SpEL

스프링 배치는 잡 또는 스텝 실행 시 특정 빈을 인스턴스화할 수 있도록 빈 스코프 설정 어노테이션인 @JobScope@StepScope를 제공한다. 잡 또는 스텝이 실행되는 동안에만 해당 빈 인스턴스가 존재하며 SpEL을 사용하여 빈의 프로퍼티에 접근할 수 있다.

빈 스코프를 설정하지 않고 SpEL을 사용하여 빈 프로퍼티에 접근하려는 경우 PropertyOrFieldReferencereadProperty() 메서드 호출 시 SpelEvaluationExeception이 발생한다. readProperty() 메서드는 PropertyAccessorcanRead()를 호출하여 리졸버 인스턴스가 대상 객체의 특정 프로퍼티에 접근 가능한지 확인하는데, 이때 사용되는 PropertyAccessor의 구현체 중 하나인 BeanExpressionContextAccessorcanRead() 메서드는 BeanExpressionContextcontainsObject() 메서드를 호출함으로써 프로퍼티 접근 가능 여부를 확인한다. containsObject() 메서드는 빈 팩토리가 해당 프로퍼티명을 이름으로 갖는 빈 정의를 포함하고 있는지 확인하거나, 빈 스코프가 존재하는 경우 Scope 인터페이스의 resolveContextualObject() 메서드를 호출하여 해당 스코프의 빈이 특정 프로퍼티를 갖고 있는지 확인한다. SpEL의 표현식 평가 시점에 빈 정의가 존재하지 않는다면 SpelEvaluationExeception이 발생한다.


참고

Comments