如何优雅创建批量Spring管理Bean?求最佳实现方案
}
}
## 方法二:采用工厂模式与构造函数注入(推荐方式) ```java @Component public class ChildLayoutFactory { private final ServiceA serviceA; private final ServiceB serviceB; public ChildLayoutFactory(ServiceA a, ServiceB b) {...} public ChildLayout create(MyDTO dto) { return new ChildLayout(a, b, dto); } } public class ChildLayout { ServiceA serviceA; ServiceB serviceB; public ChildLayout(ServiceA a, ServiceB b, MyDTO item) { this.serviceA = a; this.serviceB = b; ... } } @Component public class MainLayout { @Autowired ChildLayoutFactory childLayoutFactory; //或构造函数注入 public void init() { items.forEach(i -> this.add(childLayoutFactory.create(i))); } }
两种方法均可正常工作,但第二种方法需要额外编写工厂类,且当新增动态依赖时需向上传递构造参数。请问是否有方法可以结合两者的优势?当前的实现方式是否推荐?
回答
现有方法评价
- 方法一:实现简单,但硬依赖
ApplicationContext,耦合度高,单元测试成本高,且IDE会出现不必要的提示,开发体验不佳。 - 方法二:符合依赖注入原则,可测试性强,是Spring官方认可的方式,但需要额外维护工厂类,依赖变更时需同步修改工厂的构造和创建方法,冗余度随依赖增加而上升。
结合优势的Spring原生方案
方案1:使用ObjectProvider替代ApplicationContext
ObjectProvider是Spring提供的轻量级依赖查找工具,既避免了直接耦合上下文,又能便捷传递构造参数创建原型Bean:
@Component public class MainLayout { private final ObjectProvider<ChildLayout> childLayoutProvider; // 构造注入,符合Spring最佳实践 public MainLayout(ObjectProvider<ChildLayout> childLayoutProvider) { this.childLayoutProvider = childLayoutProvider; } public void init() { items.forEach(item -> { // 传递动态参数创建原型实例 ChildLayout childLayout = childLayoutProvider.getObject(item); this.add(childLayout); }); } } @Component @Scope("prototype") public class ChildLayout { private final ServiceA serviceA; private final ServiceB serviceB; private final MyDTO item; // 全构造函数注入,字段设为final保证不可变性 public ChildLayout(ServiceA serviceA, ServiceB serviceB, MyDTO item) { this.serviceA = serviceA; this.serviceB = serviceB; this.item = item; // 初始化逻辑 } }
优势:
- 无需编写工厂类,减少冗余代码
- 不依赖
ApplicationContext,耦合度低,单元测试只需mockObjectProvider ChildLayout采用全构造注入,符合Spring规范,安全性高- 动态参数传递简洁自然
方案2:使用@Lookup注解实现方法级Bean创建
如果MainLayout是原型Bean,或希望创建逻辑更内聚,可使用@Lookup:
@Component public class MainLayout { public void init() { items.forEach(item -> { ChildLayout childLayout = createChildLayout(item); this.add(childLayout); }); } // Spring会自动实现此方法,返回原型Bean实例 @Lookup protected ChildLayout createChildLayout(MyDTO item) { // 无需编写实现逻辑 return null; } } @Component @Scope("prototype") public class ChildLayout { private final ServiceA serviceA; private final ServiceB serviceB; private final MyDTO item; public ChildLayout(ServiceA serviceA, ServiceB serviceB, MyDTO item) { this.serviceA = serviceA; this.serviceB = serviceB; this.item = item; } }
优势:
- 完全隐藏Bean查找细节,代码更简洁
ChildLayout保持构造注入的优雅- 无需额外工具类或工厂类
总结推荐
- 优先选择**
ObjectProvider方案**:兼顾简洁性与低耦合,是Spring官方推荐的替代直接使用ApplicationContext的方式,完美解决两种原有方法的痛点。 - 若
MainLayout为原型Bean或需内聚创建逻辑,可选用**@Lookup方案**。 - 原方法二(工厂模式)仍适用于需要复杂定制创建逻辑的场景(如添加校验、日志等),此时工厂类可集中处理这些额外逻辑。
内容的提问来源于stack exchange,提问作者Alex Roig
相关产品推荐
相关产品推荐

