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}} работает корректно.
  • В @PreAuthorize SpEL вычисляется через прокси — 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» в реальном сервисе.

Sources

Related topics