SpringBoot如何自动装配含非托管类构造参数的类?
首先,先明确你的核心需求:创建一个每次调用都生成新实例的Prototype类,它既要依赖Spring托管的组件(比如Repository),又需要接收外部传入的非Spring托管对象(比如WebSocket的Session)。下面一步步给你解决:
1. 修正Prototype的作用域与依赖注入方式
首先,你的Prototype类必须标记为原型作用域,否则Spring默认会创建单例,即使调用getBean也只会返回同一个实例。另外,推荐用构造注入托管的依赖(比如Repository),外部参数用Setter注入,这样既符合Spring的依赖注入规范,又能灵活传入外部对象:
@Component @Scope("prototype") // 明确标记为原型作用域,确保每次获取都是新实例 class Prototype { private final Repository repository; private Session session; // 构造注入Spring托管的Repository,Spring会自动完成这部分注入 public Prototype(Repository repository) { this.repository = repository; } // Setter方法用于手动传入外部的Session对象 public void setSession(Session session) { this.session = session; } }
2. 正确获取原型实例并传入外部参数
在PrototypeClient中,你通过ApplicationContext.getBean(Prototype.class)获取新的原型实例,然后调用Setter传入Session即可。这种方式是正确的,只要Prototype是原型作用域,每次调用都会生成新实例:
@Component class PrototypeClient { @Autowired private ApplicationContext context; public void createNewPrototype(Session session) { // 获取新的Prototype实例(原型作用域保证每次都是新的) Prototype prototype = context.getBean(Prototype.class); // 手动设置外部传入的Session prototype.setSession(session); // 现在prototype同时拥有Spring注入的Repository和外部的Session了 } }
3. 修正WebSocketController的Bean获取方式
你原来的WebSocketController中直接用静态方法获取PrototypeClient是不规范的,而且@ServerEndpoint注解的类默认不会被Spring托管,需要做两步调整:
- 给WebSocketController加上
@Component,让Spring管理它 - 配置
ServerEndpointExporter,让Spring识别WebSocket端点 - 用
@Autowired注入PrototypeClient,而不是静态获取
调整后的代码:
@Component @ServerEndpoint(value = "/resources") class WebSocketController { @Autowired private PrototypeClient client; @OnMessage void handleMessage(Session session, String message) { client.createNewPrototype(session); } } // 新增WebSocket配置类 @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
关于你之前的报错原因
你之前调用context.getBean(Prototype.class, session)报错,是因为你的Prototype构造函数只有Session参数,Spring会尝试在容器中查找Session类型的Bean,但Session是外部传入的非Spring托管对象,所以找不到而报错。用构造注入托管依赖+Setter注入外部参数的方式,就可以避免这个问题。
补充:获取原型实例的方式是否正确?
你的思路是对的——因为需要每次生成新实例,不能用@Autowired直接注入(默认会注入单例),通过ApplicationContext.getBean获取原型作用域的Bean是正确的做法,只要确保Prototype标记了@Scope("prototype")即可。
内容的提问来源于stack exchange,提问作者Vino

