Spring FrameworkMiddleTechnical
Что такое Spring Expression Language (SpEL)?
SpEL (Spring Expression Language) — язык выражений Spring для динамического обращения к свойствам бинов, вызова методов и условной логики прямо в аннотациях и XML-конфигурации.
Spring Expression Language (SpEL)
SpEL — мощный язык выражений, встроенный в экосистему Spring. Он позволяет обращаться к свойствам объектов, вызывать методы, выполнять арифметику, работать с коллекциями и условиями прямо в аннотациях, XML-конфигурации и программном коде. SpEL вычисляется во время выполнения, что делает конфигурацию динамической.
Синтаксис: основные конструкции
#{beanName.property}— доступ к свойству бина#{T(java.lang.Math).PI}— обращение к статическому члену класса#{systemProperties['user.home']}— системные свойства#{list.?[price > 100]}— фильтрация коллекции#{list.![name]}— проецирование коллекции (извлечение поля)${property.key}— значение изapplication.properties(не SpEL, а PropertyPlaceholder)
Использование в аннотациях
@Component
public class DiscountService {
// Значение из другого бина через SpEL
@Value("#{configBean.discountRate}")
private double discountRate;
// Арифметика
@Value("#{configBean.maxItems * 1.1}")
private int bufferSize;
// Условное выражение
@Value("#{systemProperties['env'] == 'prod' ? 'https://api.example.com' : 'http://localhost:8080'}")
private String apiUrl;
// Список строк
@Value("#{'admin,user,moderator'.split(',')}")
private List<String> roles;
}
SpEL в @PreAuthorize (Spring Security)
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
public UserDto getUser(@PathVariable Long userId) { /* ... */ }
@PostAuthorize("returnObject.ownerId == authentication.principal.id")
public Document getDocument(Long docId) { /* ... */ }
SpEL в @Cacheable
// Ключ кэша зависит от поля объекта
@Cacheable(value = "products", key = "#product.category + '-' + #product.id")
public ProductDto getProductInfo(Product product) { /* ... */ }
// Условное кэширование
@Cacheable(value = "reports", condition = "#size > 100")
public List<Report> generateReport(int size) { /* ... */ }
Программное использование SpEL
ExpressionParser parser = new SpelExpressionParser();
// Простое выражение
Expression expr = parser.parseExpression("'Hello World'.toUpperCase()");
String result = expr.getValue(String.class); // "HELLO WORLD"
// Контекст с переменными
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("discount", 0.15);
context.setRootObject(new Product("Laptop", new BigDecimal("1000")));
Expression priceExpr = parser.parseExpression("price * (1 - #discount)");
BigDecimal finalPrice = priceExpr.getValue(context, BigDecimal.class);
Подводные камни
- SpEL-выражения в
@Valueвычисляются при создании бина — ошибка в выражении (например, несуществующее свойство) вызоветSpelEvaluationExceptionпри старте контекста. #{...}и${...}— разные механизмы: первый — SpEL, второй — PropertyPlaceholder; их нельзя смешивать произвольно, хотя#{${prop}}работает корректно.- В
@PreAuthorizeSpEL вычисляется через прокси — self-invocation обходит проверку безопасности. - Программный
StandardEvaluationContextпо умолчанию даёт полный доступ к рефлексии Java — в пользовательском вводе это уязвимость RCE; используйтеSimpleEvaluationContextдля ненадёжных данных. - Фильтрация коллекции
.?[condition]работает только сList,Mapи массивами; дляSetбез конвертации результаты могут быть неожиданными. - Обращение к приватным полям через SpEL требует явного разрешения рефлексии — не работает с модульной системой Java 9+.
- Сложные SpEL-выражения в аннотациях трудно тестировать изолированно и отлаживать — предпочтительнее выносить логику в отдельные методы бинов.
Common mistakes
- Путать термин «spring expression language» с соседним механизмом Spring Framework.
- Не называть границу lifecycle, transaction, thread или request для «spring expression language».
- Игнорировать production-эффекты «spring expression language»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «spring expression language» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «spring expression language».
- Уточнить, какие настройки или API меняют «spring expression language» в реальном сервисе.