Представим себе типичную ситуацию. Пусть в составе нашего приложении имеются два сервиса:
-
class PersonService
-
{
-
def postService
-
void foo()
-
{
-
// …
-
}
-
}
и
-
class PostService
-
{
-
def personService
-
void bar()
-
{
-
personService.foo()
-
}
-
}
Как мы видим, оба этих сервиса нуждаются во взаимной инъекции экземпляра другого сервиса и сохранении его в виде внутреннего свойства. С виду все правильно, однако, если мы попытаемся запустить наше приложение, то получим ошибку следующего характера:
-
Error creating bean with name '(inner bean)': Initialization of bean failed;
-
nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException:
-
Error creating bean with name 'postService':
-
org.springframework.beans.factory.FactoryBeanNotInitializedException:
-
FactoryBean is not fully initialized yet
Ошибка связана с тем, что Spring не может проинициализировать свойство, в которое требуется поместить ссылку на экземпляр объекта, нуждающийся в обратной ссылке на текущий объект. Классическая ситуация «курицы и яйца», по-другому не назовешь.
Как же решить проблему? Снимем обязанность по инъекции в одном из классов со Spring и прибегнем к ленивой инициализации свойства. А еще точнее в одном из сервисов сам процесс инъекции поместим в соответствующий геттер. Посмотрите на следующий листинг:
-
import org.springframework.context.ApplicationContext
-
import org.springframework.context.ApplicationContextAware
-
-
class PostService implements ApplicationContextAware
-
{
-
ApplicationContext applicationContext
-
-
PersonService getPersonServiceBean()
-
{
-
applicationContext.getBean("personService") as PersonService
-
}
-
-
void bar()
-
{
-
personServiceBean.foo()
-
}
-
}
Итак, что же мы сделали:
- В сервисе
PostServiceмы имплементировали интерфейсApplicationContextAware. - Добавили в класс инъекцию
ApplicationContext. - Вместо инъекции
personServiceмы создали метод-геттерgetPersonServiceBean, в котором посредством методаapplicationContext.getBean()возвращаем ссылку на экземпляр нужного нам сервиса. - В методе
barк экземпляру сервиса обращаемся посредством свойстваpersonServiceBean, которое, по сути дела, является аналогом вызова геттераgetPersonServiceBean(). Обратите внимание, что названием геттера являетсяgetPersonServiceBean, а неgetPersonService. Тем самым при помощи переименовывания мы пресекаем любые попытки инъекций этого свойства со стороны Spring.
Код сервиса PersonService мы оставляем без изменений — с односторонними зависимостями Spring справляется очень даже хорошо.
Тестируем приложение и проверяем, что теперь оно запускается без ошибок.
Вот и все на сегодня. Удачного вам дня!


