Spring: как получить компонент из контекста приложения, который реализует универсальный интерфейс?

#java #spring #generics #dependency-injection

#java #весна #дженерики #внедрение зависимостей

Вопрос:

У меня есть интерфейс:

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

Существуют команды, которые реализуют интерфейс маркера команд

 public class CreateCategoryCommand implements Command {
}

public class CreateCategoryCommand implements Command {
}
  

Для каждой команды у меня есть соответствующие реализации CommandHandler:

 @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) {

    }
}
  

Вопрос:
У меня есть командная шина

 @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()>)
    }
}
  

Как получить компонент из контекста приложения, который реализует универсальный интерфейс с определенным типом?

Комментарии:

1. @Autowired private CommandHandler<CreateProductCommand> createProductCommandHandler; Не сработало для вас? или автоматически подключенный закрытый список <CommandHandler<?>> CommandHandlers; чтобы получить их все?

2. Мне нужно динамически получать класс CreateProductCommand. Я не хочу ставить @Autowired private CommandHandler<COMMAND_TYPE> для каждого типа команды в классе SimpleCommandBus.

3. Вы заметили мой обновленный комментарий о том, как вы можете автоматически подключать их все в виде списка @Autowired List<CommandHandler<?>> CommandHandlers; Вы также можете выполнить ApplicationContext.getBeansOfType(CommandHandler.class >

4. Да, спасибо. Хорошо, у меня есть все обработчики команд. Как я могу получить конкретный обработчик команд для данной команды (например, дочерняя команда) из этого списка? Например, как я могу получить CommandHandler для CreateProductCommand?

5. для конкретной реализации ApplicationContext.getBean(«createProductCommandHandler «);

Ответ №1:

Как я это решил:

 @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;
    }


}