You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Singleton服务注入Prototype Bean?构造器注入遇NPE问题

如何将Singleton服务注入Prototype Bean解决构造器NPE问题

问题根源

  1. 构造器名称错误:你的MyService类的构造器被错误命名为myManager,Spring无法识别这是该类的构造器,导致实例创建逻辑异常。
  2. Spring创建Prototype Bean的机制限制:当通过ObjectProvider.getObject(...)或applicationContext.getBean(...)传入自定义参数创建Prototype Bean时,Spring仅会使用匹配参数的构造器实例化对象,不会自动注入构造器中未显式声明的字段/依赖——这就是mySingletonDBService和mySingletonFileService为null的核心原因。

解决方案

方案1:修正构造器,合并依赖注入与自定义参数

将单例服务作为构造器参数,通过@Inject/@Autowired让Spring同时注入单例服务和接收自定义参数,同时修正构造器名称:

@Named
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyService {
    private long recId;
    private List<String> values;
  
    // 用final保证依赖不可变,同时遵循Java命名规范修正类名
    private final MySingletonDBService myDBSvc;
    private final MySingletonFileService myFileSvc;
    
    // 修正构造器名称为MyService,标注@Inject让Spring处理依赖注入
    @Inject
    public MyService(MySingletonDBService myDBSvc, MySingletonFileService myFileSvc,
                     long recName, File folder) throws DataExtractException {
        this.myDBSvc = myDBSvc;
        this.myFileSvc = myFileSvc;
        
        this.recId = myDBSvc.getRecId(recName);
        this.values = myFileSvc.getValues(recId);
    }
 
    public void processMyValues() {
        // 业务逻辑
    }
}

调用时只需传入自定义参数,Spring会自动注入单例服务:

public class MyManager {
    @Inject
    private ObjectProvider<MyService> mySvcProvider;

    public void processMyData(){
         // 注意路径转义,Java中需用\\
         MyService mySvc = mySvcProvider.getObject("myName", new File("c:\\myfolder"));
         mySvc.processMyValues();
    }
}

若参数类型易混淆,可给自定义参数添加@Named注解指定名称,调用时传入参数名映射避免顺序问题。

方案2:使用@PostConstruct延迟初始化逻辑

将自定义参数通过构造器接收,依赖通过字段注入,然后把需要使用依赖的初始化逻辑放到@PostConstruct方法中(该方法在Spring完成依赖注入后执行):

@Named
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyService {
    private long recId;
    private List<String> values;
    private long recName;
    private File folder;
  
    @Inject
    private MySingletonDBService myDBSvc;
    @Inject
    private MySingletonFileService myFileSvc;
    
    // 仅接收自定义参数的构造器,无需标注@Inject
    public MyService(long recName, File folder) {
        this.recName = recName;
        this.folder = folder;
    }

    @PostConstruct
    public void init() throws DataExtractException {
        // 此时依赖已完成注入,不会出现NPE
        this.recId = myDBSvc.getRecId(recName);
        this.values = myFileSvc.getValues(recId);
    }
 
    public void processMyValues() {
        // 业务逻辑
    }
}

调用方式与方案1一致,无需修改MyManager代码。

方案3:使用@Autowired标注初始化方法

保留无参构造器,用@Autowired标注一个初始化方法,同时接收依赖和自定义参数:

@Named
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyService {
    private long recId;
    private List<String> values;
  
    private MySingletonDBService myDBSvc;
    private MySingletonFileService myFileSvc;
    
    // 无参构造器(Spring会自动生成,可省略)
    public MyService() {}

    @Autowired
    public void init(MySingletonDBService myDBSvc, MySingletonFileService myFileSvc,
                     long recName, File folder) throws DataExtractException {
        this.myDBSvc = myDBSvc;
        this.myFileSvc = myFileSvc;
        
        this.recId = myDBSvc.getRecId(recName);
        this.values = myFileSvc.getValues(recId);
    }
 
    public void processMyValues() {
        // 业务逻辑
    }
}

调用方式同样无需修改MyManager代码,Spring会自动匹配并执行该初始化方法。

额外注意事项

  • 遵循Java命名规范:类名首字母大写(如MySingletonDBService而非mySingletonDBService),避免组件扫描或注入时出现异常。
  • 路径转义:Java字符串中的路径分隔符需用\\,否则会被识别为转义字符。

内容的提问来源于stack exchange,提问作者Nila

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 14:43:09