如何在Spring中自动装配指定大小的MyListener集合?
哇,确实挺少见的需求,但其实Spring有几种方式能实现你要的「自动装配指定数量的原型对象列表」的效果。先帮你分析下你之前尝试的问题,再给你几个可行的方案。
为什么你的尝试会报错?
你创建了一个名为listMyListeners的ArrayList Bean,但这里有两个核心问题:
- 这个List是空的,里面没有任何
MyListener实例,Spring自然找不到符合要求的依赖; - Spring自动装配集合的逻辑是:当你注入
List<MyListener>时,它默认会去容器中查找所有类型为MyListener的Bean并收集起来,而不是去找一个List类型的Bean。你加了@Qualifier后,Spring尝试匹配那个listMyListenersBean,但它的实际内容和List<MyListener>的依赖要求不匹配,所以抛出了NoSuchBeanDefinitionException。
解决方案1:通过@Bean手动创建指定数量的原型实例
首先确保你的MyListener是原型作用域(这样每次获取都会生成新实例):
@Component @Scope("prototype") public class MyListener implements Runnable { // 你的Listener业务逻辑 }
然后在配置类中创建一个包含指定数量实例的List Bean:
@Configuration public class ListenerConfig { @Value("${workflow.threads:10}") private int threadCount; // 若MyListener已用@Component标注,这一步可省略 @Bean @Scope("prototype") public MyListener myListener() { return new MyListener(); } // 创建包含指定数量MyListener实例的List @Bean public List<MyListener> myListenerList() { List<MyListener> listeners = new ArrayList<>(threadCount); for (int i = 0; i < threadCount; i++) { // 每次调用myListener()都会生成新的原型实例 listeners.add(myListener()); } return listeners; } }
最后在你的ListenerThreadPool中注入这个List:
@Component public class ListenerThreadPool { // 其他成员变量... @Autowired private List<MyListener> myListenerList; // 你的start()等业务方法... }
解决方案2:实现ApplicationContextAware动态获取原型实例
如果不想额外写配置类,可以让ListenerThreadPool实现ApplicationContextAware,直接从Spring上下文获取原型Bean并创建指定数量的实例:
@Component public class ListenerThreadPool implements ApplicationContextAware { private static final Logger LOGGER = LoggerFactory.getLogger(ListenerThreadPool.class); private ExecutorService threadPool; @Value("${workflow.threads:10}") private int poolSize; private final AtomicBoolean isStarted = new AtomicBoolean(false); private List<MyListener> myListeners; private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } // 初始化时创建指定数量的MyListener实例 @PostConstruct public void initListeners() { myListeners = new ArrayList<>(poolSize); for (int i = 0; i < poolSize; i++) { // 每次getBean都会生成新的原型实例 myListeners.add(applicationContext.getBean(MyListener.class)); } // 顺便初始化线程池 this.threadPool = Executors.newFixedThreadPool(poolSize); } public void start() { if (isStarted.compareAndSet(false, true)) { for (MyListener listener : myListeners) { LOGGER.info("Starting listeners pool {}", listener.toString()); threadPool.submit(listener); } } else { LOGGER.warn("Cannot start listeners pool because it's already started"); } } }
同样,别忘了给MyListener加上@Scope("prototype"),否则每次getBean都会返回同一个单例实例。
额外小技巧
如果你用的是Spring Boot 2.x+,可以用ObjectProvider来替代ApplicationContextAware,代码会更简洁:
@Component public class ListenerThreadPool { // 其他成员变量... @Autowired private ObjectProvider<MyListener> myListenerProvider; @Value("${workflow.threads:10}") private int poolSize; @PostConstruct public void initListeners() { myListeners = new ArrayList<>(poolSize); for (int i = 0; i < poolSize; i++) { myListeners.add(myListenerProvider.getObject()); } } }
内容的提问来源于stack exchange,提问作者Akhtar
相关产品推荐
相关产品推荐

