Учебные материалы

Урок 5: Язык выражений Spring

Этот урок освещает работу с Spring Framework и основан на оригинальной документации §9. Spring Expression Language (SpEL).

Что вы создадите

Вы создадите некоторое количество классов, в которых будет рассмотрена функциональность языка выражений Spring Framework.

Что вам потребуется

Настройка проекта

Предварительно добавьте следующие классы:

src/main/java/lessons/spel/Invertor.java

public class Inventor {
    @Value("#{systemProperties['user.name']}")
    private String nameValued;
    private String name;
    private String nationality;
    private String[] inventions;
    private Date birthdate;
    private PlaceOfBirth placeOfBirth;

    public Inventor(String name, String nationality) {
        GregorianCalendar c= new GregorianCalendar();
        this.name = name;
        this.nationality = nationality;
        this.birthdate = c.getTime();
    }

    public Inventor(String name, Date birthdate, String nationality) {
        this.name = name;
        this.nationality = nationality;
        this.birthdate = birthdate;
    }

    public Inventor() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNameValued() {
        return nameValued;
    }

    public void setNameValued(String nameValued) {
        this.nameValued = nameValued;
    }

    public String getNationality() {
        return nationality;
    }

    public void setNationality(String nationality) {
        this.nationality = nationality;
    }

    public Date getBirthdate() {
        return birthdate;
    }

    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }

    public PlaceOfBirth getPlaceOfBirth() {
        return placeOfBirth;
    }

    public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
        this.placeOfBirth = placeOfBirth;
    }

    public void setInventions(String[] inventions) {
        this.inventions = inventions;
    }

    public String[] getInventions() {
        return inventions;
    }
}

src/main/java/lessons/starter/ExpressionLanguageStarter.java

public class ExpressionLanguageStarter {

    private static ExpressionLanguageStarter starter;

    private static final Logger logger = LogManager.getLogger(ExpressionLanguageStarter.class);

    public static void main(String[] args) throws NoSuchMethodException {
        logger.info("Starting configuration...");

        starter = new ExpressionLanguageStarter();
    }
}

Введение

Язык выражений Spring(далее SpEL) является мощным языком выражений, который поддерживает запросы и динамическую манипуляцию графа объекта. Синтаксис SpEL похож на Unified EL, но предоставляет дополнительные возможности, такие как вызов методов и базовую функциональность строковой шаблонизации. Несмотря на существование аналогичных языков, таких как OGNL, MVEL, и JBoss EL, SpEL был создан для предоставления сообществу Spring единого хорошо поддерживаемого языка выражений для всех продуктов Spring. Однако несмотря на это, возможно использовать и другие языки выражений при необходимости, либо использовать независимо от продуктов Spring.

Инструменты для использования

Основные классы и интерфейсы использования SpEL расположены в пакете org.springframework.expression и его подпакетах. Интерфейс ExpressionParser отвечает за разбор строки выражения. В примере ниже строковый литерал выражения заключен в одинарные кавычки. Интерфейс Expression отвечает за вычисление ранее определенной стрроки выражения.

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();

Метод getValue может принимать в качестве аргумента класс, к которому будет приведено вычисленное значение выражения, в случае невозможности приведения, бросается исключение EvaluationException. Чаще всего SpEL используется для вычисления выражения в отношении какого-либо объекта. Для этого имеются два способа получения значения из выражения, использование которых зависит от того, будет ли изменяться объект между вызовами вычисления выражения. При использовании StandardEvaluationContext подразумевается, что корневой объект вряд ли когда изменится, поскольку сборка StandardEvaluationContext при многоразовом использовании довольно дорогостоящая операция:

Inventor tesla = new Inventor("Nikola Tesla", new Date(), "Serbian");

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");

EvaluationContext context = new StandardEvaluationContext(tesla);
String name = (String) exp.getValue(context);

name = (String) exp.getValue(tesla);//Используется внутренний StandardEvaluationContext

Интерфейс EvaluationContext используется для вычисления знапчения выражений из свойств, методов, полей и помогает выполнять преобразования типов. Spring предоставляет реализацию StandardEvaluationContext, которая использует рефлексию для манипуляции объекта, кешируя экземпляры java.lang.reflect.Method/Field/Constructor для увеличения производительности.

С помощью метода setRootObject() указывается корневой объект, к которому будут применяться выражения, в методы setVariable() и registerFunction() передаются переменные и функции соответственно, которые будут использоваться в выражении. Регистрируя собственные ConstructorResolver, MethodResolver и PropertyAccessor, вы можете расширить функционал SpEL вычисления выражений.

Для настройки возможностей разбора выражений используется SpelParserConfiguration. Объект конфигурации контролирует поведение некоторых компонентов выражения.

class Demo {
    public List<String> list;
}

// Включение:
// - автоматическая инициализация null сылок
// - автоматическое "выращивание" коллекции
SpelParserConfiguration config = new SpelParserConfiguration(true,true);
ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
Object o = expression.getValue(demo);
logger.info("parserConfig# Expression value: " + demo.list.size()); //4
logger.info("parserConfig# Expression value: " + o); // пустая строка

При использовании выражений обычно подразумевается большая гибкость в процессе вычисления значений в ущерб производительности. В целях увеличения производительности используется компилятор выражений. Он генерирует настоящий Java класс на лету, поведение которого соответствует вычисляемому выражению. Поскольку само выражение не типизируемо, то компилятор исходит из результатов интерпретации и компиляции выражения. Если взять к примеру выражение someArray[0].someProperty.someOtherProperty < 0.1, которое предполагает доступ к элементу массива, разыменовании свойства и числовой операции, то показатели производительности могут существенноотличаться. На примере такого выражения 5000 итераций вычисления без компилятора может составлять 75 миллисекунд, а с компилятором - 3 миллисекунды.

По умолчанию компилятор не включен, для его включения существуют два способа: аргуметом в конструкторе SpelParserConfiguration или через системное свойство spring.expression.compiler.mode. Существуют 3 режима компиляции выражений:

  • OFF - компилятор отключен. Режим по умолчанию
  • IMMEDIATE - компиляция выражения происходит как можно скорее, обычно после первой интерпретации
  • MIXED - сначала выражение вычисляется интерпретатором, но после некоторого количества вычислений происходит переключение на режим компиляции, в котором выражение компилируется

На момент написания урока, компилятор SpEL имеет некоторые ограничение на используемые выражения для компиляции, которые приведены в документации

SpEL может быть использовано вашей конфигурации, в частности, с использованием аннотации @Value для полей, методов и конструкторов ваших бинов, в параметр которой передается необходимое выражение, вычисляемое в процессе создания бина.

Возможности SpEL

SpEL поддерживает следущую функциональность:

  • Символьные выражения
  • Булевые и реляционные операторы
  • Регулярные выражения
  • Class выражения
  • Доступ к свойствам, массивам, спискам, картам
  • Вызов методов
  • Вызов конструкторов
  • Встроенные списки и карты
  • Тернарные операторы
  • Переменные
  • Пользовательские функции
  • Шаблонные выражения
  • и др.

Примеры использования выражений вы можете увидеть в методе lessons.starter.ExpressionLanguageStarter#features().

Итог

В данном уроке вы познакомились с языком выражений Spring, его возможностях и применения.

comments powered by Disqus