Reputation: 6116
I have a interface with an implementation like this:
public interface Vehicle<T> {
void stuff(T param);
}
@Component
public class Car<T> implements Vehicle<T> {
private final Strategy<T> strat;
public Car(@Lazy Strategy strat) {
this.strat = strat;
}
void stuff(T param) {
strat.stuff(param);
}
}
The strategy interface/classes looks like this:
public interface Strategy<T> {
void stuff(T param);
}
@Component
public class FooAStrategy implements Strategy<FooA> {
void stuff(FooA param) { //do stuff }
}
@Component
public class FooBStrategy implements Strategy<FooB> {
void stuff(FooABparam) { //do stuff }
}
Finally, I have a service class like this:
@Service
public class ServiceClassA {
private FooA fooA;
private FooB fooB;
private FooC fooC;
private Vehicle<FooD> vehicle; //how to inject correct implementation?
public ServiceClass(FooA fooA, FooB fooB, FooC fooC) {
this.fooA = fooA;
this.fooB = fooB;
this.fooC = fooC;
}
}
@Service
public class ServiceClassB {
private FooA fooA;
private FooB fooB;
private FooC fooC;
private Vehicle<FooE> vehicle; //how to inject correct implementation?
public ServiceClass(FooA fooA, FooB fooB, FooC fooC) {
this.fooA = fooA;
this.fooB = fooB;
this.fooC = fooC;
}
}
Essentially, for the first service class, ServiceClassA
, I want to do this:
Strategy strat = new FooAStrategy();
Vehicle vehicle = new Car(strat);
ServiceClassA class = new ServiceClassA(fooA, fooB, fooC, vehicle);
But every time I run the application I get this error:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.test.notification.Strategy<?>' available: expected single matching bean but found 2: fooAStrategy,fooBStrategy
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:173)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116)
at org.springframework.context.annotation.ContextAnnotationAutowireCandidateResolver$1.getTarget(ContextAnnotationAutowireCandidateResolver.java:83)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192)
at com.sun.proxy.$Proxy259.generateNotifications(Unknown Source)
at com.test.notification.RabbitMQEventPublisher.process(RabbitMQEventPublisher.java:24)
at com.test.services.ServiceClassA.testConncetion(ServiceClassA.java:400)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy260.testConnection(Unknown Source)
at com.test.controllers.ControllerTest.get(ControllerTest.java:130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at com.test.security.jwt.JwtTokenAuthenticationProcessingFilter.successfulAuthentication(JwtTokenAuthenticationProcessingFilter.java:52)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:240)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:96)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Since I'm letting spring boot wire everything up by itself using the @Component
annotation and constructors I don't have any config classes. I don't know how to specify the concrete implementation of 1 dependency.
What would be the best way to go about this?
Upvotes: 0
Views: 15876
Reputation:
Remove the @Component from the car and inject a factory. Like the message says, it's ambiguous to Spring what needs to be created. One solution is to create the car yourself via a factory, such as FooACarFactory
(which may implement a generic FooAVehicleFactory
). You can let Spring create the FooACarFactory
and inject that into the service constructor which has vehicle = fooAVehicleFactory.create()
. Note: if you have both a FooACarFactory
and a FooATruckFactory
on the component scan path you could use the @Primary
annotation mentioned by @Kenny to force Spring to inject the one you want. Alternatively place the jar with the correct implementation in the java class path.
Or, rather than create variations of car factory you could have a generic factory:
@Component
public class VehicleFactory {
@Autowired
FooAStrategy fooAStrategy;
@Autowired
FooBStrategy fooBStrategy;
boolean buildTruck = true; //get from external system parameter instead
public <T> Vehicle<T> getObject(Class<?> clazz) throws Exception {
if (clazz.equals(FooA.class)) {
if (buildTruck) {
return (Vehicle<T>) new Truck<FooA>(fooAStrategy);
}
return (Vehicle<T>) new Car<FooA>(fooAStrategy);
}
return (Vehicle<T>) new Car<FooB>(fooBStrategy);
}
then in the service class:
@Service
public class ServiceClassA {
private FooA fooA;
private FooB fooB;
private Vehicle<FooA> vehicle;
@Autowired
public ServiceClassA(
FooA fooA, FooB fooB,
CarFactory vehicleFactory
) throws Exception {
this.fooA = fooA;
this.fooB = fooB;
this.vehicle = vehicleFactory.<FooA>getObject(FooA.class);
}
}
Upvotes: 1
Reputation: 1599
As your comment, I'd like to correct the answer. It is also the same idea. Using @Primary
, @Component
and @Resource
.
@Primary
@Component("car")
public class Car<T> implements Vehicle<T> {
//...
}
In service to use the Vehicle, sth looks like:
@Service
public class ServiceClassA {
private FooA fooA;
private FooB fooB;
private FooC fooC;
@Resource(name="car")
private Vehicle<FooA> vehicle;
...
}
@Primary
@Component("fooAStrategy")
public class FooAStrategy implements Strategy<FooA> {
}
@Component("fooBStrategy")
public class FooBStrategy implements Strategy<FooB> {
}
Hope this help.
Upvotes: 0