Voici la carte
@Autowired
private Map<String, ISendableConverter> converters;
et ISendableConverter
public interface ISendableConverter {
ISendableMsg convert(BaseMessage baseMessage);
String getType();
}
Il existe des classes qui implémentent ISendableConverter
Je veux les injecter dans la variable converters
en utilisant l'annotation spring @Autowried
.
L'instance de classe comme valeur et le résultat de la méthode getType()
comme clé.
comme celui-ci
@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";
}
}
Est-ce possible? et comment?
Essayez avec quelque chose comme @ Resource - Je n'ai pas testé ce code.
@Resource(name="converters")
private Map<String, ISendableConverter> converters;
ou
@Value("#{converters}")
private Map<String, ISendableConverter> converters;
De Spring Docs
(..) les beans qui sont eux-mêmes définis comme un type de collection ou de carte ne peuvent pas être injectés via @Autowired, car la correspondance de type ne leur est pas correctement applicable. Utilisez @ Resource pour ces beans, en faisant référence à la collection spécifique ou au bean de carte par nom unique.
Cela devrait fonctionner, seulement si vous préparez un bean converters
comme ceci:
<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>
C'est aussi une question similaire:
vous pouvez faire quelque chose comme ça:
@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);
});
}
Vous pouvez créer une carte automatiquement initialisée avec les clés de votre choix en utilisant Spring Java:
En classe annoté avec @Configuration
annotation:
@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;
}
Que vous injectez cette carte avec:
@Resource()
private Map<String, ISendableConverter> sendableConverters;
Vous pouvez éventuellement ajouter une chaîne de sélection à votre @Resource
annotation si vous avez défini plusieurs cartes du même type.
De cette façon, tout ce que vous avez à faire est d'implémenter ISendableConverter
par votre bean Spring et il apparaîtra automatiquement dans la carte définie ci-dessus. Vous n'avez pas besoin de créer des éléments de carte à la main pour chaque implémentation.
Essayez quelque chose comme ça, ça marche pour moi
private Map<String, ISendableConverter> converters;
@Autowired
public void setUploadServices(Set<ISendableConverter> converters){
this.conveters = converters.stream()
.collect(Collectors.toMap(ISendableConverter::getType, Function.identity()));
}
Le même résultat peut être obtenu en utilisant l'injection de constructeur:
private Map<String, ISendableConverter> converters;
@Autowired
public Foo(Set<ISendableConverter> converters) {
this.conveters = converters.stream()
.collect(Collectors.toMap(ISendableConverter::getType, Function.identity()));
}
Vous pouvez facilement câbler automatiquement les beans de la même interface qu'une carte. La clé est le nom du bean. Donc, si vous nommez les composants eux-mêmes (c'est-à-dire avec la valeur finale statique, vous pouvez également l'utiliser dans la méthode), cela devrait déjà faire l'affaire.
@Component("VOICE")
public class SendableVoiceMsgConverter implements ISendableConverter {
@Override
public ISendableMsg convert(BaseMessage baseMessage) {
// TODO Auto-generated method stub
return null;
}
}
Vous souhaiterez peut-être simplement ajouter le nom du type directement dans l'annotation du composant, qui fera le travail.
Vous pouvez le rendre plus générique et créer quelque chose comme ceci:
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;
}
}
Vous pouvez maintenant utiliser ce StrategyMap
partout dans votre code comme ceci:
private StrategyMap<ISendableConverter> converters;
@Autowired
public Foo(List<ISendableConverter> converters) {
this.conveters = new StrategyMap<>(converters);
}
Cette approche génère la création d'un StrategyMap
et aussi la logique pour le cas où la valeur n'est pas trouvée peut être centralisée.
PS: Bien sûr, ISendableConverter
doit étendre l'interface BasicStrategy
.