Resolving Spring conflict with #Autowired and #Qualifier - java

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.

Related

Why change #Repository to #Service solved the UnsatisfiedDependencyException?

#Service
public class A implements ServiceA {
#Autowired
private ServiceB b;
}
#Service
public class B implements ServiceB {
#Autowired
private ServiceC c;
}
#Repository
public class C implements ServiceC {
#Autowired
private ServiceA a;
}
When start the application, it will throw the Exception like this "org.springframework.beans.factory.UnsatisfiedDependencyException. Message: Error creating bean with name.......org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'ServiceC': Bean with name 'ServiceC' has been injected into other beans ...in its raw version as part of a circular reference, but has eventually been wrapped.".
I know we can use #Lazy or other methods to solve the Exception, but, when I only changed class C's annotation #Repository to #Service, the application starts normally without Exceptions. This confused me, why the application don't throw UnsatisfiedDependencyException? Whats the differences between #Service and #Repository?
ps. I have read some materials like this What's the difference between #Component, #Repository & #Service annotations in Spring?

How to use #Qualifier with Service and Repository

I have a generic repository interface called UserRepository.
I then have an interface which extends from that called MyUserRepository.
It deals with an MyUser class which extends User.
I also have a Service Interface called UserService and a class called MyUserServiceImpl.
The service wants an instance of the UserRepository and I though I could use some sort of annotation like #Qualifier but it doesn't work.
#NoRepositoryBean
public interface UserRepository <T extends User> extends JpaRepository<T, Long>{
<S extends T> S findByLoginName(String loginName);
<S extends T> S saveAndFlush(User user);
}
#Repository
#Qualifier("myUserRepository")
public interface MyUserRepository extends UserRepository<MyUser> {
}
public interface UserService {
public List<User> getUsers();
}
#Service
public class MyUserServiceImpl implements UserService {
#Autowired
#Qualifier("myUserRepository")
private UserRepository<User> userRepository;
#Override
public List<User> getUsers() {
....
}
}
APPLICATION FAILED TO START
Description:
Parameter 0 of constructor in com....services.MyUserServiceImpl
required a bean of type 'com....repositories.UserRepository' that
could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com....repositories.UserRepository'
in your configuration.
#Qualifier annotation is used only when calling a bean already created. So you shouldn't call on class head, you might name it #Repository("myUserRepository") and call it on after #Autowired #Qualifier("myUserRepository")
No need to have a qualifier in your case.
#Repository
public interface MyUserRepository extends UserRepository<MyUser> {
}
Auto-wire the repository as :
#Service
public class MyUserServiceImpl implements UserService {
#Autowired
private UserRepository<User> userRepository;
...
#Qualifier is used with #Autowired annotation. By default #Autowired will inject beans based on types.When you have multiple beans of same types then #Qualifier helps to resolve the conflict. In your case using annotation #Repository will do you job. Also in your UserRepository interface , you have to supply the Id class along with JPA entity class.

SpringBoot Autowiring a generic type fails because of multiple possible beans

I'm trying to create a class that Autowire an object of type T.
#component
public class TaskScheduler<T extends TaskService>{
#Autowired
private T taskService;
}
the problem is that I have two components that extend TaskService.
#component
public class firstTaskService extends TaskService {
}
and
#component
public class secondTaskService extends TaskService {
}
so when this line is executed (ts is being created)
#Autowired
TaskScheduler<firstTaskService> ts;
I get this error :
Description:
Parameter 1 of constructor in TaskScheduler required a single bean, but 2 were found
the message I got suggested this :
Action: Consider marking one of the beans as #Primary, updating the consumer to accept multiple beans, or using #Qualifier to identify
the bean that should be consumed.
But from what I understood, the #Primary and #Qualifier annotations make me choose 1 of the components, which not what I want because I want to use firstTaskService and secondTaskService with that same class (TaskScheduler).
How could this be done?
Edit: Clarification: My objective is to reuse the TaskScheduler class with different classes that extend the TaskService class (not to use multiple classes that extend TaskService together in TaskScheduler).
If you want to autowire all beans that extends TaskService maybe you should change the autowired field to a List:
#Component
public class TaskScheduler<T extends TaskService>{
#Autowired
private List<T> taskService;
}
In this way Spring should put in the List all autowireable beans that extends TaskService.
EDIT: since you want to dinamically select the type of TaskService the only way I've found is the following. First, redefine your TaskScheduler:
public class TaskScheduler <T extends TaskService>{
private T taskService;
public void setTaskService(T taskService) {
this.taskService = taskService;
}
}
Your TaskService and related subclasses should remain untouched. Set up a configuration class as it follows:
#Configuration
public class TaskConf {
#Autowired
private FirstTaskService firstTaskService;
#Autowired
private SecondTaskService secondTaskService;
#Bean
public TaskScheduler<FirstTaskService> firstTaskServiceTaskScheduler(){
TaskScheduler<FirstTaskService> t = new TaskScheduler<>();
t.setTaskService(firstTaskService);
return t;
}
#Bean
public TaskScheduler<SecondTaskService> secondTaskServiceTaskScheduler(){
TaskScheduler<SecondTaskService> t = new TaskScheduler<>();
t.setTaskService(secondTaskService);
return t;
}
}
And then test your TaskScheduler in this way:
#Autowired
TaskScheduler<firstTaskService> ts;

EJB override injection of abstract bean

I have an abstract bean with injected ISomeOtherBean interface:
public abstract class AbstractBean {
#EJB ISomeOtherBean myService;
}
The ISomeOtherBean is interface. He have many implementations, for example: SomeBeanA, SomeBeanB...
#Stateless
public class SomeBeanA implements ISomeOtherBean {
}
#Stateless
public class SomeBeanB implements ISomeOtherBean {
}
How can I inject concrete implementation of ISomeOtherBean inside some implementation of the AbstractBean?
#Stateless
public class BeanImpl extends AbstractBean {
// how write that I want inject SomeBeanB which implements ISomeOtherBean
}
First i tried to inject concrete implementation inside BeanImpl, then I passed it to additional method of superclass (Abstractbean) which writed it to field myService.
But this solution is bad for me. I remember Spring has ability to pass some beans to constructor of given bean. But it was inside XML configuration. I want something like this, but with EJB and annotation configurations.
Anyone knows how do it?

Spring autowiring parameter by name

I have two beans with the type InterfaceA.
I was trying to inject the bean into an argument of a #Bean method using #Qualifier to autowire by name.
I was surprising that Spring can't resolve the proper bean unless your parameter name is matching the bean name.
I was trying:
#Component
public class ClassA implements InterfaceA {
}
#Component
public class ClassB implements InterfaceA {
}
#Configuration
public class AppConfig {
#Bean
#Autowired
#Qualifier("classA")
public SomeOtherClass someOtherClass(InterfaceA object) {...}
}
But got the NoUniqueBeanDefinitionException.
However if I use parameter name matching the component name it works fine.
#Configuration
public class AppConfig {
#Bean
#Autowired
public SomeOtherClass someOtherClass(InterfaceA classA) {...}
}
Could someone explain why I can't use autowiring by name with #Resource or #Qualifier here?
Add the #Qualifier annotation to the parameter not to the method:
public SomeOtherClass someOtherClass(#Qualifier("classA") InterfaceA object) {...}

Categories