[java]Spel(Spring Expression Language) 관련
1. Overview
- SpEL(Spring Expression Language)은 expression language 이고 런타임에 동작한다.
-
XML이나 Spring configurations기반의 annotation 으로 사용할수 있다.
- 언어로 사용할수 있는 몇가지 operators이 아래와 같이 있다.
Type | Operators |
---|---|
Arithmetic | +, -, *, /, %, ^, div, mod |
Relational | <, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge |
Logical | and, or, not, &&, ||, ! |
Conditional | ?: |
Regex | matches |
2. Operators
- 아래 예는 annotation기반의 설정을 기반으로 한다 XML 설정은 다루지 않을 예정이다. For these examples, we will use annotation-based configuration. More details about XML configuration can be found in later sections of this article.
- SpEL 표현은 # 기호고 시작하고 중괄호로 묶고(#{expression), 속성은 $ 기호로 시작하여 중괄호로 묶는 방식을 택하고있다. (${property.name}.). 속성 자리 표시자는 SpEL 표현식을 포함할수없지만 표현식에는 속성 참조가 포함될수있다.
- 간단하게 아래 예제를 보자
- 이 예제에서 someProperty 값이 2라고 가정하면 결과 표현식은 2 +2가 되어 4로 평가된다.
#{${someProperty} + 2}
- 이 예제에서 someProperty 값이 2라고 가정하면 결과 표현식은 2 +2가 되어 4로 평가된다.
2.1. 산술 Operators
- 모든 기본 산술 연산자가 지원된다.
@Value("#{19 + 1}") // 20
private double add;
@Value("#{'String1 ' + 'string2'}") // "String1 string2"
private String addString;
@Value("#{20 - 1}") // 19
private double subtract;
@Value("#{10 * 2}") // 20
private double multiply;
@Value("#{36 / 2}") // 19
private double divide;
@Value("#{36 div 2}") // 18, the same as for / operator
private double divideAlphabetic;
@Value("#{37 % 10}") // 7
private double modulo;
@Value("#{37 mod 10}") // 7, the same as for % operator
private double moduloAlphabetic;
@Value("#{2 ^ 9}") // 512
private double powerOf;
@Value("#{(2 + 2) * 2 + 9}") // 17
private double brackets;
- 나누기는 /, 모듈로는 % 명령어로도 표현할수있고, + 명령어는 는 문자열 합치는 용도로도 사용 가능하다.
2.2. Relational and Logical Operators
@Value("#{1 == 1}") // true
private boolean equal;
@Value("#{1 eq 1}") // true
private boolean equalAlphabetic;
@Value("#{1 != 1}") // false
private boolean notEqual;
@Value("#{1 ne 1}") // false
private boolean notEqualAlphabetic;
@Value("#{1 < 1}") // false
private boolean lessThan;
@Value("#{1 lt 1}") // false
private boolean lessThanAlphabetic;
@Value("#{1 <= 1}") // true
private boolean lessThanOrEqual;
@Value("#{1 le 1}") // true
private boolean lessThanOrEqualAlphabetic;
@Value("#{1 > 1}") // false
private boolean greaterThan;
@Value("#{1 gt 1}") // false
private boolean greaterThanAlphabetic;
@Value("#{1 >= 1}") // true
private boolean greaterThanOrEqual;
@Value("#{1 ge 1}") // true
private boolean greaterThanOrEqualAlphabetic;
- 모든 관계 연산자에도 알파벳으로 사용할수있는데 XML 구반 구성에는 몇가지것을을 사용할수없다(<, <=, >, >=)
- 대신 lt (보다 작음), le (보다 작거나 같음l), gt (보다 큼), or ge (크거나 같음) 을 사용할 수있다.
2.3. Logical Operators
@Value("#{250 > 200 && 200 < 4000}") // true
private boolean and;
@Value("#{250 > 200 and 200 < 4000}") // true
private boolean andAlphabetic;
@Value("#{400 > 300 || 150 < 100}") // true
private boolean or;
@Value("#{400 > 300 or 150 < 100}") // true
private boolean orAlphabetic;
@Value("#{!true}") // false
private boolean not;
@Value("#{not true}") // false
private boolean notAlphabetic;
2.4. Conditional Operators
- 조건 연산자는 일부 조건에 따라 다른 값을 주입하는데 사용된다.
@Value("#{2 > 1 ? 'a' : 'b'}") // "a"
private String ternary;
- 삼항 연산자는 if-then-else 조건부 논리를 수행할때 사용되고, 일부 변수가 null인지 아닌지 확인한 다음 변수 값이나 기본값을 반환하는데 사용된다.
@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}")
private String ternary;
- Elvis 연산자는 Groovy 언어에서 사용되는 경우에 삼항연산자 구문을 단축하는 방법이다. SpEL에서도 사용할수 있다.
- 아래의 예제는 위의 예제와 동일하다
@Value("#{someBean.someProperty ?: 'default'}") // Will inject provided string if someProperty is null private String elvis;
2.5. Using Regex in SpEL
- matches operator는 정규식을 통해 스트링 매칭을 확인할수 있다
@Value("#{'100' matches '\\d+' }") // true
private boolean validNumericStringResult;
@Value("#{'100fghdjf' matches '\\d+' }") // false
private boolean invalidNumericStringResult;
@Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") // true
private boolean validAlphabeticStringResult;
@Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") // false
private boolean invalidAlphabeticStringResult;
@Value("#{someBean.someValue matches '\d+'}") // true if someValue contains only digits
private boolean validNumericValue;
2.6. Accessing List and Map Objects
- SpEL를 이용해 Map이나 List에 SpEL을 사용해 접근가능하다. workersHolder 란 새로운 빈을 생성한 아래 예제를 살펴보자.
@Component("workersHolder")
public class WorkersHolder {
private List<String> workers = new LinkedList<>();
private Map<String, Integer> salaryByWorkers = new HashMap<>();
public WorkersHolder() {
workers.add("John");
workers.add("Susie");
workers.add("Alex");
workers.add("George");
salaryByWorkers.put("John", 35000);
salaryByWorkers.put("Susie", 47000);
salaryByWorkers.put("Alex", 12000);
salaryByWorkers.put("George", 14000);
}
//Getters and setters
}
이제 아래처럼 접근이 가능하다.
@Value("#{workersHolder.salaryByWorkers['John']}") // 35000
private Integer johnSalary;
@Value("#{workersHolder.salaryByWorkers['George']}") // 14000
private Integer georgeSalary;
@Value("#{workersHolder.salaryByWorkers['Susie']}") // 47000
private Integer susieSalary;
@Value("#{workersHolder.workers[0]}") // John
private String firstWorker;
@Value("#{workersHolder.workers[3]}") // George
private String lastWorker;
@Value("#{workersHolder.workers.size()}") // 4
private Integer numberOfWorkers;
3. 활용예시
- 아래 예제와 같이 SpEL을 사용하여 defaultValue 를 설정할 수 있음
- 입력값이 없는 경우 현재 날짜보다 3개월 전을 default 로 세팅함
@GetMapping(value = "/path/...")
public String test(
@RequestParam(name = "begin", required = false, defaultValue = "#{T(java.time.OffsetDateTime).now().minusMonths(3)}") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime begin,
@RequestParam(name = "end", required = false, defaultValue = "#{T(java.time.OffsetDateTime).now()}") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime end) {
.
.
}
참고
https://www.baeldung.com/spring-expression-language