RandomQuestion
RandomQuestion

Reputation: 6998

How to inject singleton objects produced into factory

Even after going through similar questions on factory and dependency injection, I couldn't find an answer.

Let's say I have a EventHandler factory which gives out different kind of EventHandler based on event type. Each type of EventHandler has it's own dependencies used in handling that event and these could be different from other EventHandler type.

public interface EventHandler {
   void handle(Event e);
}

public class EventHandlerA implements EventHandler{
  private DependencyA depA;
  //constructors for depA
}

public class EventHandlerB implements EventHandler{
  private DependencyB depB;
  //constructors for depB
} 

public class Factory {
 public EventHandler getHandler(EventType type) {
    if(type.equals("A")) {
        //return instance of EventHandlerA
    }
 }
}

@Configuration
public class Configuration {
  @Bean
  public EventHandler eventHandlerA() { return new EventHandler(depA()); }

  @Bean
  public Factory factory() {  //.. ?? }
}

My question is - I am using Spring @Configuration with @Bean annotation to create singleton beans of all these event handlers and factory. How do I inject these beans of various event handlers into the factory so that factory can return these instances based on event type.

One direct approach I can think of is to just pass all the event handlers to factory during factory instantiation (via constructor) and maintain a mapping of type to handler and just do a get on that map. Downside of this approach is that whenever I need to add new event handler to the factory, I'll have to modify the constructor or add a new setter. Is there any better approach?

Upvotes: 0

Views: 407

Answers (1)

ug_
ug_

Reputation: 11440

There are alot of ways to go about doing this but here are 2 ways.

In your question you have this:

public EventHandler eventHandlerA() { return new EventHandler(depA()); }

Now I dont know if you intended to have this but your bean will be a different instance at every injection because it creates a new one every return statement. You've essentially made your configuration into a factory. I am going to assume that this is unintended.

Method 1


I think this is the strategy I would use because you can add a boat load of handlers, you dont pollute your app context namespace and its clean and easy to follow. For this method I renamed EventHandlerFactory to EventHandlerRepository to make it more appropriate to its functionality.

When the configuration is loaded all the event handlers are added to the factory and then you can @Autowire that factory around your application then call repository.getHandler(/*Type*/).

This same design pattern could be used to make a real factory by simply renaming EventHandlerRepository to EventHandlerFactory then passing it Class<? extends EventHandler> instead of actual instances and creating a new instance on getHandler.

EventType:

public enum EventType {
    A, B;
}

EventHandlerRepository:

public class EventHandlerRepository {
    private Map<EventType, EventHandler> handlers;

    public EventHandlerRepository() {
        handlers = new HashMap<EventType, EventHandler>();
    }

    void synchronized registerHandler(EventType type, EventHandler handler) {
        handlers.put(type, handler);
    }

    public synchronized EventHandler getHandler(EventType type) {
        return handlers.get(type);
    }
}

Configuration:

@Configuration
public class Configuration {

    private EventHandlerRepository repository = new EventHandlerRepository();

    public Config() {
        // register all your event handlers here
        factory.registerHandler(EventType.A, new EventHandlerA());
    }

    @Bean
    public EventHandlerRepository getEventHandlerRepository() {
        return repository;
    }
}

Method 2


Simply add @Component to all your event handlers and make sure they are in your component scan path.

@Component
public class EventHandlerA implements EventHandler

Upvotes: 2

Related Questions