Главная » Spring » Обработка коллекций на языке SpEL

0

Одни из самых необычных особенностей языка SpEL связаны с обработкой коллекций. Сослаться на отдельный элемент коллек- ции в языке SpEL можно точно так же, как в языке Java. Но в SpEL имеется мощный механизм выборки элементов коллекций на осно- ве значений их свойств. Он также позволяет извлекать значения свойств элементов коллекций и составлять из них новые коллекции.

Для примера предположим, что имеется некоторый класс City, представленный ниже (для экономии места из объявления были исключены методы доступа):

package  com.habuma.spel.cities; public class  City  {

private String name; private  String  state; private int population;

}

И что требуется сконфигурировать список объектов City с по- мощью элемента <util:list>, как показано ниже.

<util:list id="cities">

<bean  class="com.habuma.spel.cities.City"

p:name="Chicago"  p:state="IL"  p:population="2853114"/>

<bean     class="com.habuma.spel.cities.City"   p:name="Atlanta"  p:state="GA"  p:population="537958"/>

<bean  class="com.habuma.spel.cities.City"

p:name="Dallas"  p:state="TX"  p:population="1279910"/>

<bean  class="com.habuma.spel.cities.City"

p:name="Houston" p:state="TX" p:population="2242193"/>

<bean     class="com.habuma.spel.cities.City" p:name="Odessa" p:state="TX" p:population="90943"/>

<bean  class="com.habuma.spel.cities.City"

p:name="El  Paso"  p:state="TX"  p:population="613190"/>

<bean     class="com.habuma.spel.cities.City" p:name="Jal"  p:state="NM"  p:population="1996"/>

<bean  class="com.habuma.spel.cities.City"

p:name="Las Cruces" p:state="NM" p:population="91865"/>

</util:list>

Элемент <util:list> определен в пространстве имен util фрейм- ворка Spring. Фактически он создает компонент типа java.util.List, содержащий все значения или компоненты, перечисленные в нем. В данном случае это список из восьми компонентов City.

Язык SpEL имеет несколько удобных операторов для работы с коллекциями, подобными этой.

Доступ к элементам коллекции

Чаще всего бывает необходимо извлечь из списка единственный элемент и внедрить его в свойство:

<property name="chosenCity" value="#{cities[2]}"/>

В данном случае я отобрал третий элемент из списка cities (отсчет элементов начинается с нуля) и внедрил его в свойство chosenCity. Для остроты ощущений можно также попробовать выбрать элемент списка случайным образом:

<property name="chosenCity" value="#{cities[T(java.lang.Math).random()    *    cities.size()]}"/>

В любом случае для доступа к элементу коллекции по его индексу должны использоваться квадратные скобки ([]).

Оператор [] также можно использовать для извлечения элемен- тов из отображений java.util.Map. Например, представим, что объ- екты City находятся в отображении (Map), где роль ключей играют названия городов. В этом случае элемент с городом Dallas (Даллас) можно извлечь так:

<property  name="chosenCity"  value="#{cities[‘Dallas’]}"/>

Другой пример применения оператора [] – извлечение значения из коллекции типа java.util.Properties. Например, предположим, что в Spring необходимо загрузить файл с настройками свойств, исполь- зуя элемент <util:properties>, как показано ниже:

<util:properties  id="settings" location="classpath:settings.properties"/>

Здесь компонент settings будет иметь тип java.util.Properties и содержать все элементы, имеющиеся в файле, с именами settings. properties. Благодаря SpEL можно получить доступ к свойствам в этом файле, как если бы они были элементами отображения типа Map. Например, ниже показано, как с помощью SpEL можно прочи- тать свойство twitter.accessToken из компонента settings:

<property  name="accessToken"  value="#{settings[‘twitter.accessToken’]}"/>

Помимо чтения свойств из коллекции <util:properties>, фрейм- ворк Spring обеспечивает доступ в SpEL к двум специальным свой- ствам: systemEnvironment  и systemProperties.

Свойство systemEnvironment содержит все переменные окружения системы, в которой выполняется приложение. Это обычная коллек- ция типа java.util.Properties, поэтому для доступа к ее элементам по ключам можно использовать квадратные скобки. Например, в моей системе MacOS X я могу внедрить путь к домашнему каталогу поль- зователя в свойство компонента, как показано ниже:

<property  name="homePath"  value="#{systemEnvironment[‘HOME’]}"/>

Свойство systemProperties, в свою очередь, содержит все парамет- ры, которые были установлены при запуске приложения (обычно с помощью ключа -D). То есть, если виртуальная машина JVM бы- ла запущена с параметром -Dapplication.home=/etc/myapp, это значение можно внедрить в свойство homePath с помощью следующего выра- жения на языке SpEL:

<property   name="homePath"   value="#{systemProperties[‘application.home’]}"/>

Стоит также отметить, хотя это и не имеет прямого отношения к работе с коллекциями, что оператор [] можно применять к стро- ковым значениям для извлечения одиночных символов. Например, следующее выражение вернет "s":

‘This is a test'[3]

Возможность доступа к отдельным элементам коллекций сама по себе удобна. Но в выражениях на языке SpEL имеется также возмож- ность отбирать элементы коллекций, соответствующие определен- ным критериям. Попробуем сделать такую выборку из коллекции.

Выборка элементов коллекций

