0

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?

Richard
  • 5,840
  • 36
  • 123
  • 208
  • 1
    It seems duplicated in here: https://stackoverflow.com/questions/22300102/no-unique-bean-of-type-is-defined-expected-single-matching-bean-but-found-2 – Kenny Tai Huynh Aug 22 '17 at 19:01
  • @KennyTaiHuynh I know why it's happening, I want to know what the solution to this issue is. – Richard Aug 22 '17 at 19:24

2 Answers2

1

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);
    }
}
  • How exactly would that work? Would I have a factory for each vehicle type and strategy type? Like a `FooACarFactory`, `FooBCarFactory`, `FooATruckFactory` etc etc – Richard Aug 23 '17 at 12:19
  • Yes, but there are optimizations, e.g. by manipulating Spring configurations to create different sets of beans to inject based on jar files in the path. Or see my edit for a manual way via a generic factory. –  Aug 23 '17 at 15:32
0

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.

Kenny Tai Huynh
  • 1,464
  • 2
  • 11
  • 23
  • this is not really what I want. Please take a look at the original post again. I can't just inject the strategy. The service classes define the type of strategy through the Vehicle interface. – Richard Aug 22 '17 at 20:40
  • @Richard: Just add some more code to the answer, can you try?! – Kenny Tai Huynh Aug 22 '17 at 21:57
  • No this doesn't work. The problem is not the `Car` object, it's defining the strategy. Giving the car a component name doesn't help in this regard – Richard Aug 23 '17 at 01:45
  • it's interesting. I tried with the same code and it's working. :) – Kenny Tai Huynh Aug 23 '17 at 18:12