Spring Boot启动顺序与@Value注入为null问题解析
1. Spring Boot配置文件与Bean创建的加载顺序
Spring Boot启动时application.properties会先被加载到环境变量中,之后才会开始创建标注@Bean的Bean实例。但你遇到的qrCodeAppUrl为null的问题,根源不在加载顺序,而是对象的创建方式。
2. 为什么qrCodeAppUrl为null?
你在TxnIdGeneratorProperties的seqGenerator()方法中,是手动通过new TimeBasedTxnIdGenerator()创建的实例,这个对象完全脱离了Spring容器的管理:
- Spring只会对自身容器内创建的Bean执行依赖注入(包括
@Value属性的赋值) - 你手动new的对象,Spring不会主动为它注入
qrCodeAppUrl的值,所以调用calculateLimit()时该字段是null
至于改成int类型抛出转换异常,是因为Spring扫描到TimeBasedTxnIdGenerator上的@Service注解时,会尝试创建该类的Bean,此时Spring读取配置文件中的qr.code.app.url值,试图给int类型的qrCodeAppUrl赋值,自然会抛出类型不匹配的异常——这个异常来自Spring自动创建的Bean,和你手动new的对象无关。
3. 解决方案
你有两种修复方式,任选其一即可:
方式一:让Spring管理Bean创建,用@PostConstruct执行初始化逻辑
因为TimeBasedTxnIdGenerator已经标注@Service,Spring会自动创建它的Bean。如果需要在Bean初始化时调用calculateLimit(),可以用@PostConstruct注解确保在依赖注入完成后执行:
@Service public class TimeBasedTxnIdGenerator extends AbstractSequencer implements TransactionIdGenerator { @Value("${qr.code.app.url}") private String qrCodeAppUrl; // 其他代码保持不变 @PostConstruct public void init() { calculateLimit(); // 此时qrCodeAppUrl已被Spring注入 } @Override public void calculateLimit() { System.out.println("MFS App param : " + qrCodeAppUrl); // 值不再为null long nodeId = createNodeId(); setSequencerLimit(nodeId); } }
同时删除配置类中的seqGenerator()方法,@Service已足够让Spring创建Bean。
方式二:在配置类中手动注入配置值,传递给实例
如果你一定要手动创建Bean,可以在配置类中通过@Value获取配置值,再传递给TimeBasedTxnIdGenerator:
@Slf4j @Configuration public class TxnIdGeneratorProperties { @Value("${qr.code.app.url}") private String qrCodeAppUrl; @Bean public TimeBasedTxnIdGenerator seqGenerator() { if (log.isInfoEnabled()) log.info("{} Loading CustomProperties :: getTxnIdGeneratorAccounts....", BOOT_CONFIG); TimeBasedTxnIdGenerator sequencer = new TimeBasedTxnIdGenerator(); sequencer.setQrCodeAppUrl(qrCodeAppUrl); // 需要给TimeBasedTxnIdGenerator添加对应的setter方法 sequencer.calculateLimit(); return sequencer; } }
同时给TimeBasedTxnIdGenerator添加qrCodeAppUrl的setter方法,并且可以移除该类上的@Service注解(避免Spring重复创建Bean)。
4. 额外注意点
- 不要同时用
@Service和配置类手动创建同一个类的Bean,否则Spring会生成多个实例,可能导致逻辑混乱 @PostConstruct是Spring官方推荐的Bean初始化方法,会在所有依赖注入完成后执行,适合处理初始化逻辑
内容的提问来源于stack exchange,提问作者Arun Sudhakaran

