Browsing the Spring Javaconfig reference documentation http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/beans.html I found some confusing parts...
Under section "5.12.4 Using the @Configuration annotation" it says:
"When @Beans have dependencies on one another, expressing that dependency is as simple as having one bean method call another:
@Configuration
public class AppConfig {
@Bean
public Foo foo() {
return new Foo(bar());
}
@Bean
public Bar bar() {
return new Bar();
}
}
In the example above, the foo bean receives a reference to bar via constructor injection."
Well, if everything is stateless, it might not matter much, but if you have the config above, and then in your application do:
@Autowired
private Foo foo;
@Autowired
private Bar bar;
checking the hashCodes of the beans, it turns out, that your private variable bar will refer to a different instance of Bar than the one used by foo, which is probably not what you expect, right?
I would say the normal pattern should rather be:
@Configuration
public class AppConfig {
@Bean
public Bar bar() {
return new Bar();
}
@Autowired Bar bar;
@Bean
public Foo foo() {
return new Foo(bar);
}
}
Now, when you autowire both beans in your app, you will only create a single instance of Bar.
Am I missing something, or am I correct that the documentation is flaky here?
Then, further down, under section "Further information about how Java-based configuration works internally" it looks like they try to "clarify" this issue:
@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();
}
}
Now, unfortunately, this configuration, won't even load at runtime, because there are 2 beans of the same type, ClientService, with no distinguishing properties, so get exception
org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [.....] is defined:
expected single matching bean but found 2
But even if we change the example slightly, and give the first 2 beans different types,
@Bean
public ClientService1 clientService1() {...clientDao()...}
@Bean
public ClientService2 clientService2() {...clientDao()...}
@Bean
public ClientDao clientDao() {
return new ClientDaoImpl();
}
this is still incorrect, since -- contrary to what the text claims -- we would still create 3 different instances of ClientDaoImpl, when autowiring all 3 beans.
Again, am I totally missing something, or is the documentation really as bad as it seems to me?
EDIT: Added a demo that will demonstrate the issue I see:
https://github.com/rop49/demo
A bean ServiceA, and two beans ServiceB1, ServiceB2 that constructor-injects ServiceA.
Then two test-classes Config01Test and Config02Test that are identical except for the configurations. The first test PASSES, the second FAILS because of uniqueness-asserts.
Check that you have @Configuration
annotations on your configuration class. With your demo at least they are missing from the Config01
and Config02
classes. If I add them to those classes the tests pass.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With