3

I am trying to resolve generic types in a FactoryBean in spring version 4.3.6. I create the beans using a FactoryBean which is used to create any number of instances of CarClient.

public class CarFactoryBean<T> implements FactoryBean<CarClient<T>>{

    @Autowired
    CarClientCreationHelper cch;

    private Class<T> type;

    @Overrides
    public CarClient<T> getObject() throws Exception {
        return cch.provideCar(type);
    }

    @Override
    public Class<?> getObjectType() {
        ResolvableType resolvableType = ResolvableType.forClassWithGenerics(CarClient.class, type);
        return resolvableType.resolve();
    } 

    @Override
    public boolean isSingleton() {
        return true;
    }

    public void setType(Class<T> type) {
        this.type = type;
    }
}

I am registering these beans as follows,

public class CarClientRegistrar  implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

    private MyBeanNameGenerator bng;

    public CarClientRegistrar(){
        this.bng = new MyBeanNameGenerator();
    }


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //logic to obtain serviceInterface, working correctly

         AbstractBeanDefinition definition = BeanDefinitionBuilder.genericBeanDefinition(CarFactoryBean.class)
            .addPropertyValue("type", serviceInterface)     
            .getBeanDefinition();

        definition.setDescription("desc");

        final String beanName = bng.generateBeanName(definition, registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(new BeanDefinitionHolder(definition, beanName,
            new String[]{beanName.substring(0, beanName.lastIndexOf("/"))}), registry);

    }
}

Finally, i am importing the CarClientRegistrar in the AppConfig,

@Import({CarClientRegistrar.class})
public class ApplicationConfig {

}

and autowiring a CarClient,

  @RestController
    @RequestMapping(value = "blah")
    public class CallsController {
        private CarClient<Ford> fb;

        @Autowired
        public CallsController(CarClient<Ford> fordBean){
           this.fb = fordBean;
        }
    }

I get the following exception,

Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.empire.client.CarClient' available: expected single matching bean but found 2: car1Impl,car2Impl

However, if i rename, fordBean to car1Impl (which is the required bean), it works

 @Autowired
    public CallsController(CarClient<Ford> car1Impl){
        this.fb = fordBean;
    }

Any ideas on how to resolve the generic type in the FactoryBean, without depending on bean name (but on type)?

ResolvableType resolvableType = ResolvableType.forClassWithGenerics(CarClient.class, type);
        return resolvableType.resolve(); 

doesn't seem to work.

Thanks in advance!

Akhil
  • 183
  • 1
  • 1
  • 8
  • I think there is useful information about it in this post: https://stackoverflow.com/questions/22603291/how-to-autowire-bean-of-generic-type-t-in-spring – Daniel C. Aug 23 '17 at 02:39

1 Answers1

1

I solved this by resolving the type in the CarClientRegistrar and assigning it as a target type to BeanDefinition.

ResolvableType resolvableType = ResolvableType.forClassWithGenerics(AsyncClient.class, serviceInterface);

RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setTargetType(resolvableType);
rootBeanDefinition.setBeanClass(CarFactoryBean.class);
rootBeanDefinition.getPropertyValues().add("type", serviceInterface);
rootBeanDefinition.setDescription("desc");

instead of using a GenericBeanDefinition

Akhil
  • 183
  • 1
  • 1
  • 8