Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring: how to get bean from application context which implements generic interface?

I have interface:

public interface CommandHandler<T extends Command> {
    void handle(T command);
}

There are commands which implement Command marker interface

public class CreateCategoryCommand implements Command {
}

public class CreateCategoryCommand implements Command {
}

For each command I have apropriate CommandHandler implementations:

@Component
public class CreateProductCommandHandler implements CommandHandler<CreateProductCommand> {
    @Override
    public void handle(CreateProductCommand command) {
        System.out.println("Command handled");
    }
}

@Component
public class CreateCategoryCommandHandler implements CommandHandler<CreateCategoryCommand> {

    @Override
    public void handle(CreateCategoryCommand command) {

    }
}

Question: I have command bus

@Component
public class SimpleCommandBus implements CommandBus {

    @Autowired
    private ApplicationContext context;

    @Override
    public void send(Command command) {
        // OF COURSE, THIS NOT COMPILED, BUT I NEED SOMETHING LIKE THIS
        CommandHandler commandHandler = context.getBean(CommandHandler<command.getClass()>)
    }
}

How to get bean from application context which implements generic interface with particular type?

like image 379
Teimuraz Avatar asked Oct 05 '16 18:10

Teimuraz


People also ask

Which is the correct way to obtain a bean from Spring context?

getBean() method. Simply put, as the name of the method also suggests, this is responsible for retrieving a bean instance from the Spring container.

Which interface does Spring bean implement?

The Spring container recognizes that LocalSessionFactoryBean implements the FactoryBean interface, and thus treats this bean specially: An instance of LocalSessionFactoryBean is instantiated, but instead of being directly returned, instead the getObject() method is invoked.

How do you list all beans loaded in the application context?

You can call applicationContext. getBeanDefinitionNames() method to list all beans loaded into ApplicationContext.


1 Answers

Way I solved it:

@Component
public class SimpleCommandBus {

    private final Logger logger;
    private final Set<CommandHandler<?>> handlers;
    private final Map<Class<?>, CommandHandler<?>> commandHandlersCache = new WeakHashMap<>();

    public SimpleCommandBus(Logger logger, Set<CommandHandler<?>> handlers) {
        this.logger = logger;
        this.handlers = handlers;
    }

    public void send(Command command) {
        CommandHandler<Command> commandHandler = getCommandHandler(command);

        if (commandHandler != null)
            commandHandler.handle(command);
        else
            logger.error("Can't handle command " + command);

    }

    @SuppressWarnings("unchecked")
    private <C extends Command> CommandHandler<C> getCommandHandler(C command) {
        Class<?> commandType = command.getClass();

        if (commandHandlersCache.containsKey(commandType))
            return (CommandHandler<C>) commandHandlersCache.get(commandType);

        for (CommandHandler<?> haandler : handlers) {
            Class<?> supportedCommandType = resolveTypeArgument(haandler.getClass(), CommandHandler.class);

            if (commandType.isAssignableFrom(supportedCommandType)) {
                commandHandlersCache.put(commandType, haandler);
                return (CommandHandler<C>) haandler;
            }
        }

        commandHandlersCache.put(commandType, null);
        return null;
    }


}
like image 186
Radu Avatar answered Oct 11 '22 05:10

Radu