Reputation: 6998
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
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.
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;
}
}
@Component
to all your event handlers and make sure they are in your component scan path.
@Component
public class EventHandlerA implements EventHandler
Upvotes: 2