-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeneral_Concepts.txt
More file actions
726 lines (434 loc) · 57.5 KB
/
Copy pathGeneral_Concepts.txt
File metadata and controls
726 lines (434 loc) · 57.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
========================================================================================================================
================================================= Контейнеры ===========================================================
========================================================================================================================
Фреймворк Spring имеет не один контейнер. В его состав входят несколько реализаций контейнера, которые подразделяются на
два разных типа. Фабрики компонентов (bean factories) (определяются интерфейсом org.springframework.beans.factory.BeanFactory)
самые простые из контейнеров, обеспечивающие базовую поддержку DI. Контекст приложений (application contexts)
(определяется интерфейсом org.springframework.context.ApplicationContext) основан на понятии фабрик компонентов и
реализует прикладные службы фреймворка, такие как возможность приема текстовых сообщений из файлов свойств и возможность
подписывать другие программные компоненты на события, возникающие в приложении.
========================================================================================================================
===================== В составе Spring имеется несколько разновидностей контекстов приложений. =========================
=================================== Три из них используются наиболее часто: ============================================
========================================================================================================================
1. ClassPathXmlApplicationContext – загружает определение контекста из XML-файла, расположенного в библиотеке
классов(classpath), и обрабатывает файлы с определениями контекстов как ресурсы;
2. FileSystemXmlApplicationContext – загружает определение контекста из XML-файла в файловой системе;
3. XmlWebApplicationContext – загружает определение контекста из XML-файла, содержащегося внутри веб-приложения.
========================================================================================================================
========================================== Жизненный цикл компонента ===================================================
========================================================================================================================
1. Spring создает экземпляр компонента.
2. Spring внедряет значения и ссылки на компоненты в свойства данного компонента.
3. Если компонент реализует интерфейс BeanNameAware , Spring передает идентификатор компонента методу setBeanName() .
4. Если компонент реализует интерфейс BeanFactoryAware , Spring вызывает метод setBeanFactory() , передавая ему саму
фабрику компонентов.
5. Если компонент реализует интерфейс ApplicationContextAware, Spring вызывает метод setApplicationContext() ,
передавая ему ссылку на вмещающий контекст приложения.
6. Если какие-либо из компонентов реализуют интерфейс BeanPost Processor , Spring вызывает их методы
postProcessBeforeInitialization() .
7. Если какие-либо из компонентов реализуют интерфейс InitializingBean , Spring вызывает их методы afterPropertiesSet() .
Аналогично, если компонент был объявлен с атрибутом init-method , вызывается указанный метод инициализации.
8. Если какие-либо из компонентов реализуют интерфейс BeanPostProcessor , Spring вызывает их методы postProcessAfterI
nitialization().
9. В этот момент компонент готов к использованию приложением и будет сохраняться в контексте приложения, пока он не
будет уничтожен.
10. Если какие-либо из компонентов реализуют интерфейс DisposableBean , Spring вызывает их методы destroy() . Аналогич-
но, если компонент был объявлен с атрибутом destroy-method, вызывается указанный метод.
========================================================================================================================
==================== Фреймворк Spring поддерживает несколько пространств имен XML, посредством =========================
=============================== которых выполняется настройка контейнера Spring ========================================
========================================================================================================================
aop Предоставляет элементы для объявления аспектов и для автоматического проксирования классов,
объявляемых аспектами с помощью аннотации @AspectJ
beans Пространство имен с основными элементами, позволяющими объявлять компоненты и определять
связи между ними
context Содержит элементы для конфигурирования контекста приложения Spring, включая возможность
автоматического определения и автоматического связывания компонентов, а также внедрения
объектов, которые не управляются фреймворком Spring непосредственно
jee Обеспечивает интеграцию с такими Java EE API, как JNDI и EJB
jms Предоставляет элементы для объявления POJO, управляемых сообщениями
lang Позволяет объявлять компоненты, реализованные как сценарии на языках Groovy, JRuby и BeanShell
mvc Включает такие возможности Spring MVC, как аннотированные контроллеры, контроллеры
представлений и обработчики
oxm Поддерживает возможность настройки механизма отображения объектов в XML
tx Содержит элементы настройки декларативных транзакций
util Набор различных вспомогательных элементов, включающий возможность объявления коллекций как
компонентов и поддержку элементов-заполнителей свойств
========================================================================================================================
================================ Создание компонентов с помощью фабричных методов ======================================
========================================================================================================================
Иногда единственный способ создать объект заключается в том, чтобы использовать статический фабричный метод.
Фреймворк Spring поддерживает возможность связывания компонентов, объявленных с помощью элементов <bean> , которые
содержат атрибут factory-method.
К счастью, элемент <bean> имеет атрибут factory-method , позволяющий определить статический метод, который должен
вызываться для создания экземпляра класса вместо конструктора.
========================================================================================================================
=========================================== Область действия компонента ================================================
========================================================================================================================
По умолчанию все компоненты Spring единичны. Когда контейнер передает компонент (либо через связывание, либо как
результат вызова метода контейнера getBean() ), всегда будет передан тот же самый экземпляр компонента. Однако иногда
бывает необходимо получить уникальный экземпляр компонента при каждом обращении. Как изменить единичный нрав Spring?
При объявлении <bean> компонента можно определить область его действия. Чтобы заставить фреймворк Spring создавать новый
экземпляр при каждом обращении, в объявление компонента следует добавить атрибут scope со значением prototype .
Фреймворк Spring позволяет определять различные области действия компонентов
без жесткого определения правил видимости в программном коде
singleton В каждом контейнере Spring может быть создан только один компонент (по умолчанию)
prototype Позволяет создавать произвольное количество компонентов (по одному на каждое обращение)
request Область действия компонента ограничивается HTTP-запросом. Может применяться только в веб-приложениях
Spring (например, использующих Spring MVC)
session Область действия компонента ограничивается HTTP-сеансом. Может применяться только в веб-приложениях
Spring (например, использующих Spring MVC)
global-session Область действия компонента ограничивается глобальным HTTP-сеансом. Может применяться
только в портлетах
Проницательный читатель заметит, что понятие единичных компонентов ограничено областью действия контекста Spring. В от-
личие от истинных классов-одиночек, гарантирующих существование единственного экземпляра на каждый загрузчик классов
(classloader), для единичных компонентов в Spring гарантируется только наличие единственного экземпляра компонента в
контексте приложения – ничто не мешает создать экземпляр того же класса традиционным способом или даже создать
несколько объявлений <bean> для одного и того же класса.
========================================================================================================================
========================================= Инициализация и уничтожение компонентов ======================================
========================================================================================================================
При создании компонента может потребоваться выполнить некоторые операции по его инициализации, чтобы привести его в при-
годное для использования состояние. Аналогично, перед уничтожением и удалением компонента из контейнера, может
потребоваться выполнить некоторые заключительные операции. Для этой цели фреймворк Spring позволяет определять методы
управления жизненным циклом компонента.
Чтобы определить методы, вызываемые при создании и уничтожении компонента, просто добавьте в элемент <bean> атрибуты
init-method и/или destroy-method . Атрибут init-method определяет метод, вызываемый сразу после создания экземпляра
компонента. Аналогично атрибут destroy-method определяет метод, вызываемый непосредственно перед удалением компонента
из контейнера.
-------------------------------- Интерфейсы InitializingBean и DisposableBean. -----------------------------------------
Фреймворк Spring обеспечивает альтернативный способ определения методов инициализации и уничтожения компонентов – реализовать
в классе компонента интерфейсы InitializingBean и DisposableBean. Компонентам, реализующим эти интерфейсы, предоставляется
возможность управлять этапами своего жизненного цикла, и они обрабатываются фреймворком Spring особым образом. Интерфейс
InitializingBean объявляет метод afterPropertiesSet(), который играет роль метода инициализации. А интерфейс
DisposableBean объявляет метод destroy(), который вызывается перед удалением компонента из контекста приложения.
Главным преимуществом использования этих интерфейсов управления жизненным циклом является автоматическое определение
контейнером Spring компонентов, реализующих эти интерфейсы, без каких-либо внешних настроек. А недостаток состоит в
образовании тесной связи между прикладными компонентами и Spring API. Именно по этой причине для реализации операций
инициализации и уничтожения компонентов я рекомендую использовать атрибуты init-method и destroy-method. Единственное,
где могут пригодиться интерфейсы управления жизненным циклом, – при разработке компонентов собственного фреймворка,
которые будут использоваться исключительно в контейнере Spring.
----------------------------------- Методы инициализации и уничтожения по умолчанию. -----------------------------------
Если в файле определения контекста необходимо определить множество компонентов с методами инициализации или уничтоже-
ния , имеющими одинаковые имена, можно не объявлять атрибуты init-method и destroy-method для каждого отдельного
компонента, а добавить атрибуты default-init-method и default-destroy-method в элемент <beans>.
Атрибут default-init-method определяет метод инициализации для всех компонентов в данном определении контекста.
Аналогично атрибут default-destroy-method определяет общий метод уничтожения для всех компонентов в определении контекста.
В данном случае фреймворку Spring предписывается инициализировать все компоненты, описанные в файле определения контекста,
вызовом метода turnOnLights() и уничтожать их вызовом метода turnOffLights() (если эти методы существуют – иначе ничего
не произойдет).
========================================================================================================================
======================================= Внедрение в свойства компонентов ===============================================
========================================================================================================================
------------------------------------------- Внедрение простых значений. ------------------------------------------------
Свойства компонента могут быть настроены в Spring с помощью элемента <property> . Элемент <property> во многом схож с
элементом <constructor-arg> , за исключением того, что вместо внедрения значений через аргументы конструктора элемент
<property> вызывает метод записи свойства.
Элемент <property> позволяет внедрять не только строковые значения. В атрибуте value можно также указывать числовые
(int, float, java.lang.Double и другие) и логические значения.
Фреймворк Spring автоматически определяет тип значения, опираясь на тип свойства. Поскольку свойство age имеет тип int,
Spring знает, что строку "37" следует преобразовать в целочисленное значение перед вызовом метода setAge() .
Использование элемента <property> прекрасно подходит для настройки простых свойств компонента, но DI – это больше, чем
присваивание жестко определенных значений. Настоящая ценность DI заключается в возможности связывания взаимодействующих
объектов, чтобы они не связывали себя сами.
--------------------------------------- Внедрение ссылок на другие компоненты ------------------------------------------
------------------------------------------- Внедрение внутренних компонентов -------------------------------------------
Как Java-разработчик вы, вероятно, хорошо знакомы с понятием внутренних классов – классов, которые определены в пределах
других классов. Аналогично и внутренние компоненты – это компоненты, которые определяются внутри других компонентов
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="Jingle Bells" />
<property name="instrument">
<bean class="org.springinaction.springidol.Saxophone" />
</property>
</bean>
Как видите, внутренний компонент определяется с помощью элемента <bean> , вложенного непосредственно в элемент <property>
свойства, куда он должен быть внедрен
Внутренние компоненты можно внедрять не только с помощью приема внедрения через методы записи. Внутренние компоненты
можно также внедрять через аргументы конструктора,
<bean id="duke" class="com.springinaction.springidol.PoeticJuggler">
<constructor-arg value="15" />
<constructor-arg>
<bean class="com.springinaction.springidol.Sonnet29" />
</constructor-arg>
</bean>
Обратите внимание, что внутренние компоненты не имеют атрибута id . Нет ничего противозаконного в том, чтобы добавить
атрибут id в объявление внутреннего компонента, однако в этом нет необходимости, потому что к внутреннему компоненту
никто и никогда не будет обращаться по имени. Это является основным недостатком внутренних компонентов: они не могут
быть повторно использованы. Внутренние компоненты могут использоваться только для однократного внедрения, и к ним не
могут обращаться другие компоненты.
Можно также заметить, что определение внутреннего компонента оказывает негативное влияние на удобочитаемость
конфигурационных XML-файлов.
========================================================================================================================
==================================== Связывание свойств с помощью пространства имен p ==================================
========================================================================================================================
Внедрение значений и ссылок в свойства компонентов с помощью элемента <property> не представляет большого труда. Тем не
менее пространство имен p фреймворка Spring позволяет использовать иной способ связывания свойства компонентов, не
требующий такого большого количества угловых скобок.
<bean id="kenny" class="com.springinaction.springidol.Instrumentalist"
p:song = "Jingle Bells"
p:instrument-ref = "saxophone" />
========================================================================================================================
============================================== Внедрение коллекций =====================================================
========================================================================================================================
Фреймворк Spring предлагает четыре типа элементов определе-
ния коллекций, которые пригодятся для конфигурирования значе-
ний, являющихся коллекциями.
Элементы <list> и <set> можно использовать для настройки свойств, которые являются массивами или одной из реализаций
java.util.Collection . Как будет показано чуть ниже, фактическая реализация коллекции, используемая для определения
свойства, имеет некоторое значение при выборе между элементами <list> и <set> . Оба элемента могут использоваться почти
взаимозаменяемо, со свойствами любого типа java.util.Collection .
--------------------------- Подобно тому как в Java имеется несколько видов коллекций, фреймворк Spring ----------------
------------------------------- так же позволяет внедрение нескольких видов коллекций ----------------------------------
<list> Связывание списка значений, допускаются повторяющиеся значения
<set> Связывание множества значений, гарантирует отсутствие повторяющихся значений
<map> Связывание коллекций пар имя/значение, где имя и значение могут быть значениями любых типов
<props> Связывание коллекций пар имя/значение, где имя и значение должны имеет строковый тип (String)
Что касается элементов <map> и <props> , они соответствуют коллекциям с интерфейсами java.util.Map и java.util.Properties
соответственно. Эти типы коллекций можно использовать, когда требуется коллекция, состоящая из пар ключ/значение.
Ключевым отличием между ними состоит в том, что при использовании элемента <props> ключи и значения в коллекции должны
быть значениями типа String, тогда как при использовании элемента <map> ключи и значения могут быть любых типов.
<bean id="hank" class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<list>
<ref bean="guitar" />
<ref bean="cymbal" />
<ref bean="harmonica" />
</list>
</property>
</bean>
-------------------------------------------------- List Set ------------------------------------------------------------
Элемент <list> содержит одно или более значений. Здесь элементы <ref> используются для определения ссылок на другие
компоненты в контексте Spring.
Однако внутри элемента <list> можно также использовать другие элементы, определяющие значения, включая <value> , <bean>
и <null/> . В действительности элемент <list> может содержать в себе другие элементы <list> как элементы многомерных списков.
В листинге 2.8 свойство instruments класса OneManBand имеет тип java.util.Collection , и для сохранения в нем значений
типа Instrument используются обобщения Java 5. Но элемент <list> может применяться к свойствам, которые могут быть любыми
реализациями java.util.Collection или массивом. Другими словами, элемент <list> можно было бы использовать, даже если
свойство instruments было бы объявлено как: "java.util.List<Instrument> instruments" или как: "Instrument[] instruments"
Как уже говорилось, элементы <list> и <set> могут использоваться для связывания значений с любой реализацией
java.util.Collection или массивом. Если свойство имеет тип java.util.Set , это еще не означает, что для его инициализации
следует обязательно использовать элемент <set> . Даже при том, что возможность инициализировать свойство типа java.util.List
с помощью элемента <set> может показаться странной, в действительности это вполне допустимо. В этом случае гарантируется
уникальность элементов списка List .
------------------------------------------------------------------------------------------------------------------------
Элемент <util:list> определен в пространстве имен util фреймворка Spring. Фактически он создает компонент типа
java.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>
----------------------------------------------------- Map --------------------------------------------------------------
Элемент <entry> в элементе <map> позволяет определить ключ и значение элемента отображения,
каждый из которых может быть простым значением или ссылкой на другой компонент
key Определяет ключ элемента отображения как строку
key-ref Определяет ключ элемента отображения как ссылку на компонент в контексте Spring
value Определяет значение элемента отображения как строку
value-ref Определяет значение элемента отображения как ссылку на компонент в контексте Spring
Интерфейс Properties служит примерно той же цели, что и класс Map, но ограничивает ключи и значения строковым типом.
<bean id="hank" class="com.springinaction.springidol.OneManBand">
<property name="instruments">
<props>
<prop key="GUITAR">STRUM STRUM STRUM</prop>
<prop key="CYMBAL">CRASH CRASH CRASH</prop>
<prop key="HARMONICA">HUM HUM HUM</prop>
</props>
</property>
</bean>
------------------------------------------------- Properties -----------------------------------------------------------
Элемент <props> создает отображение типа java.util.Properties, каждый член которого определяется элементом <prop> .
Каждый элемент <prop> имеет атрибут key , определяющий ключ члена отображения Properties , а значение определяется
содержимым элемента <prop> .
Это, пожалуй, самый сложный для обсуждения элемент конфигурации Spring, потому что термин «property» (свойство ) сильно
перегружен.
Здесь важно придерживаться следующих правил:
<property> – это элемент для внедрения значения или ссылки на компонент в «обычное» свойство компонента;
<props> – это элемент для определения коллекций типа java.util.Properties ;
<prop> – это элемент для определения члена коллекции <props> .
========================================================================================================================
============================================== Внедрение пустого значения ==============================================
========================================================================================================================
некоторые компоненты могут сами присваивать свойствам непустые значения по умолчанию.
Чтобы присвоить свойству значение null , достаточно просто воспользоваться элементом <null/> .
<property name="someNonNullProperty"><null/></property>
Другая причина для явного внедрения значения null в свойство – отменить автоматическое связывание значения свойства.
========================================================================================================================
======================================== Spring Expression Language, SpEL ==============================================
========================================================================================================================
В версии Spring 3.0 появилась возможность использовать язык выражений Spring (Spring Expression Language, SpEL) – мощный,
но краткий способ внедрения значений в свойства компонентов или аргументы конструкторов с помощью выражений, которые
вычисляются на этапе выполнения.
Язык SpEL обеспечивает массу интересных возможностей, среди которых:
получение ссылок на компоненты по их идентификаторам;
вызов методов и обращение к свойствам объектов;
математические и логические операции над значениями, а также операции отношения;
сопоставление с регулярными выражениями;
операции с коллекциями.
--------------------------------------------------- Литералы -----------------------------------------------------------
Простейшим выражением на языке SpEL является выражение, состоящее из одного литерального значения.
Конструкция #{} подсказывает фреймворку Spring, что ее содержимое является выражением на языке SpEL.
Эти выражения можно также смешивать с обычными значениями: <property name="count" value="#{5}"/>
На языке SpEL можно также определять вещественные числа. <property name="frequency" value="#{89.7}"/>
Числа могут записываться в научной форме записи.
Как показано в следующем фрагменте, который присваивает значение 10000.0 свойству capacity : <property name="capacity" value="#{1e4}"/>
Язык SpEL также поддерживает строковые литералы, которые могут заключаться в апострофы или кавычки.
Например, внедрение строкового литерала в свойство компонента можно записать так: <property name="name" value="#{'Chuck'}"/>
Если для определения значений XML-атрибутов используются апострофы, выражение на языке SpEL можно заключить в кавычки:
<property name='name' value='#{"Chuck"}'/>
Для определения логических значений можно использовать пару литералов true и false . Например, значение false можно
выразить так: <property name="enabled" value="#{false}"/>
Стоит также отметить, хотя это и не имеет прямого отношения к работе с коллекциями, что оператор [] можно применять к
строковым значениям для извлечения одиночных символов. Например, следующее выражение вернет "s" :
'This is a test'[3]
------------------------------- Ссылки на компоненты и обращение к их свойствам и методам ------------------------------
Другой основной особенностью выражений на языке SpEL является возможность ссылаться на другие компоненты по их иденти-
фикаторам . Например, с помощью выражения на языке SpEL можно внедрить компонент в свойство другого компонента:
<property name="instrument" value="#{saxophone}"/>
При конфигурировании компонента carl можно воспользоваться выражением на языке SpEL, чтобы скопировать произведение из
свойства song Кенни, как показано ниже:
<bean id="carl" class="com.springinaction.springidol.Instrumentalist">
<property name="song" value="#{kenny.song}" />
</bean>
Точно так же можно вызывать методы компонентов:
<property name="song" value="#{songSelector.selectSong()}"/>
Теперь предположим, что по некоторой причине Карл пожелал, чтобы название произведения передавалось ему в виде строки,
со-стоящей только из заглавных символов. Нет проблем... для этого достаточно вызвать метод toUpperCase() строкового
значения:
<property name="song" value="#{songSelector.selectSong().toUpperCase()}"/>
Этот трюк будет срабатывать всегда... пока метод selectSong() не вернет значение null . Если в какой-то момент selectSong()
вернет null , во время вычисления выражения возникнет исключение NullPointerException .
Чтобы избежать появления жуткого исключения NullPointer-Exception в выражении, следует задействовать защищенный оператор
доступа к свойству:
<property name="song" value="#{songSelector.selectSong()?.toUpperCase()}"/>
Здесь вместо одиночной точки ( . ) для вызова метода toUpperCase() используется оператор ?. . Этот оператор сначала
проверяет, не является ли пустым элемент слева, и только потом выполняет обращение к элементу справа. То есть если
selectSong() вернет значение null , тогда выражение не будет даже пытаться вызвать toUpperCase().
----------------------------------------------- Использование типов ----------------------------------------------------
Ключом к использованию методов класса и констант в языке SpEL является оператор T() . Например, чтобы обратиться к Java-
классу Math в выражении на языке SpEL, необходимо использовать оператор T() , как показано ниже:
T(java.lang.Math)
Результатом оператора T() , представленного здесь, является объект типа Class , представляющий класс java.lang.Math .
Этот результат можно было бы даже внедрить в свойство компонента типа Class . Но истинная ценность значения, возвращаемого
оператором T() , заключается в возможности доступа к статическим методам и константам указанного класса.
<property name="multiplier" value="#{T(java.lang.Math).PI}"/>
Аналогично с помощью оператора T() можно вызывать статические методы:
<property name="randomNumber" value="#{T(java.lang.Math).random()}"/>
--------------------------------------- Выполнение операций со значениями SpEL -----------------------------------------
Язык SpEL предлагает несколько операций, которые могут применяться к значениям в выражениях.
Арифметические +, -, *, /, %, ^
Операторы отношений <, >, ==, <=, >=, lt, gt, eq, le, ge
Логические and, or, not, !
Условные ?: (трехместный), ?: (Элвис)
Регулярные выражения matches
--------------------------------- Выполнение математических операций в языке SpEL --------------------------------------
Язык SpEL поддерживает все основные арифметические операторы, имеющиеся в языке Java, плюс оператор «крышки» ( ^ ),
выполняющий возведение в степень.
<property name="adjustedAmount" value="#{counter.total + 42}"/>
<property name="adjustedAmount" value="#{counter.total - 20}"/>
Оператор * – умножение: <property name="circumference" value="#{2 * T(java.lang.Math).PI * circle.radius}"/>
Оператор / – деление: <property name="average" value="#{counter.total / counter.count}"/>
И оператор % – деление по модулю: <property name="remainder" value="#{counter.total % counter.count}"/>
В отличие от Java, язык SpEL также предлагает оператор возведения в степень:
<property name="area" value="#{T(java.lang.Math).PI * circle.radius ^ 2}"/>
------------------------------------------------- Конкатенация ---------------------------------------------------------
Даже при том, что речь идет об арифметических операторах языка SpEL, стоит упомянуть, что оператор + перегружен и
способен выполнять конкатенацию строковых значений.
<property name="fullName" value="#{performer.firstName + ' ' + performer.lastName}"/>
---------------------------------------------- Сравнение значений ------------------------------------------------------
Например, чтобы выяснить равенство двух чисел, можно воспользоваться оператором равенства ( == ):
<property name="equal" value="#{counter.total == 100}"/>
К сожалению, операторы «меньше чем» и «больше чем» могут вызывать проблемы при использовании в конфигурационных XML-
файлах, так как в языке разметки XML они имеют специальное значение. Поэтому при использовании выражений на языке SpEL
в XML-файлах 1 лучше использовать текстовые эквиваленты этих операторов. Например:
<property name="hasCapacity" value="#{counter.total le 100000}"/>
Язык SpEL включает несколько текстовых операторов для сравнения значений в выражениях
Равно == eq
Меньше чем < lt
Меньше или равно <= le
Больше чем > gt
Больше или равно >= ge
----------------------------------------------- Логические выражения ---------------------------------------------------
Язык SpEL включает несколько операторов для манипулирования логическими значениями в выражениях:
and Логическая операция И; чтобы результатом выражения было значение true, оба операнда должны иметь
значение true
or Логическая операция ИЛИ; чтобы результатом выражения было значение true, хотя бы один из операндов
должен иметь значение true
not или ! Логическая операция НЕ; инвертирует значение целевого выражения
<property name="largeCircle" value="#{shape.kind == 'circle' and shape.perimeter gt 10000}"/>
<property name="outOfStock" value="#{!product.available}"/>
Ето эквивалентно использованию оператора not :
<property name="outOfStock" value="#{not product.available}"/>
в языке SpEL отсутствуют символические эквиваленты операторов and и or .
------------------------------------------------- Условные вычисления --------------------------------------------------
тернарный оператор
<property name="instrument" value="#{songSelector.selectSong()=='Jingle Bells'?piano:saxophone}"/>
Трехместный оператор в языке SpEL действует точно так же, как в языке Java.
<property name="song" value="#{kenny.song != null ? kenny.song : 'Greensleeves'}"/>
Это выражение построено правильно, но в нем имеется повторяющийся элемент – ссылка на свойство kenny.song .
Язык SpEL предлагает разновидность трехместного оператора, упрощающую подобные выражения:
<property name="song" value="#{kenny.song ?: 'Greensleeves'}"/>
При таком способе использования оператор ?: называют оператором Элвиса. Этим странным названием оператор обязан сравнению
со смайликами – если повернуть его на 90 градусов, вопросительный знак будет напоминать прическу знаменитого Элвиса Пресли
----------------------------------------------- Регулярные выражения ---------------------------------------------------
Язык SpEL поддерживает сопоставление с шаблонами с помощью оператора matches.
Результатом выполнения оператора matches является логическое значение: true – если строка совпадает с шаблоном
и false – в противном случае.
<property name="validEmail" value="#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.com'}"/>
------------------------------------------ Обработка коллекций на языке SpEL -------------------------------------------
--------------------------------------------- Доступ к элементам коллекции ---------------------------------------------
<property name="chosenCity" value="#{cities[2]}"/>
<property name="chosenCity" value="#{cities[T(java.lang.Math).random() * cities.size()]}"/>
В любом случае для доступа к элементу коллекции по его индексу должны использоваться квадратные скобки ( [] ).
Оператор [] также можно использовать для извлечения элементов из отображений java.util.Map .
<property name="chosenCity" value="#{cities['Dallas']}"/>
Другой пример применения оператора [] – извлечение значения из коллекции типа java.util.Properties .
<property name="accessToken" value="#{settings['twitter.accessToken']}"/>
------------------------------------------ Выборка элементов коллекций -------------------------------------------------
( .?[] )
<property name="bigCities" value="#{cities.?[population gt 100000]}"/>
Оператор выборки создаст новую коллекцию, которая будет включать элементы оригинальной коллекции, соответствующие
критерию выбора, заключенному в квадратные скобки. В данном случае в свойство bigCities будет внедрен список городов
(объектов City ) с населением больше 100 000 человек.
В языке SpEL имеются также другие операторы выборки, .^[] и .$[] , позволяющие получить первый и последний
(соответственно) элементы в выборке из коллекции.
<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]}"/>
========================================================================================================================
========================================================================================================================
========================================================================================================================
Помимо чтения свойств из коллекции <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']}"/>