티스토리 뷰
[SpringFramework] SpEL (Spring Expression Language, 스프링 표현식)
Jaime.Lee 2021. 7. 29. 14:56
SpEL (Spring Expression Langauge) 이란?
스프링 표현식은 SpEL
로 표기하고 객체 그래프를 조회하고 조작하는 기능을 제공합니다.
Unified EL과 비슷하지만 메소드 호출과 문자열 템플릿 기능도 제공합니다.
자바에서 사용할 수 있는 표현식은 여러 가지(OGNL: Object Graph Navigation Language, MVEL: MVFlEX Expression Language, JBOss EL)가 있지만 SpEL
은
스프링 프로젝트 내에서 사용할 용도로 만든 표현식입니다.
스프링 3 버전부터 지원하고 있습니다.
SpEL을 사용하는 스프링 프로젝트
SpEL
은 스프링 프로젝트 전반적으로 모두 사용되지만 가장 비중있게 사용하는 부분은 아래와 같습니다.
- @Value
- @ComditionalOnExpression
- spring-security
- @PreAuthorize
- @PostAuthorize
- @PreFilter
- @PostFilter
- spring-data
- @Query
- Thymeleaf
주로 애너테이션의 속성으로 활용됩니다.
SpEL 구성
package io.lcalmsky.spel;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
@SpringBootApplication
public class SpelApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpelApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
ExpressionParser expressionParser = new SpelExpressionParser();
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
Expression expression = expressionParser.parseExpression("");
Object value = expression.getValue();
}
}
SpEL 사용
표현식을 사용하기 위해서는 #{}
를, properties
를 참조할 때는 ${}
를 사용합니다.
콜론(:
)을 이용해 기본 값을 명시할 수 있고 표현식과 참조를 동시에 사용할 수 있습니다.
간단히 소스 코드로 작성해보겠습니다.
먼저 application.properties
(또는 application.yml
)를 작성합니다.
스프링 웹 기능을 사용하지 않을 것이므로 관련 설정을 off 하였습니다.
application.properties
spring.main.web-application-type=none
foo.bar=100
foo.baz=string
foo.qux=true
다음으로 표현식 사용을 위해 테스트 클래스 작성을 따로 하지 않고 기본 클래스에 내용을 추가하였습니다.
SpelApplication.java
package io.lcalmsky.spel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpelApplication implements CommandLineRunner { // (1)
public static void main(String[] args) {
SpringApplication.run(SpelApplication.class, args);
}
@Value("#{1 + 1}") // (2)
private int value;
@Value("#{'heungmin' + ' ' + 'Son'}") // (3)
private String name;
@Value("#{1 + 1 eq 2 }") // (4)
private boolean equals;
@Value("${foo.bar}") // (5)
private int bar;
@Value("${foo.baz}") // (6)
private String baz;
@Value("${foo.qux}") // (7)
private boolean qux;
@Value("${foo.quux:default value}") // (8)
private String quux;
@Override
public void run(String... args) throws Exception {
// (9)
System.out.println("expressions");
System.out.println("value = " + value);
System.out.println("name = " + name);
System.out.println("equals = " + equals);
System.out.println("references");
System.out.println("bar = " + bar);
System.out.println("baz = " + baz);
System.out.println("qux = " + qux);
System.out.println("quux = " + quux);
}
}
(1)
CommandLineRunner
를 구현해 앱 실행 시run
메서드가 호출되게 하였습니다.
(2)1 + 1
과 같은 숫자를 이용한 표현이 가능합니다.
(3) 문자열을 이용한 표현이 가능합니다.
(4)boolean
표현이 가능합니다.
(5)(6)(7)properties
의 속성을 읽어와 주입하는데 타입을 자동으로 매칭시켜줍니다.
(8)properties
에 해당 값이 없으면 기본 값을 이용합니다.
(9) 제대로 연산이 수행되고 참조가 되는지 확인하기 위해 값을 출력합니다.
앱을 실행시켜보면 정상적으로 연산이 수행되고 값을 읽어왔음을 확인할 수 있습니다.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.3)
2021-07-29 14:15:22.784 INFO 33062 --- [ main] io.lcalmsky.spel.SpelApplication : Starting SpelApplication using Java 11.0.11 on Jungminui-iMac.local with PID 33062 (/Users/jaime/git-repo/jaime-notes/spel/build/classes/java/main started by jaime in /Users/jaime/git-repo/jaime-notes)
2021-07-29 14:15:22.787 INFO 33062 --- [ main] io.lcalmsky.spel.SpelApplication : No active profile set, falling back to default profiles: default
2021-07-29 14:15:23.437 INFO 33062 --- [ main] io.lcalmsky.spel.SpelApplication : Started SpelApplication in 1.227 seconds (JVM running for 1.911)
expressions
value = 2
name = heungmin Son
equals = true
references
bar = 100
baz = string
qux = true
quux = 'default value'
조금 더 응용해 보겠습니다.
@Value("#{${foo.quux:'default value'}.replace(' ', '')}") // (1)
private String quuxWithoutSpace;
@Override
public void run(String... args) throws Exception {
// 생략
System.out.println("quuxWithoutSpace = " + quuxWithoutSpace);
}
(1)
foo.quux
를properties
에서 읽어오고 그 값이 없으면 기본 값default value
로 대체한 뒤 표현식을 이용해 공백을 없애줍니다.
실행한 결과는 아래와 같습니다.
quuxWithoutSpace = defaultvalue
SpEL 동작 방식
SpEL Parser
를 이용해 표현식을 파싱한 뒤 표현식에서 값을 추출합니다.
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
// 생략
{
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("");
Object value = expression.getValue();
}
getValue()
는 타입을 지정하지 않은 경우 Object
로, 타입을 지정할 경우 해당 타입으로 반환하게 됩니다.
타입이 맞지 않을 경우 TypeMismatchException
이 발생합니다.
List
, Map
등의 타입으로도 매핑할 수 있고 메서드나 연산자 등을 사용할 수 있습니다.
자세한 내용은 공식 문서에 나와있으니 참고하시면 될 거 같습니다.
Bean 참조
표현식을 이용해 Bean
을 바로 참조할 수 있습니다.
Properties.java
package io.lcalmsky.spel;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Properties {
private int number = 1;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
@Configuration
, @Component
등 빈 등록을 위한 애너테이션을 추가한 뒤 필드에 대한 getter/setter
를 추가해주면,
// 생략
@Value("#{properties.number}")
private int number;
// 생략
{
System.out.println("number from bean = " + number);
}
다른 클래스에서 표현식을 이용해 해당 값을 주입할 수 있습니다.
이 부분을 따로 다룬 이유는 바로 프레임워크나 라이브러리에서 많이 쓰이는 방식이기 때문입니다.
@Enable
로 시작하는 애너테이션을 많이 보셨을텐데 이러한 애너테이션을 추가하게되면 @Import
나 @ComponentScan
을 이용해 클래스나 패키지에 있는 Bean
들을 모두 등록하게되고 이 때 자바 클래스 설정을 사용하는 경우 기본 값이 제공되기 때문에 별도의 설정 없이도 동작하게 됩니다.
그리고 설정을 변경해야 하는 경우 properties
에서 bean
의 이름과 속성을 탐색해서 수정해 줄 수 있는데 내부적으로 setter
가 정의되어있기 때문입니다.
맨 위에 객체 그래프를 조회하고 조작한다고 설명했는데 이런식의 bean
을 통한 설정을 잘 제공한다면 사용하는 개발자에게 엄청난 편의성을 제공할 수 있습니다.
물론 라이브러리를 사용하듯이 다루려면 위 내용 이외에 다른 기능들도 같이 사용해야 합니다만 기본 원리에 대해 설명하고자 추가로 설명하였습니다.
'SpringBoot' 카테고리의 다른 글
커스텀 애너테이션에 @AliasFor 활용하기 (0) | 2022.09.01 |
---|---|
스프링 부트 설정 파일 우선 순위 (0) | 2022.06.26 |
스프링 부트에 graceful shutdown 적용하기 (0) | 2021.06.11 |
Spring Boot로 효율적인 Docker Image 만들기 (0) | 2021.06.11 |
Spring Boot 프로젝트를 웹 서버 기동 없이 실행시키기 (0) | 2020.07.02 |
- Total
- Today
- Yesterday
- 스프링 부트 튜토리얼
- leetcode
- Jackson
- JSON
- spring boot app
- 함께 자라기 후기
- Spring Boot Tutorial
- Spring Data JPA
- JPA
- r
- 스프링 부트 애플리케이션
- 스프링 부트
- spring boot application
- Spring Boot
- Linux
- QueryDSL
- 클린 아키텍처
- @ManyToOne
- 스프링부트
- 헥사고날 아키텍처
- 스프링 부트 회원 가입
- Spring Boot JPA
- intellij
- proto3
- 함께 자라기
- Java
- gRPC
- 알고리즘
- spring boot jwt
- 스프링 데이터 jpa
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |