Suppose I have interface foo and bar, then have multiple classes that implements both of them:
public interface InterfaceFoo{
int getFoo()
}
public interface InterfaceBar{
int getBar()
}
public class FooBarOne implements InterfaceFoo, InterfaceBar{
public int getFoo() { ... }
public int getBar() { ... }
}
public class FooBarTwo implements InterfaceFoo, InterfaceBar{
public int getFoo() { ... }
public int getBar() { ... }
}
Then I create beans for both of these classes:
@Configuration
@ComponentScan
public class Config {
@Bean
fooBarOne getFooBarOne() { return new FooBarOne(); }
@Bean
fooBarTwo getFooBarTwo() { return new FooBarTwo();}
}
Finally another bean which @Autowries in a list of all implementations of foo
@Configuration
@ComponentScan
public class FooConfig {
@Bean
@Autowired
public List<InterfaceFoo> fooFetcher(List<<InterfaceFoo>> listFoos) {
return listFoos;
}
}
My question is, how does Spring identify/autowire beans when they have multiple implementations of an interface? The above pseudo code seems to work, however, if I change the return type of the bean from the concrete class to an ibar, it is not picked up by the autowire:
@Configuration
@ComponentScan
public class Config {
@Bean
InterfaceBar getFooBarOne() { return new FooBarOne(); }
@Bean
InterfaceBar getFooBarTwo() { return new FooBarOne(); }
}
From the example above, spring does not pick these beans up when autowiring for implementations of ifoo. Switching the return type to ifoo works, which implies to me that spring looks at the return type of the bean rather than that of the returned object? Even though fooBarOne and fooBarTwo both implement foo and bar, do I need to return either the concrete class or interface ifoo if I want the autowire for List to pick it up?
CodePudding user response:
Spring's default autowire mode is by type, so it makes sense that if you create two beans of type iBar
, they will not be autowired as iFoo
, even though the concrete classes are also instances of iFoo
. Spring does not know about all of the interfaces that a class implements, it only knows of the type of beans that were created in its context.
When you attempt to autowire List<<iFoo>>
, Spring will look in its context for all the beans of type iFoo
to inject, which you have none.
Also, please read up on the Java naming conventions, you probably want your interfaces to just be Foo
and Bar
https://www.oracle.com/java/technologies/javase/codeconventions-namingconventions.html
CodePudding user response:
When declaring beans with @Bean-annotated methods, Spring assigns return type of the method as bean type, i.e. saves class name (ibar.class.getName()
) to BeanDefinition, along with other object metadata. From now, spring does not care whether your class implements other interfaces, it just has your object and assigned type.
When autowiring, spring filters beans that match bean type and name constrains, provided by type of bean to be constructed
In this case, when trying to create List<iFoo>
, spring can hook beans with type iFoo
or types extending/implementing it, but not ibar
, because ibar
is not related to iFoo
If you need just List<iFoo>
, and you are not using fooBarOne
/fooBarTwo
beans, practically it does not matter if you declare bean as iFoo
or fooBarOne
. But in general, you should prefer using interfaces over implementations, it will help to keep Dependency Inversion in your code.
And, always name your classes with capital letter.