User's collector

Внимание!   Данная опция будет доступна только после того, как вы авторизуетесь.
   запомнить меня 
13 декабря 2009

Проблема с циклическими зависимостями между сервисами в Grails

Представим себе типичную ситуацию. Пусть в составе нашего приложении имеются два сервиса:

GROOVY:
  1. class PersonService
  2. {
  3.     def postService
  4.     void foo()
  5.     {
  6.         // …
  7.     }
  8. }

и

GROOVY:
  1. class PostService
  2. {
  3.     def personService
  4.     void bar()
  5.     {
  6.         personService.foo()
  7.     }
  8. }

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

CODE:
  1. Error creating bean with name '(inner bean)': Initialization of bean failed;
  2. nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
  3. Error creating bean with name 'postService':
  4. org.springframework.beans.factory.FactoryBeanNotInitializedException:
  5. FactoryBean is not fully initialized yet

Ошибка связана с тем, что Spring не может проинициализировать свойство, в которое требуется поместить ссылку на экземпляр объекта, нуждающийся в обратной ссылке на текущий объект. Классическая ситуация «курицы и яйца», по-другому не назовешь. :)

Как же решить проблему? Снимем обязанность по инъекции в одном из классов со Spring и прибегнем к ленивой инициализации свойства. А еще точнее в одном из сервисов сам процесс инъекции поместим в соответствующий геттер. Посмотрите на следующий листинг:

GROOVY:
  1. import org.springframework.context.ApplicationContext
  2. import org.springframework.context.ApplicationContextAware
  3.  
  4. class PostService implements ApplicationContextAware
  5. {
  6.     ApplicationContext applicationContext
  7.  
  8.     PersonService getPersonServiceBean()
  9.     {
  10.         applicationContext.getBean("personService") as PersonService
  11.     }
  12.    
  13.     void bar()
  14.     {
  15.         personServiceBean.foo()
  16.     }
  17. }

Итак, что же мы сделали:

  1. В сервисе PostService мы имплементировали интерфейс ApplicationContextAware.
  2. Добавили в класс инъекцию ApplicationContext.
  3. Вместо инъекции personService мы создали метод-геттер getPersonServiceBean, в котором посредством метода applicationContext.getBean() возвращаем ссылку на экземпляр нужного нам сервиса.
  4. В методе bar к экземпляру сервиса обращаемся посредством свойства personServiceBean, которое, по сути дела, является аналогом вызова геттера getPersonServiceBean(). Обратите внимание, что названием геттера является getPersonServiceBean, а не getPersonService. Тем самым при помощи переименовывания мы пресекаем любые попытки инъекций этого свойства со стороны Spring.

Код сервиса PersonService мы оставляем без изменений — с односторонними зависимостями Spring справляется очень даже хорошо. :) Тестируем приложение и проверяем, что теперь оно запускается без ошибок.

Вот и все на сегодня. Удачного вам дня!

Теги:


Оставьте свой комментарий:

Имя: *
* — обязательно для заполнения
Электропочта: *
Сайт:
Сообщение *
Коментировать
Коментировать