Может ли 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
.