0

I want to inject a service into another generic service.

I have an abstract class ApplicationAccessor:

@Component
public abstract class ApplicationAccessor {

    @Autowired
    protected ApplicationRepository applicationRepository;

    @Autowired
    protected ApplicationTypeRepository applicationTypeRepository;

    public abstract List<ApplicationTypeEntity> getAllApplicationsTypes();

    public abstract List<ApplicationEntity> getAllApplications();
}

Which is extended by two Application Accessors:

@Component
public class DefaultApplicationAccessor extends ApplicationAccessor {

    @Override
    public List<ApplicationTypeEntity> getAllApplicationsTypes() {
        return this.applicationTypeRepository.findAllNonRemoteWorkApplicationTypes();
    }

    @Override
    public List<ApplicationEntity> getAllApplications() {
        return this.applicationRepository.findAllNonRemoteWorkApplications();
    }
}

And RemoteWorkApplicationAccessor which is very similar to the one above.

I also have ApplicationService which is generically dependent on ApplicationAccessor

@Service
public class ApplicationServiceImpl<T extends ApplicationAccessor> implements ApplicationService<T> { }

And ApplicationService (interface that implements ApplicationServiceImpl):

public interface ApplicationService<T extends ApplicationAccessor> { }

In ApplicationServiceImpl I want to have a generic property with type T being ApplicationAccessor which is autowired:

@Autowired
private T applicationAccessor;

Then in the controller I want to inject generic ApplicationService:

@Autowired
private ApplicationService<DefaultApplicationAccessor> applicationService;

But unfortunately I get an error:

Parameter 0 of constructor in ApplicationServiceImpl required a single bean, but 2 were found:
- defaultApplicationAccessor: defined in file [...]
- remoteWorkApplicationAccessor: defined in file [...]

What can I do to fix it?

RarstManPL
  • 13
  • 4
  • Spring bean by default uses its interface name if I remember correctly. You have 2 implementations there for the same interface, so they have the same bean name, and the same bean name conflicts, which results in that error. One solution is, you may give your bean a different name, in `@Service` alike annotations. And use that name to inject, name like `qualification` something. – Tiina Jul 18 '23 at 09:13
  • @Tiina where I should do this? – RarstManPL Jul 18 '23 at 09:20
  • "@Service("yourServiceName")" with a specific "@Bean" annotation or "@Primary" annotations (if you are using the interfaces only) are solutions to your problem as others persons have already suggested. – BendaThierry.com Jul 18 '23 at 09:52
  • @RarstManPL have a look here: `https://www.baeldung.com/spring-bean-names` pay attention to section 4 and 5. – Tiina Jul 18 '23 at 10:01
  • [This answer](https://stackoverflow.com/a/50987156/10231374) will solve your problem. – Toni Jul 18 '23 at 19:46

1 Answers1

0

If you use @Primary on the implementation you wish to use by default, Spring will prefer that bean over any others. For example, if you wish Spring to use DefaultApplicationAccessor:

@Component
@Primary
public class DefaultApplicationAccessor extends ApplicationAccessor {

    @Override
    public List<ApplicationTypeEntity> getAllApplicationsTypes() {
        return this.applicationTypeRepository.findAllNonRemoteWorkApplicationTypes();
    }

    @Override
    public List<ApplicationEntity> getAllApplications() {
        return this.applicationRepository.findAllNonRemoteWorkApplications();
    }
}
NorthernSky
  • 488
  • 2
  • 10
  • This solution doesn't work in case `@Autowired private ApplicationService applicationService;`, where it's expected that `RemoteWorkApplicationAccessor` is autowired in `ApplicationService`, but it's always `DefaultApplicationAccessor`. – Toni Jul 18 '23 at 16:55