【问题标题】:Can spring @Autowired Map?可以弹出@Autowired Map吗?
【发布时间】:2014-01-22 00:26:55
【问题描述】:

这是地图

@Autowired
private Map<String, ISendableConverter> converters;

ISendableConverter

public interface ISendableConverter {

    ISendableMsg convert(BaseMessage baseMessage);

    String getType();
}

有些类实现了ISendableConverter

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

类的实例作为值,方法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";
    }
}

这可能吗?以及如何?

【问题讨论】:

  • 你真的试过这个吗?它应该在 Spring 2.5 及更高版本中工作。键将是 bean 名称。
  • 不,您不能将它们注入到键是对 bean 的方法调用的返回值的 Map 中。

标签: java spring autowired


【解决方案1】:

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

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

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

来自Spring Docs

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

只有当你像这样准备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>

这也是类似的问题:

【讨论】:

  • 但这并不是试图注入已注册为 bean 的地图。 Autowired 确实支持注入问题所在的所有实现。请参阅文档:docs.spring.io/spring/docs/2.5.x/reference/…
  • 我有一个情况。我不想从 xml bean 中执行此操作。我想通过注释来做到这一点。同样,我不能做任何硬编码。有什么建议吗?
  • 将它们自动连接为列表,然后循环遍历该列表以创建地图。如果您可以将逻辑定义为 @Bean 方法,那么您可以将地图作为 bean 返回。
【解决方案2】:

您可以使用 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 注释中。

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

【讨论】:

    【解决方案3】:

    你可以这样做:

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

    【讨论】:

      【解决方案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】:

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

            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 接口。

        【讨论】:

          【解决方案7】:

          首先,将Bean名称设置为getType()的返回值

          @Component("VOICE")
          public class SendableVoiceMsgConverter implements ISendableConverter {
          
              @Override
              public ISendableMsg convert(BaseMessage baseMessage) {
                  // TODO Auto-generated method stub
                  return null;
              }
          }
          

          你可以这样使用:

          @Autowired
          private Map<String, ISendableConverter> converters;
          

          这个map的V类型是Bean的实例,K类型是Bean的名字

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-02-11
            • 1970-01-01
            • 2015-09-25
            • 2011-03-02
            • 2017-08-16
            • 2020-04-08
            • 1970-01-01
            • 2015-06-15
            相关资源
            最近更新 更多