Межкомпонентные зависимости Spring Javaconfig

Просмотрите справочную документацию Spring Javaconfig http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html Я нашел несколько запутанных частей...

В разделе "5.12.4 Использование аннотации @Configuration" говорится:

"Когда @Beans зависят друг от друга, выразить эту зависимость так же просто, как заставить один метод компонента вызывать другой:

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        return new Foo(bar());
    }
    @Bean
    public Bar bar() {
        return new Bar();
    }
}

В приведенном выше примере bean-компонент foo получает ссылку на bar посредством внедрения конструктора."

Ну, если все без состояния, то может это и не имеет большого значения, но если у вас есть конфиг выше, а затем в вашем приложении сделайте:

@Autowired 
private Foo foo;

@Autowired 
private Bar bar;

проверяя хэш-коды 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 bean-компонента одного типа, ClientService, без отличительных свойств, поэтому получите исключение

org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type [.....] is defined: 
expected single matching bean but found 2

Но даже если мы немного изменим пример и дадим первым двум bean-компонентам разные типы,

@Bean
public ClientService1 clientService1() {...clientDao()...}
@Bean
public ClientService2 clientService2() {...clientDao()...}
@Bean
public ClientDao clientDao() {
    return new ClientDaoImpl();
}

это по-прежнему неверно, поскольку - вопреки тому, что утверждается в тексте - мы все равно создадим 3 разных экземпляра ClientDaoImpl при автоподключении всех 3 bean-компонентов.

Опять же, я что-то упустил, или документация действительно так плоха, как мне кажется?

EDIT: добавлена ​​демонстрация, демонстрирующая проблему, которую я вижу:

https://github.com/rop49/demo

Компонент ServiceA и два компонента ServiceB1, ServiceB2, которые внедряют конструктор ServiceA.

Затем два тестовых класса Config01Test и Config02Test, которые идентичны, за исключением конфигураций. Первый тест ПРОЙДЕТ, второй НЕ ПРОЙДЕТ из-за проверки уникальности.


person Rop    schedule 19.09.2014    source источник
comment
Как вы думаете, почему вы получите разные Bar объекты?   -  person chrylis -cautiouslyoptimistic-    schedule 20.09.2014
comment
Я сделал небольшой тест и проверил хэш-коды в bean-компонентах. Они разные. Я пояснил это в вопросе выше, спасибо.   -  person Rop    schedule 20.09.2014
comment
В любом случае вы могли бы опубликовать простой пример работы на GitHub, возможно, с Spring Boot? Я никогда не видел такого поведения, и это ошибка, если это действительно происходит.   -  person chrylis -cautiouslyoptimistic-    schedule 20.09.2014
comment
Кроме того, не могли бы вы попробовать установить поле экземпляра из статического счетчика? Это может быть просто проблема с проксированием Spring.   -  person chrylis -cautiouslyoptimistic-    schedule 20.09.2014
comment
Я добавил демо-тест в конце моего вопроса выше.   -  person Rop    schedule 20.09.2014
comment
Я только что сделал еще одну фиксацию, чтобы использовать уникальные UUID вместо hashCode(). Все та же проблема.   -  person Rop    schedule 20.09.2014


Ответы (2)


Убедитесь, что в вашем классе конфигурации есть аннотации @Configuration. По крайней мере, в вашей демонстрации они отсутствуют в классах Config01 и Config02. Если я добавлю их в эти классы, тесты пройдут.

person Phil Webb    schedule 20.09.2014
comment
Ааа, отлично, я разобрался :) Спасибо, Фил! - person Rop; 20.09.2014
comment
В качестве примечания... имеет ли смысл, чтобы Spring отклонял \@SpringApplicationConfiguration классов, у которых нет аннотации \@Configuration? Или это когда-нибудь будет допустимым вариантом использования, чтобы разрешить это? - person Rop; 20.09.2014
comment
К сожалению, это нарушит обратную совместимость. См. раздел lite-beans здесь: docs.spring.io/spring-framework/docs/current/ - person Phil Webb; 21.09.2014

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

@Configuration
public class AppConfig {
    @Bean
    public Foo foo(Bar bar) {
        return new Foo(bar);
    }
    @Bean
    public Bar bar() {
        return new Bar();
    }
}
person Ashok Koyi    schedule 27.07.2015