Search code examples
javaspringspring-bootabstract-factory

How to integrate a factory method which accepts Class<T> and produces T into Spring


I have a factory object which implements something like the following:

public interface MyFactory {

    <T> T getInstance(Class<T> clazz);
}

It can be used like this:

MyService s = factory.getInstance(MyService.class);

It can produce many kinds of instances based on clazz. If it gets a clazz which is not supported by the factory object, it returns null.

Now I'm writing a Spring application (Spring Boot 2.0.1) and want to use its injection mechanism with the factory object. For example, I want to do something like this:

@Controller
public class MyController {

    @Autowired
    private MyService s;
}

Is there any way to integrate the MyFactory object into Spring like this? I know I can create bindings for each class manually but I'm looking for an easier way.


Solution

  • I added the following method which returns a set of classes the factory object supports:

    Set<Class<?>> getSupportedClasses();
    

    Then I put the following class into my Spring app and it seems to be working fine:

    @Component
    @Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
    public class SomeBean {
    
      private static final Logger log = LoggerFactory.getLogger(SomeBean.class);
    
      private final MyFactory factory;
      private final GenericApplicationContext context;
    
      @Autowired
      public SomeBean(GenericApplicationContext context, MyFactory factory) {
        this.context = context;
        this.factory = factory;
      }
    
      @PostConstruct
      public void init() {
        factory.getSupportedClasses().forEach(this::register);
      }
    
      private <T> void register(Class<T> clazz) {
        log.info("Registering {} as a bean into ApplicationContext", clazz);
        context.registerBean(clazz,
            () -> factory.getInstance(clazz),
            (beanDefinition -> beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)));
      }
    }