spring boot interface extends multiple interfaces - java

I have an interface A and a class AImpl that implements it.
public interface A {a()}
#Component
class AImpl implements A{...}
and I have an interface B and a class BImpl that implements it.
public interface B {b()}
#Component
class BImpl implements B{...}
A and B have different methods
interface C extends A, B {...}
is there a way to Autowired C c; without putting any qualifier and call
c.a();

AFAIK you don't need any qualifier to autowire a bean of type C. Just do:
#Component
class CImpl implements C {...}
#Service
class OtherService {
#Autowired
C c;
}

Related

Spring inject interface implementation

I'd like to use lombok to inject a class implemented from a interface like below:
#RequiredArgsConstructor(onConstructor = #_(#Inject))
public class className {
#NonNull
private final ClassA1 a1 implements ClassA;
...
}
But obviously this is not working, so what's the correct way to do this?
=================
edit:
Or should I do this way?
public class className {
private ClassA a1;
public className(A1 a1) {
this.a1 = a1; }
}
===================
Here's the code after taking advice from Mykhailo Moskura:
#Component
#RequiredArgsConstructor(onConstructor = #_(#Inject))
public class C {
#NonNull
private A b;
public someFunction() {
b.method();
}
}
Here A is the interface, while b is Class implementing A with camelcase name. And using lombok I injected b, and now call some method of b in some function. However I realized b.method still points to the interface A, but not B.
#NonNull is not required
Lombok will generate a constructor with fields that are marked as final or #NonNull
You can autowire just declaring the interface type
and giving the implementation class name in camel case starting from lower case.
Also you need to declare your implementation as bran and the class in which you are injecting it too.
#Inject is java ee CDI dependency.
#Autowired is spring specific.
Spring supports both but it says to use #Autowired
Here is an example:
public interface A{
}
#Component
public class B implements A{
}
#Component
public class C {
private A a;
#Autowired
public C(A a){
this.a = a;
}
}
Lombok sample:
#RequiredArgsConstructor
#Component
public class C {
//Here it will inject the implementation of A interface with name of implementation (As we have name of impl B we declare field as b , if HelloBeanImpl then helloBeanImpl
private A b;
}
But if you have many implementations of one interface you can use #Qualifier with name of bean or the above sample with lombok where A b where b is the name of implementation

Spring - Inject proper service implementation in abstract class level autowired field

I have 2 class hierarchies:
* ClassA
* ClassB
* AbstractClass
* Class1
* ...
* Class5
AbstractClass autowires ClassA as follows:
public abstract class AbstractClass {
#Autowired
protected ClassA classA;
}
Now I would like to inject ClassA into Class1, .., Class4 implementations but ClassB into Class5. I'm aware that I can do that by injecting directly in implementing classes rather than in abstract class (as in Similar Question) but that means that I have to have the same field declared not once but five times. Additionally if I want to use this field in abstract class I would have to enforce creating getter in implementing class and use it to get that service. It works but it doesn't seem to me like right way to do it.
Here's one way to do it
#Component
class ClassA {}
#Component
class ClassB extends ClassA {}
abstract class AbstractClass {
protected ClassA classA;
}
#Component
class Class1 extends AbstractClass {
public Class1(ClassA classA) {
this.classA = classA;
}
}
//... Same for Class2/3/4
#Component
class Class5 extends AbstractClass {
public Class5(ClassB classB) {
this.classA = classB;
}
}
This lets you have the common property and methods in the abstract class and if qualify them in the child classes using constructor injection

Dynamically create Spring beans that extend an abstract class with parameterized types

I would like to take advantage of Spring 4.0's support for autowiring of generic types but I would like to avoid having to create explicit concrete or anonymous classes for each type. To use an example, lets say I have an interface:
public interface Cache<T extends Entity>
And an abstract implementation of the interface:
public abstract class AbstractCache<T extends Entity> implements Cache<T>
{
#Autowired
private EntityDao<T> dao;
#Autowired
private List<CacheListener<T>> listeners;
...
}
And entity classes A to Z that implement Entity (e.g):
public class A implements Entity
public class B implements Entity
...
public class Z implements Entity
Is there a way I can create instances of Cache<A> through Cache<Z> such that I can autowire these generic types in other classes? E.g.
#Autowire
private Cache<Z> zCache;
I know I can achieve this by individually defining each bean, E.g.
#Bean
public Cache<Z> cacheZ() {
return new AbstractCache<Z> () {};
}
But I have been unable to come up with a way to do this for all Entity classes in a particular package. E.g.
public void registerEntityCaches (BeanFactory beanFactory) {
for (Class<? extends Entity> cls : entityPackage.getAllClasses()) {
beanFactory.registerBean(new AbstractCache<cls>() {});
}
}
Is something like this possible or do I have to define them individually?

