如何在运行时动态实例化含@Autowired依赖的Spring Bean
刚好遇到过类似的场景,针对你需要在运行时动态创建带@Autowired依赖的原型Bean的需求,Spring有几种原生的解决方案,结合你的代码示例给你详细说明:
核心问题解析
你不能直接用new ClientA()或new ClientB()创建实例,是因为手动new出来的对象不在Spring容器的管理范围内,Spring不会自动为它注入@Autowired标注的MyHelperService,导致依赖为null。而我们需要让Spring来负责Bean的创建,才能保证依赖注入完成,同时满足运行时动态确定类型、每个任务使用独立实例的要求。
方案1:注入ApplicationContext动态获取原型Bean
Spring的ApplicationContext自带Bean实例化能力,对于prototype作用域的Bean,每次调用getBean()都会返回全新的实例,并且自动完成内部依赖注入。
修改你的ClientFactory代码如下:
@Component public class ClientFactory { @Autowired private ApplicationContext applicationContext; public Client createClient(String type) { // 根据传入的type动态匹配对应的Client实现类 if ("A".equals(type)) { return applicationContext.getBean(ClientA.class); } else if ("B".equals(type)) { return applicationContext.getBean(ClientB.class); } // 可根据业务需求添加默认实现或抛出异常 throw new IllegalArgumentException("不支持的Client类型: " + type); } }
方案2:使用BeanFactory(更轻量的替代方案)
如果不想依赖功能更全但也更重的ApplicationContext,可以直接注入BeanFactory(它是ApplicationContext的父接口,仅提供Bean管理的核心能力),用法和上面完全一致:
@Component public class ClientFactory { @Autowired private BeanFactory beanFactory; public Client createClient(String type) { if ("A".equals(type)) { return beanFactory.getBean(ClientA.class); } else if ("B".equals(type)) { return beanFactory.getBean(ClientB.class); } throw new IllegalArgumentException("不支持的Client类型: " + type); } }
方案3:结合@Lookup注解(适合枚举型动态场景)
如果你的Client类型是可提前枚举的固定集合,还可以用Spring的@Lookup注解来定义原型Bean的获取方法,这种方式更符合Spring的依赖注入风格:
@Component public abstract class ClientFactory { // Spring会自动实现这个抽象方法,返回全新的ClientA原型实例 @Lookup protected abstract ClientA getClientA(); @Lookup protected abstract ClientB getClientB(); public Client createClient(String type) { if ("A".equals(type)) { return getClientA(); } else if ("B".equals(type)) { return getClientB(); } throw new IllegalArgumentException("不支持的Client类型: " + type); } }
注意:这里ClientFactory必须是抽象类,因为@Lookup标注的方法需要Spring动态生成子类来实现实例逻辑。
适配你的多线程场景
你的实际业务是通过ExecutorService提交多线程任务,每个任务需要独立的Client实例,以上方案都能完美适配:
- 每次从工厂获取的都是全新的
prototype实例,任务执行完毕后实例生命周期结束,不会和其他任务的实例产生资源冲突; MyHelperService作为单例Bean,只要它本身是线程安全的(这是Spring单例Bean的基本要求),就可以在多线程下安全复用。
修改后的MyService示例:
@Service public class MyService { @Autowired private ClientFactory factory; @Autowired private ExecutorService executorService; public void processList(List<String> requests) { List<Callable<Void>> tasks = requests.stream() .map(request -> (Callable<Void>) () -> { Client client = factory.createClient(request); client.process(request); return null; }) .collect(Collectors.toList()); try { executorService.invokeAll(tasks); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // 根据业务需求处理中断异常 } } }
内容的提问来源于stack exchange,提问作者Michael C

