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?
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
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.
Related
I have a parameterized class that implements the FactoryBean interface:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
What I want is to use this factory for spawning objects of different classes (all of these classes extend Dog). So I imagined that I could do something like the following:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
public ShepherdService(
#Autowired
DogFactory<Shepherd> shepherdFactory
) {
this.shepherdFactory = shepherdFactory;
}
// ...
}
Alas, I get the following error: Couldn't autowire. No beans of DogService<Shepherd> type found. :-(
How to inject it and use as DogFactory<Shepherd> or DogFactory<Corgi> (not just DogFactory)?
And another question. I also need to pass the Shepherd.class (or Corgi.class) to this bean, so it could "know" at run-time, objects of what exactly class should it produce. Is there a way to do that?
Or should I forget about FactoryBean and instantiate the factory as a regular class? Of course, I could do it this way:
DogFactory<Shepherd> shepherdFactory = new DogFactory<Shepherd>(Shepherd.class);
It would work perfectly, but I'd like to use FactoryBean as I use it for other factories in this project, so I would like to stick to FactoryBean.
Thanks in advance!
Update 1
Maybe I should clarify it more precise. What I need is a factory that could produce objects of different classes. All these classes should be extensions of the certain class (e.g., Shepherd and Corgi - all these classes extend Dog). And as the final result I actually need something like that:
public class DogService<T extend Dog> {
private DogFactory<T> dogFactory;
#Autowired
public DogService(DogFactory<T> dogFactory) {
this.dogFactory = dogFactory;
}
public T spawnDog(Color color) {
T dog = DogFactory.getObject(color);
return dog;
}
}
But it seems that I can't make a "universal" factory with the FactoryBean interface, so I can't do something like that:
public class DogFactory<T extends Dog> implements FactoryBean<T> {
// ...
}
Instead of that I have to do something like the following:
public class ShepherdFactory implements FactoryBean<Shepherd> {
// ...
}
public class CorgiFactory implements FactoryBean<Corgi> {
// ...
}
Is that true?
Thanks!
What I want is to instantiate this factory in another class
If you mean "having the factory injected in the class instead of the bean it creates", you could inject the factory by prefixing its name with an ampersand:
public class ShepherdService {
private DogFactory<Shepherd> shepherdFactory;
#Qualifier("&dogFactory")
public ShepherdService(DogFactory<Shepherd> shepherdService) {
this.shepherdService = shepherdService;
}
}
From the Spring documentation (section 3.8.3):
When you need to ask a container for an actual FactoryBean instance
itself, not the bean it produces, you preface the bean id with the
ampersand symbol & (without quotes) when calling the getBean method of
the ApplicationContext. So for a given FactoryBean with an id of
myBean, invoking getBean("myBean") on the container returns the
product of the FactoryBean, and invoking getBean("&myBean") returns
the FactoryBean instance itself.
Furthermore, my guess is that the FactoryBean is not picked up as a Spring bean as your snippets did not contain an XML config section, a Java config section, or a #Component annotation. I would explicitly declare the factory bean in a #Configuration annotated class, or annotate the factory class with #Component.
[edit]
But it seems that I can't make a "universal" factory with the
FactoryBean interface, so I can't do something like that:
You could still have a single Configuration class in which you declare all the factory beans.
#Configuration
public class DogFactoryConfig {
#Bean
public DogFactory<Shepherd> shepherdFactory() {
return new DogFactory<Shepherd>();
}
#Bean
public DogFactory<Corgi> corgiFactory() {
return new DogFactory<Corgi>();
}
}
And remove the #Component annotation from your DogFactory if it is present.
I have an interface which is defined in two places like that:
#Configuration
public class AppContext {
#Bean
public SomeInterface usageOne() {
return new SameImpl();
}
#Bean
public SomeInterface usageTwo() {
return new SameImpl(someOtherConfig);
}
#Bean
public Client clientOne(SomeInterface usageOne) {
return new Client(usageOne);
}
#Bean
public OtherClient clientTwo(SomeInterface usageTwo) {
return new OtherClient(usageTwo);
}
}
My client implementation classes do not have any annotations only required constructor. How to qualify the correct interface implementation usage in that case? I don't want to use #Primary as in my case it's semantically incorrect to name one of the usages as primary (they are in some sense equal). I need to pass the same interface with the same implementation class but configured differently for specific use cases of respected clients. I was thinking that naming the parameter by which I inject the implementation to the bean creation method is enough, but Spring complains with: required a single bean, but 2 were found. I don't understand how should I use the #Qualifier annotation.
I'm running with Spring Boot 2.0.4.RELEASE and respected beans and clients created in separate configuration classes so I cannot just call usageTwo() method when creating OtherClient like that: new OtherClient(usageTwo()); as this method is not available in clients configuration class.
As mentioned by #chrylis in the comments, you can simply add the #Qualifier annotation to the #Bean methods like this:
#Bean
public Client clientOne(#Qualifier("usageOne") SomeInterface usageOne) {
return new Client(usageOne);
}
#Bean
public OtherClient clientTwo(#Qualifier("usageTwo") SomeInterface usageTwo) {
return new OtherClient(usageTwo);
}
The value specified as value for the #Qualifier annotation is the respective bean's name. That is either the name of the corresponding #Bean method or the value of the annotation if used like this #Bean("usageThree").
Person.java
#Controller
public class Person
{
#Autowired
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}
Ability.java
#Controller
public class Ability
{
void printMessasge(){
System.out.println("I print message");
}
}
spring.xml
<bean id="invisible" class="com.mkyong.common.Ability" >
</bean>
<context:component-scan base-package="com.mkyong" />
App.java
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring.xml");
Person person = (Person) context.getBean("person");
person.printMessage( );
}
In the above example i have defined two beans of Ability class one using #Controller and one in xml file. According to Autowire by type i should get
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException:
...
No unique bean of type [com.mkyong.common.Ability] is defined:
But i am getting proper output. Why?
And if i create an interface which Ability class implements then i would get UnsatisfiedDependencyException exception. Like this:
Parent.java
public interface Parent{
public void printMessasge();
}
Ability.java
#Controller
public class Ability implements Parent
{
void printMessasge(){
System.out.println("I print message");
}
Person.java
#Controller
public class Person
{
#Autowired
Parent parent;
public void printMessage(){
parent.printMessasge();
}
public void setOutputGenerator( Parent parent) {
this.parent= parent;
}
}
By default Spring indeed matches by type, but in case of multiple matching beans, it falls back to matching by name. The reference (6.9.4 Fine-tuning annotation-based autowiring with qualifiers) says:
For a fallback match, the bean name is considered a default qualifier value.
Using the #Qualifier annotation makes the autowiring by type and additionally by name more explicit.
You are not going to get a UnsatisfiedDependencyException with this configuration because Spring is smart enough to also look at the name of your required dependency, as #Adam writes in his answer. So even if the default autowiring mode is by type and there are two beans of the same type for that dependency, Spring is able to solve the conflict.
This behavior is described in the official documentation, 6.9.4 Fine-tuning annotation-based autowiring with qualifiers section.
More details about autowiring modes can be found 6.4.5 Autowiring collaborators.
If you change the name of Person.ability field to something else, like Person.ability2, then Spring will throw a org.springframework.beans.factory.NoUniqueBeanDefinitionException. It cannot decide which bean it should wire for the Person.ability2 field because:
There are two beans of com.mkyong.common.Ability type in the context
Field name does not match with either bean names
EDIT: in the second case you mention (with the interface), Spring throws the exception for the same bean naming reason I explained above.
Official documentation references:
6.4.5 Autowiring collaborators
6.9.4 Fine-tuning annotation-based autowiring with qualifiers
you have two ambiguous beans defined, when spring try find the bean via type it finds two beans, one via scan other explicitly defined in spring.xml. use #Qualifier to mark the correct bean loaded in Person class. So probably you want like this,
#Controller
public class Person
{
#Autowired
#Qualifier("invisible")
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}
I would like to have a singleton bean instance by generic parameter based on a single #Component generic class.
(I am using Spring 4.)
My code :
I have an interface like this :
public interface Mapper<I, O> {
...
}
And multiple implementation of it which are Spring #Components (singletons). Something like this :
#Component
public class MapperA implements Mapper<ClazzAI, ClazzAO> {
...
}
and
#Component
public class MapperB implements Mapper<ClazzBI, ClazzBO> {
...
}
where ClazzAI, ClazzAO, ClazzBI and ClazzBO are basic Java classes.
I have another Spring #Component (singleton) which have a Mapper class as a generic parameter :
#Component
public class TransformerImpl<I, O, M extends Mapper<I, O>> {
/** The Mapper */
protected final M mapper;
#Inject
private TransformerImpl(final M mapper) {
this.mapper= mapper;
}
...
}
and I would like to use it like this :
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
#Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
The problem :
But Spring is not able to instantiate those 2 objects because it founds 2 implementations of Mapper : MapperA and MapperB even if I specify which implementation I want as a generic parameter.
Any idea how to make it without the need of instantiate all of those beans in a #Configuration class ?
You're asking for a singleton but requiring two injection points
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
#Inject
private TransformerImpl<ClazzBI, ClazzBO, MapperB> transformerB;
for differently constructed objects. That doesn't make much sense.
You now realize you need two beans. If you can't (don't want to) do it in a #Configuration class with #Bean factory methods, you'll need to declare (and scan) two separate #Component classes. (I made your parent constructor public here.)
#Component
class MapperATransformerImpl extends TransformerImpl<ClazzAI, ClazzAO, MapperA> {
#Inject
public MapperATransformerImpl(MapperA mapper) {
super(mapper);
}
}
#Component
class MapperBTransformerImpl extends TransformerImpl<ClazzBI, ClazzBO, MapperB> {
#Inject
public MapperBTransformerImpl(MapperB mapper) {
super(mapper);
}
}
When processing the injection target
#Inject
private TransformerImpl<ClazzAI, ClazzAO, MapperA> transformerA;
Spring will find the MapperATransformerImpl, which is of type TransformerImpl<ClazzAI, ClazzAO, MapperA> and inject that.
Try with Spring 4. See Using generics as autowiring qualifiers
Edit
Like #SotiriosDelimanolis explained in his answer, Spring 4 can use type parameter information as qualifiers to select which bean definition matches a particular injection point, but in the end, it will only match against bean definition with concrete type definitions. In your case, the problem is that you need a TransformerImpl bean definition for each concrete type you want to inject.
As an alternative to defining all bean definition explicitly, check my answer to Spring autowiring issues on paramaterized class
I have a question related to Spring injection.
I have a class defined with a generic type parameter. I want to know whether it is possible to inject the class object of the type T ( I mean T.class) ?
Like this:
#Component
public class MyExecutor<T> {
#Autowired
public MyExecutor(<Inject class object of T>) {
....
}
}
Thank you very much.
That concrete example would not work, but there is another way to create beans using generics and keep the type when injecting the bean in other beans.
Spring 4 has extended the support for generics in Java configuration. It is now possible to define two beans that differ only in the generic parameter used, and inject them by type in another bean, see this JIRA:
#Configuration
public class Config {
#Bean("beanA")
public MyExecutor<A> beanA() {
return new MyExecutor<A>(A.class);
}
#Bean("beanB")
public MyExecutor<B> beanB() {
return new MyExecutor<B>(B.class);
}
}
Then beanA or beanB can be injected by type:
#Component
public class OtherClass {
#Autowired
private MyExecutor<A> beanA;
}