Здесь мы увидим, как управлять зависимостями между компонентами в контексте среды Spring, используя «противоположное направление» таких зависимостей.
Spring великолепен в обеспечении инверсии управляющего контейнера, создании экземпляров bean-компонентов, их подключении и управлении жизненным циклом. Но можно ли там улучшить?
Да, Spring Framework великолепен. Нет сомнения. Я помню то волнение, когда я играл с ним впервые (это было несколько лет назад). Было замечательно видеть, что после обеспечения правильной конфигурации волшебство произошло — все бины были созданы и, что более важно, они были соединены вместе контейнером …
В тот момент инверсия управления была довольно новой концепцией, и вещи, которыми занимался Spring, слишком сильно впечатлили нас. В то время мы завершили довольно большой продукт, который требовал сложной настройки компонентов и зависимостей между ними, и поэтому мы были полностью разочарованы тем, что Spring появился с опозданием.
Хорошо, весна отличная. Но определенно можно улучшить в некоторых областях. Оставим в покое только священные войны, связанные с Spring, и рассмотрим его базовую функциональность — контейнер IoC. В конце концов, это одна из основных концепций Spring — так что, возможно, мы сможем немного ее отшлифовать?
Давайте представим, что у нас есть два bean-компонента в контексте Spring — BeanA и BeanB. И у BeanA есть свойство, к которому должен быть подключен BeanB. Если мы исключим проблемы с автопроводкой, такая зависимость будет описана в XML-контексте Spring следующим образом:
<bean class="..." id="BeanA">
<property name="propertyThatRefersToB" ref="BeanB"/>
</bean>
<bean id="BeanB" class="..."/>
или это может быть еще короче, если используется пространство имен p: из Spring 2.x:
<bean class="..." id="BeanA"
p:propertyThatRefersToB-ref="BeanB"/>
В общем, этот способ проинструктировать Spring, как вводить BeanB в BeanA, довольно хорош и более чем достаточен в большинстве ситуаций.
К сожалению, не во всех.
Общая концепция среды Spring предполагает, что контекст собирается, когда известны все компоненты (и это довольно естественно). Однако такой подход не работает, если приложение должно быть построено с использованием не сплошной, а скорее архитектуры на основе плагинов.
Недавно в одном из проектов, которые мы разрабатываем здесь в
SoftAMIS, у нас возникла та же проблема — вся система должна поддерживать динамически загружаемые плагины (фактически, все приложение можно рассматривать как набор плагинов) и, что более важно, вообще говоря , этот набор плагинов неизвестен, поэтому новые плагины могут быть добавлены позже, а существующие могут быть отключены.
Это был тот случай, когда Spring контейнер просто может выйти из строя …
К счастью, в нашем приложении плагины собираются только во время запуска приложения, поэтому мы столкнулись с другой проблемой — как составить окончательный контекст Spring из нескольких файлов конфигурации. Поскольку Spring изначально поддерживал разделение всего контекста приложения на несколько файлов конфигурации, это было несложно.
Однако здесь у нас все еще были небольшие проблемы — весной, чтобы сделать ссылку на боб, вам нужно знать имя этого боба. Это было явно неприемлемо для плагинов — и нам была нужна возможность указать эту ссылку не на родительский бин (BeanA), а на бин, на который ссылается — (BeanB).
Таким образом, вместо направления ссылок сверху вниз нам понадобился противоположный, который предполагает, что непосредственно в бине мы можем указать, куда нужно вводить. Следующая картина иллюстрирует такую разницу между этими подходами:
Мы пытались найти готовое решение для этого, но не нашли ничего, что могло бы удовлетворить наши потребности (учтите, что на самом деле существуют более сложные типы ссылок между компонентами — например, через список, карту и набор).
Итак, необходимость — это мать прогресса — и мы создали небольшую библиотеку, которая позволяет делать такие противоположные инъекции весной.
Благодаря поддержке пользовательских пространств имен в Spring 2.x, полученная разметка оказалась довольно простой. Как это:
<bean class="..." id="BeanA"/>
<bean class="..." id="BeanB">
<inject:to-ref target="BeanA" name="propertyThatRefersToB"/>
</bean>
Довольно просто, правда? Обратите внимание на тег из пользовательского пространства имен (поддерживается Spring 2.x):
<inject:to-ref target="BeanA" name="propertyThatRefersToB"/>
Используя его сейчас, мы просто перевернули направление, в котором мы объявляем ссылки между bean-компонентами в контексте Spring — BeanA просто можно рассматривать как точку расширения, к которой может быть подключен базовый BeanB. И подумайте о том, чтобы иметь такую возможность динамически подключать бин в список или на карту (да, они у нас уже есть) ….
Ну, это было описание общей идеи. На днях я опубликую более подробную информацию об этой технологии, а также предоставлю полный исходный код для нее — так что следите за обновлениями!
ОБНОВИТЬ:
Я добавил еще одну запись в свой блог, которая более подробно описывает эту проблему. И оттуда доступна библиотека Inject4Spring — она обеспечивает поддержку концепций, которые я там описал.
Пожалуйста, используйте следующую ссылку, чтобы получить его: