2015-03-25 1 views
2

Я ожидал, что Spring примет во внимание @DependsOn при вызове методов @PostConstruct, но похоже, что это не так в случае использования круговых (авто-проводных) зависимостей.Весна: круговые зависимости, @PostConstruct и порядок, наложенные @DependsOn

Рассмотрите две бобы (код ниже), BeanB @DependsOn BeanA. Когда поле BeanA#b имеет это @Autowired закомментирована, методы пост-конструкт называются в ожидаемом порядке: сначала А, затем B. Но с @Autowired в силу для А, у меня Б post называется первым, затем элементов а post.

Я понимаю, что это плохой дизайн (на самом деле, это минимальная демо очень большой @Autowired ... код базы), но я ожидал, что весна, чтобы закончить инъекцию @Autowired полей и затем начинают называть жизненным циклом обратных вызовов, почитая @DependsOn, но весна, кажется, игнорирует @DependsOn заказ, когда есть круговые отпечатки.

Весенняя версия 4.1.5.

Итак, это моего недоразумения или недокументированного поведение или она может рассматриваться родник ошибкой (или, возможно, запрос функции)?

@Component 
class BeanA { 

    // @Autowired 
    private BeanB b; 

    void f() { 
     System.out.println(this); 
    } 

    @PostConstruct 
    void post() { 
     System.out.println("A done"); 
    } 

    @Override 
    public String toString() { 
     return "Bean{" + 
       "b=" + (b == null ? null : b.getClass()) + 
       '}'; 
    } 
} 
// --------------------- 
@Component 
@DependsOn("beanA") 
class BeanB { 

    @Autowired 
    private BeanA a; 

    void f() { 
     System.out.println(this); 
    } 

    @PostConstruct 
    void post() { 
     System.out.println("B done"); 
    } 

    @Override 
    public String toString() { 
     return "BeanB{" + 
       "a=" + (a == null ? null : a.getClass()) + 
       '}'; 
    } 
} 

ответ

3

В главе о Initialization callbacks документации Spring утверждает

[@PostConstruct и другие методы] позволяет бобу выполнять инициализации работы после всех необходимых свойств на бобе были были установлены контейнер.

С вашего прокомментированного кода создается следующее: beanA. Контейнер видит, что все необходимые свойства были установлены, и он вызывает метод init (@PostConstruct). Затем он переходит к beanB, который он инициализирует, сохраняет, видит @Autowired, извлекает сохраненный beanA, вводит его, прогоняет beanB , так как все его свойства были установлены.

В вашем раскомментированном коде у вас есть случай круговых зависимостей. beanA моментально создается и сохраняется. Контейнер отмечает, что он имеет цель для инъекций типа BeanB. Для выполнения этой инъекции ему нужен фасоль beanB. Поэтому он создает экземпляр компонента, сохраняет его, видит, что он имеет зависимость от beanA в качестве цели инъекции. Он извлекает beanA (который был сохранен ранее), вводит его, тогда все свойства beanB и его метод @PostConstruct вызывается. Наконец, этот инициализированный компонент beanB вводится в beanA, метод @PostConstruct затем вызывается, поскольку все его свойства установлены.

Эта вторая конструкция имеет корпус beanB, в то время как beanA строится. Это, как Спринг решает следующие

class A { 
    private B b; 
} 

class B { 
    private A a; 
} 

Экземпляр каждый должен быть создан, прежде чем либо может быть введен в другой.


Если вы избавитесь от @DependsOn, вы получите такое же поведение (но только из-за упорядочения по умолчанию сканирования, пути к классам, который, кажется, в алфавитном порядке). Если вы переименовали BeanA в BeanZ, например, сначала будет создан экземпляр beanB, затем beanZ получит экземпляр, инициализируется и возвращается для ввода в beanB.

@DependsOn действительно необходимо, только если у вас есть побочные эффекты, которые вы хотели бы выполнить до того, как будет запущен компонент.

+0

Точно - для B существует зависимость побочных эффектов (в полной версии, а не в моем примере кода): на обратном вызове жизненного цикла A необходимо завершить до того, как вызывается собственный '@ PostConstruct' B. И я собирался обеспечить это с помощью '@ DependsOn', но ... увы! –

+0

Реальная проблема с кодом устраняется путем вызова нужной логики из внешнего кода, как только создается контекст контекста, вместо того, чтобы полагаться на 'B # postConstruct()'. –

+0

Я добавил акцент на свое сообщение, чтобы сделать реальный вопрос более явным. –

Смежные вопросы