【问题标题】:Do I need to synchronize @Autowired method in Spring when I want to save multiple beans in collection?当我想在集合中保存多个 bean 时,是否需要在 Spring 中同步 @Autowired 方法?
【发布时间】:2020-04-10 06:45:02
【问题描述】:

假设我有基于 Spring 框架的项目中的下一个类:

interface I {
    String getName()
}

@Component
class I1 implements I {
    @Override
    String getName() {return "I1"}
}

@Component
class I2 implements I {
    @Override
    String getName() {return "I1"}
}

我想使用@Autowired 方法将它们全部收集到地图中:

@Component
public class A {
    private Map<I> map = new HashMap<>()

    @Autowired
    public registerI(I i) {
        map.put(i.getName(), i)
    }
}

我应该让这个方法registerI同步吗?我的意思是,Spring 可以同时在多个线程中调用此方法吗?或者这个方法会被顺序调用?

谢谢

【问题讨论】:

  • 你可以试试看。为什么会被多次调用?
  • Map 应该是List 对吧?
  • @Deadpool 在这种情况下没关系。我想知道 - 我是否需要在这里使用同步集合。
  • @f1sh 在这种情况下我没有重现任何竞争条件错误,但我仍然不确定它是否是正确的代码。
  • 为什么不在方法中使用 List 作为参数?这将确保只有一个方法调用。

标签: java spring spring-boot dependency-injection


【解决方案1】:

您不必使用synchronized,因为Spring bean 初始化is single-threaded and thread-safe。您可以将陷阱想象为线程范围或惰性 bean,但对于常规的单例 bean,初始化发生在一个线程中。

您可能希望使用synchronized 来确保在调用registerI() 方法后您的对象是safely published,尽管带有final 字段的自动连接构造函数更具可读性。

@Component
public class A {
    private final Map<String, I> map;

    public A(List<I> list) {
        map = list.stream().collect(Collectors.toMap(I::getName, i -> i));
    }

}

【讨论】:

【解决方案2】:

在应用启动期间你会得到一个异常,因为 Spring 无法确定你想要注入的接口“I”的正确实现。你应该使用@Qualifier。

如果你想完成那个场景,这就足够了。

@Component
    public static class A {
        private Map<String,I> map = new HashMap<>();

        public A(List<I> list) {
            //map = list.stream().collect(Collectors.toMap(I::getName, x -> x));
            for (I i : list) {
                map.put(i.getName(), i);
            }
        }
    }

您将在地图中仅以一个值结束。

如果没有重复的地图键,则注释行有效。

【讨论】:

    【解决方案3】:

    您可以通过@PostConstruct 方法自动装配上下文并从中获取所有感兴趣的bean,并使用它创建一个哈希图。

    或者

    如果您希望该地图在多个班级之间共享,请将其设为@Bean

    @Configuration
    class SomeConfig{
    
      @Autowire Context context;
      @Bean(name = "mapBean")
      public Map<String, MyCustomClassName1> mapBean() {
        Map<String, MyCustomClassName1> map = new HashMap<>();
        //populate the map here - from Context
        return map;
      }
    }
    
    【解决方案4】:

    Spring 用你的 bean 填充 List。在你可以在 postConstruct 中创建地图之后

    @Component
    public class A {
    
      @Autowired
      private List<I> list;
    
      @Autowired
      private Map<String, I> map;   
    
      @PostConstruct
      private void init(){
        map = list.stream()
                .collect(Collectors.toMap(I::getName, element->element);
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2015-11-11
      • 1970-01-01
      • 2010-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多