Главная » Spring » Автоматическое связывание свойств компонентов Spring

0

Если я скажу: «Сегодня луна особенно яркая», – едва ли у кого- то возникнет вопрос: «Какая луна?», – потому что мы с вами жи- тели Земли, и в данном контексте всем очевидно, что я говорю о Луне – единственном спутнике Земли. Если бы мы с вами были жителями Юпитера, вы наверняка захотели бы уточнить, о каком из 63 естественных спутников идет речь. Но на Земле моя фраза звучит более чем однозначно1. Аналогично, когда дело доходит до автоматического связывания свойств компонентов, очевидно, ссыл- ка на какой компонент должна быть внедрена в данное свойство. Если в контексте приложения имеется только один компонент типа javax.sql.DataSource, тогда любой компонент, имеющий свойство ти- па DataSource, будет зависеть именно от этого компонента DataSource. В конце концов, это единственный компонент такого типа.

Для оформления таких очевидных зависимостей фреймворк Spring предлагает механизм автоматического связывания. Почему бы не позволить фреймворку самому разобраться в подобных ситуа- циях, когда однозначно можно определить, ссылка на какой компо- нент должна быть внедрена в свойство, вместо явного связывания свойств?

Четыре типа автоматического связывания

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

1 Конечно, если бы мы оказались на Юпитере, яркость его лун едва ли мог- ла заботить нас из-за высокой плотности атмосферы и ее непригодности для дыхания.

# byName – пытается отыскать компонент в контейнере, чье имя (или идентификатор) совпадает с именем связываемого свой- ства. Если соответствующий компонент не найден, свойство останется несвязанным.

# byType  – пытается отыскать единственный компонент в кон-

тейнере, чей тип соответствует типу связываемого свойства. Если соответствующий компонент не найден, свойство не бу- дет связано.

# constructor – пытается сопоставить конструктор компонента,

куда выполняется внедрение, с компонентами, чьи типы со- впадают с аргументами конструктора.

# autodetect – сначала пытается выполнить автоматическое свя-

зывание через конструктор, а затем по типу.

У каждой из этих разновидностей есть свои достоинства и недо- статки. Посмотрим сначала, как организовать автоматическое свя- зывание, руководствуясь именами свойств компонентов.

Автоматическое связывание по имени

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

Для примера вернемся к компоненту kenny, представленному в главе 2:

<bean  id="kenny2"

class="com.springinaction.springidol.Instrumentalist">

<property name="song" value="Jingle Bells" />

<property  name="instrument"  ref="saxophone"  />

</bean>

Здесь с помощью элемента <property> явно настраивается свойство instrument. Представим на мгновение, что Saxophone объявлен как эле- мент <bean> с атрибутом id="instrument":

<bean id="instrument" class="com.springinaction.springidol.Saxophone"  />

При наличии такого  определения  идентификатор  компонен- та Saxophone  совпал бы с именем свойства instrument  и фреймворк

Spring смог бы использовать это обстоятельство для автоматическо- го связывания инструмента, если бы компонент kenny имел атрибут autowire, как показано ниже:

<bean  id="kenny"

class="com.springinaction.springidol.Instrumentalist" autowire="byName">

<property name="song" value="Jingle Bells" />

</bean>

При автоматическом связывании по имени (byName) устанавлива- ется соглашение, в соответствии с которым свойство будет автома- тически связано с компонентом, имеющим такое же имя. Атрибут autowire со значением byName сообщает фреймворку, что необходимо просмотреть все свойства компонента kenny и отыскать компоненты с соответствующими им именами. В данном случае свойство instrument подходит для автоматического связывания через метод записи. Как изображено на рис. 4.1, если в контексте имеется компонент с иден- тификатором instrument, он будет автоматически внедрен в свойство instrument.

Рис. 4.1. При автоматическом связывании по имени имя компонента соответствует свойствам с теми же именами

Недостаток автоматического связывания по имени – в том, что предполагается наличие компонента, чей идентификатор совпадает с именем свойства другого компонента. В нашем примере это потре- бовало бы создать компонент с именем instrument. Если в конфигу- рации будет определено несколько компонентов типа Instrumentalist с атрибутом autowire, тогда все они будут играть на одном и том же инструменте. В некоторых ситуациях, возможно, это не проблема, но об этом ограничении следует помнить.

Автоматическое связывание по типу

Автоматическое связывание по типу (byType) действует подобно автоматическому связыванию по имени (byName), только вместо име- ни свойства учитывается его тип. При автоматическом связывании

по типу фреймворк Spring будет искать компоненты, чей тип со- впадает с типом свойства.

Например, предположим, что атрибут autowire  компонента kenny

имеет значение byType. Контейнер попытается отыскать внутри се- бя компонент, имеющий тип Instrument, и внедрит его в свойство instrument. Как показано на рис. 4.2, компонент saxophone будет авто- матически внедрен в свойство instrument компонента kenny, потому что и свойство instrument, и компонент saxophone имеют тип Instrument.

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

«Spring Idol» наверняка будет несколько компонентов, типы кото- рых являются подклассами класса Instrument.

