Может ли spring @Автомобильная карта?

Здесь карта

@Autowired
private Map<String, ISendableConverter> converters;

и ISendableConverter

public interface ISendableConverter {

    ISendableMsg convert(BaseMessage baseMessage);

    String getType();
}

Есть несколько классов, которые реализуют ISendableConverter

Я хочу добавить их в converters переменных, используя аннотацию @Autowried.

Экземпляр класса в качестве значения и результат метода getType() качестве ключа.

как этот

@Component
public class SendableVoiceMsgConverter implements ISendableConverter {

    @Override
    public ISendableMsg convert(BaseMessage baseMessage) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getType() {
        return "VOICE";
    }
}

Это возможно? и как?

Ответы

Ответ 1

Попробуйте что-то вроде @Resource - я не проверял этот код.

@Resource(name="converters")
private Map<String, ISendableConverter> converters;

или же

@Value("#{converters}")
private Map<String, ISendableConverter> converters;

Из весенних документов

(..) bean-компоненты, которые сами определены как тип коллекции или типа карты, не могут быть внедрены через @Autowired, потому что сопоставление типов не применимо к ним должным образом. Используйте @Resource для таких bean-компонентов, ссылаясь на конкретную коллекцию или bean-компонент карты с уникальным именем.

Это должно работать, только если вы подготовите bean-компонент converters следующим образом:

<util:map id="converters" scope="prototype" map-class="java.util.HashMap" 
          key-type="java.lang.String" value-type="...ISendableConverter">
    <entry key="VOICE" value="sendableVoiceMsgConverter" />
</util:map>

Это тоже похожий вопрос:

Ответ 2

вы можете сделать что-то вроде этого:

@SuppressWarnings("unused")
private List<OneTypeImageLoader> imageLoaders;
private Map<String, OneTypeImageLoader> imageLoaderMap=new HashMap<>();

@Autowired
public void setImageLoaders(List<OneTypeImageLoader> imageLoaders) {
    this.imageLoaders = imageLoaders;
    imageLoaders.forEach(l-> {
        imageLoaderMap.put(l.getType(), l);
    });
}

Ответ 3

Вы можете создать автоматически инициализированную карту с выбранными вами ключами с помощью конфигурации Spring Java:

В классе, аннотированном аннотацией @Configuration:

@Autowired
private List<ISendableConverter> sendableConverters;

@Bean
public Map<String, ISendableConverter> sendableConvertersMap() {
    Map<String, ISendableConverter> sendableConvertersMap = new HashMap<>();
    for (ISendableConverter converter : sendableConverters) {
        sendableConvertersMap.put(converter.getType(), converter);
    }
    return sendableConvertersMap;
}

Чем вы вводите эту карту:

@Resource()
private Map<String, ISendableConverter> sendableConverters;

Вы можете дополнительно добавить некоторую селекторную строку в свою аннотацию @Resource, если вы определили больше карт того же типа.

Таким образом, все, что вам нужно сделать, это реализовать ISendableConverter с помощью Spring bean, и он автоматически появится на карте, указанной выше. Вам не нужно создавать элементы карты вручную для каждой реализации.

Ответ 4

Попробуйте что-то вроде этого, оно работает для меня

private Map<String, ISendableConverter> converters;

@Autowired
public void setUploadServices(Set<ISendableConverter> converters){
    this.conveters = converters.stream()
        .collect(Collectors.toMap(ISendableConverter::getType, Function.identity()));
}

Тот же результат может быть достигнут с использованием впрыска конструктора:

private Map<String, ISendableConverter> converters;

@Autowired
public Foo(Set<ISendableConverter> converters) {
    this.conveters = converters.stream()
        .collect(Collectors.toMap(ISendableConverter::getType, Function.identity()));
}

Ответ 5

@Component("VOICE")
public class SendableVoiceMsgConverter implements ISendableConverter {

    @Override
    public ISendableMsg convert(BaseMessage baseMessage) {
        // TODO Auto-generated method stub
        return null;
    }
}

Вы можете просто добавить имя типа непосредственно в аннотацию компонента, которая выполнит эту работу.

Ответ 6

Вы можете легко подключать бины с тем же интерфейсом, что и карта. Ключ - это имя бина. Итак, если вы называете компоненты сами (то есть со статическим конечным значением, которое вы также можете использовать в методе), то это уже должно сработать.

Ответ 7

Вы можете сделать его более общим и создать что-то вроде этого:

    public interface BasicStrategy {
        String getKey();
    }

    public final class StrategyMap<K extends BasicStrategy> extends HashMap<String, K> {

    public StrategyMap(List<K> strategies) {
        super(strategies.stream().collect(Collectors.toMap(K::getKey, Function.identity())));
    }

    @Override
    public K get(Object key) {
        BasicStrategy basicStrategy = super.get(key);
        if (basicStrategy == null) {
            throw new RuntimeException("No strategy found for key: '" + key + "'");
        }
        return (K) basicStrategy;
    }
}

Теперь вы можете использовать этот StrategyMap везде в вашем коде, например так:

private StrategyMap<ISendableConverter> converters;

@Autowired
public Foo(List<ISendableConverter> converters) {
    this.conveters = new StrategyMap<>(converters);
}

Этот подход генерирует создание StrategyMap, а также можно централизовать логику для случая, когда значение не найдено.

PS: Конечно, ISendableConverter должен расширить интерфейс BasicStrategy.