Spring Javaconfig межбобовых зависимостей
Просматривая справочную документацию по Spring Javaconfig http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html я нашел некоторые запутанные части...
В разделе "5.12.4 Использование аннотации @Configuration" говорится:
"Когда @Beans имеют зависимости друг от друга, выразить эту зависимость так же просто, как заставить один метод bean вызывать другой:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
В приведенном выше примере компонент foo получает ссылку на bar с помощью инжектора конструктора."
Хорошо, если все не имеет состояния, это может не иметь большого значения, но если у вас есть конфигурация выше, а затем в вашем приложении выполните:
@Autowired
private Foo foo;
@Autowired
private Bar bar;
проверяя hashCodes bean-компонентов, получается, что ваша приватная переменная bar будет ссылаться на экземпляр Bar, отличный от того, который используется foo, что, вероятно, не то, что вы ожидаете, верно?
Я бы сказал, что нормальный шаблон должен быть:
@Configuration
public class AppConfig {
@Bean
public Bar bar() {
return new Bar();
}
@Autowired Bar bar;
@Bean
public Foo foo() {
return new Foo(bar);
}
}
Теперь, когда вы автоматически подключите оба компонента в своем приложении, вы создадите только один экземпляр Bar.
Я что-то упустил, или я правильно понял, что документация здесь нестабильная?
Затем, далее, в разделе "Дополнительная информация о том, как внутренняя конфигурация на основе Java" работает, похоже, они пытаются "прояснить" эту проблему:
@Configuration
public class AppConfig {
@Bean
public ClientService clientService1() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientService clientService2() {
ClientServiceImpl clientService = new ClientServiceImpl();
clientService.setClientDao(clientDao());
return clientService;
}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
}
Теперь, к сожалению, эта конфигурация даже не будет загружаться во время выполнения, потому что есть 2 компонента одного типа, ClientService, без отличительных свойств, поэтому получите исключение
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [.....] is defined:
expected single matching bean but found 2
Но даже если мы немного изменим пример и дадим первые 2 бина разных типов,
@Bean
public ClientService1 clientService1() {...clientDao()...}
@Bean
public ClientService2 clientService2() {...clientDao()...}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
это по-прежнему неверно, поскольку - вопреки тому, что говорится в тексте - мы все равно создали бы 3 разных экземпляра ClientDaoImpl при автоматической разводке всех 3 бинов.
Опять же, я что-то упускаю или документация действительно так плоха, как мне кажется?
РЕДАКТИРОВАТЬ: Добавлено демо, которое продемонстрирует проблему, которую я вижу:
Бин ServiceA и два бина ServiceB1, ServiceB2, которые конструктор внедряет ServiceA.
Затем два тестовых класса Config01Test и Config02Test, которые идентичны, за исключением конфигураций. Первый тест PASSES, второй FAILS из-за уникальности утверждает.
2 ответа
Проверьте, что у вас есть @Configuration
аннотации к вашему классу конфигурации. По крайней мере, в вашей демоверсии они отсутствуют Config01
а также Config02
классы. Если я добавлю их в эти классы, тесты пройдут.
Я сам не тестировал пример. Это должно убедиться, что у вас есть только один экземпляр Bar в полном контексте приложения.
@Configuration
public class AppConfig {
@Bean
public Foo foo(Bar bar) {
return new Foo(bar);
}
@Bean
public Bar bar() {
return new Bar();
}
}