Допустим, что необходимо сократить список городов, оставив в нем только города с населением больше 100 000 человек. Один из способов заключается в том, чтобы внедрить весь компонент cities в свойство другого компонента и возложить на него все бремя от- сеивания маленьких городов. Однако язык выражений SpEL пред- лагает более простое решение – оператор выборки (.?[]), как по- казано ниже:

<property  name="bigCities"  value="#{cities.?[population  gt  100000]}"/>

Оператор выборки создаст новую коллекцию, которая будет включать элементы оригинальной коллекции, соответствующие критерию выбора, заключенному в квадратные скобки. В данном случае в свойство bigCities будет внедрен список городов (объектов City) с населением больше 100 000 человек.

В языке SpEL имеются также другие операторы выборки, .^[] и

.$[], позволяющие получить первый и последний (соответственно) элементы в выборке из коллекции. Например, чтобы получить пер- вый крупный город из коллекции cities:

<property name="aBigCity" value="#{cities.^[population gt 100000]}"/>

В процессе выборки объекты никак не упорядочиваются, поэто- му в свойство aBigCity будет внедрен объект City, представляющий город Чикаго (Chicago). Аналогично объект City, представляющий город Эль-Пасо (El Paso), можно выбрать следующим образом:

<property name="aBigCity" value="#{cities.$[population gt 100000]}"/>

Чуть ниже мы вернемся к проблеме выборки элементов из кол- лекций. Но сначала посмотрим, как отображаются свойства из ори- гинальной коллекции в новую коллекцию.

Отображение коллекций

Отображение коллекций связано с выбором определенного свой- ства каждого элемента оригинальной коллекции и помещением его в новую коллекцию. Оператор отображения (.![]) в языке SpEL вы- полняет именно эту операцию.

Например, предположим, что на основе списка объектов City не- обходимо получить список строк с именами городов. Чтобы полу-

чить такой список, можно выполнить внедрение в свойство cityNames, как показано ниже:

<property  name="cityNames"  value="#{cities.![name]}"/>

В результате выполнения этого выражения свойству cityNames будет присвоен список объектов String с такими значениями, как Chicago, Atlanta, Dallas и т. д. Свойство name в квадратных скобках определяет, какие элементы будет содержать получившийся в ре- зультате список.

Но возможности отображения не ограничиваются отображением одиночных свойств. С небольшими дополнениями к предыдущему примеру можно получить список городов и названий штатов:

<property name="cityNames" value="#{cities.![name + ‘, ‘ + state]}"/>

Теперь свойство cityNames будет содержать список значений, таких как «Chicago, IL», «Atlanta, GA» и «Dallas, TX».

И в последнем примере попробуем объединить операции создания выборки из коллекции и отображения. Ниже показано, как можно внедрить в свойство cityNames список имен только крупных городов:

<property  name="cityNames"

value="#{cities.?[population gt 100000].![name + ‘, ‘ + state]}"/>

Поскольку результатом операции выборки является новый спи- сок объектов City, ничто не мешает отобразить его в новую коллек- цию, содержащую имена всех крупных городов.

Этот пример демонстрирует возможность конструирования инте- ресных (и сложных) выражений на языке SpEL из более простых подвыражений. Нетрудно понять, насколько широкие перспективы открывает эта возможность. Но не нужно обладать развитым вооб- ражением, чтобы понять, что она также еще и опасна. Выражения на языке SpEL – это всего лишь строки, которые сложно тестировать и для которых не поддерживается подсветка синтаксиса в интегри- рованных средах разработки.

Я рекомендую использовать выражения на языке SpEL везде, где они могут упростить внедрение, которое сложно (если вообще возможно) реализовать иными способами. Но будьте внимательны, не переусердствуйте. Не поддавайтесь искушению перенести мак- симально возможный объем логики в выражения на языке SpEL.

Мы еще вернемся к выражениям на языке SpEL позднее и рас- смотрим возможность их применения в ситуациях, не связанных с конфигурированием компонентов. В главе 4 я вырву выражения SpEL из XML-файлов и задействую их в операциях внедрения че- рез аннотации. Кроме того, в главе 10 будет показано, насколько важную роль играют выражения на языке SpEL в последней версии фреймворка Spring Security.

В заключение

В центре фреймворка Spring Framework находится контейнер Spring. В состав Spring входят несколько реализаций контейнера, но все они подразделяются на две категории. BeanFactory – самый прос- той вид контейнера, обеспечивает основу DI и службы связывания компонентов. Но когда возникает потребность в контейнере с расши- ренными возможностями, используется контейнер ApplicationContext.

В этой главе вы увидели, как связывать компоненты друг с другом внутри контейнера Spring. Обычно связывание внутри контейнера Spring выполняется с использованием XML-файла. Этот XML-файл содержит конфигурационную информацию для всех компонентов приложения, а также информацию, которая помогает контейнеру выполнять внедрение зависимостей, связывая компоненты друг с другом.

Теперь, когда вы знаете, как реализовать связывание компонен- тов внутри XML-файлов, я покажу, как организовать связывание с минимальным привлечением кода разметки XML. В главе 4 мы познакомимся с преимуществами автоматического связывания и аннотациями, позволяющими уменьшить объем конфигурационных XML-файлов в приложениях на основе фреймворка Spring.

Источник:   Уоллс К., Spring в действии. – М.: ДМК Пресс, 2013. – 752 с.: ил.

По теме:

  • Комментарии