Весна. Решить круговую зависимость с java-конфигурацией и без @Autowired
У меня есть круговая зависимость и java-конфиг. Хотя решение с помощью xml-конфигурации очень просто, я не могу разрешить его с помощью java-конфигурации без @Autowired. Фасоль:
public class A {
private B b;
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
public class B {
private A a;
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
Я пробовал это (я прочитал, что с аннотацией @Bean Spring не будет вызывать метод каждый раз, когда bean ссылается, но в этом случае он фактически вызывается все время):
@Configuration
public class Config {
@Bean
public A a() {
A a = new A();
a.setB(b());
return a;
}
@Bean
public B b() {
B b = new B();
b.setA(a());
return b;
}
}
И это, с @Autowired из полей класса Configuration:
@Configuration
public class Config {
@Autowired
A a;
@Autowired
B b;
@Bean
public A a() {
A a = new A();
a.setB(b);
return a;
}
@Bean
public B b() {
B b = new B();
b.setA(a);
return b;
}
}
Также я пробовал все выше с аннотацией @Lazy. Не помогает. Но работает отлично, если я комментирую сеттеры A и B с @Autowired. Но это не то, что я хочу сейчас. Что я делаю неправильно и есть ли способ разрешить зависимость Circular в java-конфиге без использования @Autowired?
Ответы
Ответ 1
Поведение, которое вы хотите получить, следующее
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
Методы @Bean
не дают вам этого. Они заканчиваются, чтобы предоставить экземпляр компонента.
Вам в основном нужно частично создать один из экземпляров, а затем завершить его инициализацию, когда вы создали другую.
@Configuration
class Config {
@Bean
public A a() {
A a = new A();
return a;
}
@Bean
public B b() {
B b = new B();
A a = a();
b.setA(a);
a.setB(b);
return b;
}
}
или
@Bean
public B b(A a) {
B b = new B();
b.setA(a);
a.setB(b);
return b;
}
Ответ 2
Другой подход, использующий @Autowired
и @Component
- использовать этот шаблон:
@Component
class A {
private B b;
public B getB() {
return b;
}
public void setB(final B b) {
this.b = b;
}
}
@Component
class B {
private final A a;
@Autowired
public B(final A a) {
this.a = a;
a.setB(this);
}
public A getA() {
return a;
}
}
Это устраняет необходимость в отдельной @Configuration
-class. Кроме того, setB
-method может быть защищен от пакетов, если классы существуют в одном пакете, чтобы максимально минимизировать область охвата.
Ответ 3
Я хочу добавить еще одно возможное решение для вашего кода. Вместо установки круговых зависимостей прямо в config:
@Configuration
public class Config {
@Bean
public A a() {
A a = new A();
a.setB(b());
return a;
}
@Bean
public B b() {
B b = new B();
b.setA(a());
return b;
}
}
Вы также можете позволить весне выполнять работу с помощью @Autowired аннотации.
@Configuration
public class Config {
@Bean
public A a() {
A a = new A();
return a;
}
@Bean
public B b() {
B b = new B();
return b;
}
}
public class A {
private B b;
@Autowired
public setB(B b) { this.b = b; }
}
public class B {
private A a;
@Autowired
public setA(A a) { this.a = a; }
}
Конечно, это нетривиально из "чистой/удобочитаемой/понятной" точки зрения, потому что теперь ваша конфигурация смешивается в @Configuration и самом классе. Но поскольку круговые зависимости встречаются довольно редко, мы можем позволить себе взломать.