Java 可以弹簧@Autowired Map吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20918825/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 05:19:29  来源:igfitidea点击:

Can spring @Autowired Map?

javaspringautowired

提问by Joe

Here's the Map

这是地图

@Autowired
private Map<String, ISendableConverter> converters;

and ISendableConverter

ISendableConverter

public interface ISendableConverter {

    ISendableMsg convert(BaseMessage baseMessage);

    String getType();
}

There are some classes that implements ISendableConverter

有一些类实现 ISendableConverter

I want to inject them into the variable convertersby using spring @Autowriedannotation.

我想converters通过使用 spring@Autowried注释将它们注入到变量中。

The instance of class as value, and the result of method getType()as key.

类的实例作为值,方法的结果getType()作为键。

like this one

像这个

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

Is this possible? and how?

这可能吗?如何?

回答by MariuszS

Try with something like @Resource- I have not tested this code.

尝试使用@Resource 之类的东西- 我还没有测试过这段代码。

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

or

或者

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

From Spring Docs

来自Spring 文档

(..) beans that are themselves defined as a collection or map type cannot be injected through @Autowired, because type matching is not properly applicable to them. Use @Resourcefor such beans, referring to the specific collection or map bean by unique name.

(..) 本身定义为集合或映射类型的 bean 不能通过 @Autowired 注入,因为类型匹配不适用于它们。对此类 bean使用@Resource,通过唯一名称引用特定集合或映射 bean。

This should work, only if you prepare convertersbean like this:

这应该有效,只有当你converters像这样准备bean 时:

<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>

This is also similar question:

这也是类似的问题:

回答by Ondrej Bozek

You can create automatically initialized map with keys of your choice using Spring Java configuration:

您可以使用 Spring Java 配置使用您选择的键创建自动初始化的映射:

In class annotated with @Configurationannotation:

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

Than you inject this map with:

比你注入这个地图:

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

You can optionally add some selector string to your @Resourceannotation if you have defined more maps of same type.

@Resource如果您定义了更多相同类型的地图,您可以选择将一些选择器字符串添加到您的注释中。

This way all you have to do is implement ISendableConverterby your spring bean and it will automatically appear in Map defined above. You don't need to create map items by hand for each implementation.

这样你所要做的就是ISendableConverter由你的 spring bean 实现,它会自动出现在上面定义的 Map 中。您无需为每个实现手动创建地图项。

回答by TimYi

you can do something like this:

你可以做这样的事情:

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

回答by Sergii Bishyr

Try something like this, it works for me

尝试这样的事情,它对我有用

private Map<String, ISendableConverter> converters;

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

The same result can be achieved using constructor injection:

使用构造函数注入可以实现相同的结果:

private Map<String, ISendableConverter> converters;

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

回答by Rigeborod

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

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

You may want to just add the type name directly into the component annotation, that will do the job.

您可能只想将类型名称直接添加到组件注释中,这样就可以了。

回答by Manuel Polacek

You can easily autowire beans of the same interface as a map. The key is the bean name. So, if you name the components itself (ie. with the static final value, you can use in the method as well), then this should already do the trick.

您可以轻松地自动装配与地图相同接口的 bean。关键是 bean 名称。因此,如果您命名组件本身(即使用静态最终值,您也可以在方法中使用),那么这应该已经可以解决问题了。

回答by Borys Rudenko

You can make it more generic and build something like this:

您可以使其更通用并构建如下内容:

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

Now you can use this StrategyMapeverywhere around your code like this:

现在您可以StrategyMap在代码周围的任何地方使用它,如下所示:

private StrategyMap<ISendableConverter> converters;

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

This approach generify the creation of a StrategyMapand also logic for the case where value is not found can be centralized.

这种方法泛化了创建,StrategyMap并且对于找不到价值的情况也可以集中逻辑。

PS: Of course ISendableConverterhas to extend BasicStrategyinterface.

PS:当然ISendableConverter要扩展BasicStrategy接口。