Чтобы устранить неоднозначность при использовании автомати- ческого связывания по типу, Spring предлагает два решения: либо определить основного кандидата для автоматического связывания, либо исключить компоненты из списка кандидатов на автоматиче- ское связывание.

Чтобы определить основного кандидата для автоматического свя- зывания, в элемент <bean> определения компонента необходимо до- бавить атрибут primary. Если механизм автоматического связывания обнаружит только один подходящий компонент с атрибутом primary, установленным в значение true, он отдаст предпочтение этому ком- поненту.

Но самое неприятное, что атрибут primary получает значение true по умолчанию. Это означает, что все кандидаты для автоматического связывания по умолчанию являются основными (и потому ни одно- му из них не может быть отдано предпочтение). То есть при исполь- зовании решения на основе атрибута primary необходимо присвоить ему значение false во всех остальных компонентах. Например, чтобы указать, что компонент saxophone не является основным кандидатом для автоматического связывания со свойством типа Instruments:

<bean  id="saxophone"

class="com.springinaction.springidol.Saxophone" primary="false"  />

Атрибут primary удобно использовать только для определения предпочтительного кандидата. При использовании решения, осно- ванного на исключении некоторых компонентов из рассмотрения механизмом автоматического связывания, следует установить их атрибут autowire-candidate  в значение false, как показано ниже:

<bean  id="saxophone"

class="com.springinaction.springidol.Saxophone" autowire-candidate="false"   />

Автоматическое связывание через конструктор

Если компонент настроен на внедрение зависимостей через кон- структор, можно просто убрать элементы <constructor-arg> из его объявления и позволить фреймворку автоматически выбрать ар- гументы для передачи конструктору из компонентов, имеющихся в контексте Spring.

Например, взгляните на следующее объявление компонента duke:

<bean  id="duke"

class="com.springinaction.springidol.PoeticJuggler" autowire="constructor"  />

Рис. 4.2. Автоматическое связывание

по совпадению типа компонента с типом свойства

Рис. 4.3. При автоматическом связывании через конструктор новый компонент duke типа PoeticJuggler будет создан вызовом конструктора с аргументом Poem

Из этого нового объявления компонента duke исчезли элемен- ты <constructor-arg>, а атрибуту autowire было присвоено значение constructor. Это объявление сообщает фреймворку, что он должен исследовать конструктор класса PoeticJuggler и попытаться найти в конфигурации компоненты, соответствующие аргументам одного из конструкторов. В конфигурации уже присутствует определение компонента sonnet29, имеющего тип Poem и соответствующего аргу- менту одного из конструкторов класса PoeticJuggler. Поэтому для создания компонента duke Spring будет использовать этот конструк- тор и передаст ему компонент sonnet29, как изображено на рис. 4.3.

На автоматическое связывание через конструктор распространя- ются те же ограничения, что и на автоматическое связывание по типу (byType). В случае обнаружения нескольких компонентов, со- ответствующих аргументам конструктора, фреймворк Spring не бу- дет пытаться угадать, какой из них использовать. Кроме того, если класс имеет несколько конструкторов, каждый из которых отвечает требованиям автоматического связывания, Spring не будет пытаться угадать, какой конструктор использовать.

Автоматическое связывание с автоматическим определением

Если вы желаете организовать автоматическое связывание ком- понентов, но не можете выбрать наиболее подходящий способ, не волнуйтесь – установив атрибут autowire в значение autodetect, вы можете позволить контейнеру сделать выбор за вас. Например:

<bean  id="duke"

class="com.springinaction.springidol.PoeticJuggler" autowire="autodetect"  />

Когда компонент настроен на автоматическое связывание по- средством автоматического определения, фреймворк будет пытаться сначала выполнить автоматическое связывание через конструктор. Если подходящее совпадение не будет найдено, тогда будет пред- принята попытка выполнить автоматическое связывание по типу.

Автоматическое связывание по умолчанию

Если вы вдруг обнаружите, что вам придется добавить один и тот же атрибут autowire во все компоненты (или в большинство из них), можно просто попросить фреймворк автоматически применить одни и те же настройки автоматического связывания ко всем компонен-

там. Для этого в корневой элемент <beans> следует добавить атрибут

default-autowire:

<?xml  version="1.0"  encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd" default-autowire="byType">

</beans>

По умолчанию атрибут default-autowire имеет значение none, ука- зывая, что настройка автоматического связывания для каждого ком- понента выполняется индивидуально, посредством атрибута autowire. В примере выше ему было присвоено значение byType, чтобы указать, что свойства всех компонентов должны автоматически связываться по типу. Но атрибуту default-autowire можно присвоить любой дру- гой тип автоматического связывания, который должен применяться ко всем компонентам, описанным в конфигурационном файле.

Обратите внимание: я сказал, что настройка атрибута default- autowire будет применяться ко всем компонентам, описанным в кон- фигурационном файле Spring, но я не сказал, что она будет при- меняться ко всем компонентам в контексте приложения. Можно создать несколько файлов конфигурации, определяющих параметры одного и того же контекста приложения, и в каждом из них исполь- зовать собственные настройки автоматического связывания.

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

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

По теме:

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