Spring Bean implementing multiple interfaces

I have a bean which implements two interfaces. The barebones code is as follows:
interface InterfaceA {
...
}
interface InterfaceB {
...
}
public class ClassC implements InterfaceA, InterfaceB {
...
}
In my AppConfig I am specifying the following:
#Bean(name = "InterfaceA")
public InterfaceA interfaceA() {
return new ClassC();
}
#Bean(name = "InterfaceB")
public InterfaceB interfaceB() {
return new ClassC();
}
And I use it so:
public class MyClass {
#Inject
private final InterfaceA a;
public MyClass(#Named("InterfaceA") InterfaceA a) {
this.a = a;
}
...
}
However, Spring complains that:
No qualifying bean of type
[com.example.InterfaceA] is
defined: expected single matching bean but found 2:
InterfaceA, InterfaceB
Similar question was asked and answered for EJB here but I could not find anything for Spring beans. Anybody know the reason?
The workaround is to introduce a new interface which extends both InterfaceA and InterfaceB and then let ClassC implement that. However, I am loath to change my design because of framework constraints.
Thank you for your excellent question.
In my case, I created an interface that extends both A and B:
public interface InterfaceC extends InterfaceA, InterfaceB {}
... and the common implementation implements the unified interface:
public class ClassC implements InterfaceC {
//...
}
This unified interface allows then to create a single bean:
#Bean
public InterfaceC implementationForAandB() {
return new ClassC();
}
The Spring framework is then able to inject or autowire the common implementation to dependencies expressed in terms of the primary interfaces:
public class MyClass {
#Inject
private final InterfaceA a;
#Inject
private final InterfaceB b;
public MyClass(InterfaceA a, InterfaceB b) {
//...
}
}
Spring is right ... When you write
#Bean(name = "InterfaceA")
public InterfaceA interfaceA() {
return new ClassC();
}
#Bean(name = "InterfaceB")
public InterfaceB interfaceB() {
return new ClassC();
}
Spring creates to ClassC objects, one named InterfaceA, the other InterfaceB, both implementing InterfaceA and InterfaceB.
Then when you write :
#Inject
private final InterfaceA a;
you ask Spring to find a bean implementing InterfaceA, but as said above there are 2 so the error.
You could either create only one object of type ClassC, or use #Qualifier or #Named annotations :
#Inject
#Named("InterfaceA")
private final InterfaceA a;
That way, you explicitely ask Spring to find the bean named InterfaceA, and hopefuly it is now unique.

Resolving Spring conflict with #Autowired and #Qualifier

I have an interface
public interface ParentService{}
And Two Implementation Class
#Service("child1service")
public class Child1 implements ParentService{}
#Service("child2service")
public class Child2 implements ParentService{}
Now my Controller
public class ServeChild1Controller extendds AbstractController{
#Autowired
public ServeChild1Controller(#Qualifier("child1service") ParentService child1service){
super(child1service)
}
Similarly there is ServeChild2Controller..
So when i run I get the following error
Error for ServeChild1Controller: No unique bean of type [com.service.ParentService] is defined: expected single matching bean but found 2 child1service, child2service
Am trying to read more about these Annotations but not able to resolve it ..
Any pointers will be of help
Thanks
In order to use a specific instance you need to provide Annotate the service with #Qualifier(id) and in the constructor anotate the parameter with #Qualifier again, as follows:
#Service("child1service")
#Qualifier("child1service")
public class Child1 implements ParentService{}
#Service("child2service")
#Qualifier("child2service")
public class Child2 implements ParentService{}
And you constructor:
public class ServeChild1Controller extendds AbstractController{
#Autowired
public ServeChild1Controller(#Qualifier("child1service") ParentService child1service){
super(child1service)
}
}
With Spring (beans) 4.3 it works exactly the way you wrote it in your question.
I can give you example with implementation groupping that I faced recently. Spring can autowire based on on type and qualifier distinction. Using service names is not enough as they need to be unique so you would end up with type conflict.
Example:
public interface ServiceA {}
public interface ServiceB {}
#Qualifier("mockedA")
#Service
public class MockedA implements ServiceA {}
#Qualifier("realA")
#Service
public class RealA implements ServiceA {}
#Qualifier("mockedB")
#Service
public class MockedB implements ServiceB {}
#Qualifier("realB")
#Service
public class RealB implements ServiceB {}
#Autowired
public ABController (
#Qualifier("mockedA") ServiceA mockedA,
#Qualifier("realA") ServiceA realA,
#Qualifier("mockedB") ServiceB mockedB,
#Qualifier("realB") ServiceB realB) {
}
I think the #Qualifier annotation might need to be provided at the same level as the #Autowired annotation.

Categories