Spring Boot自定义@ConfigValue注解注入失效问题求助
问题原因与解决方案
嘿,这个问题我之前踩过类似的坑,核心原因是Spring Bean的初始化顺序在搞鬼!
为什么MysqlConf的password无法注入?
你的MysqlConf是一个@Configuration配置类,Spring在启动时会优先处理配置类:
- 首先实例化
MysqlConf - 紧接着调用里面的
druidDataSource()方法创建数据源Bean - 而你的
BeanPropertiesUtil是普通@Component,它的afterPropertiesSet方法要等自身初始化完成后才执行,这时候druidDataSource已经创建好了——你这时候给MysqlConf的password字段赋值,根本赶不上数据源的初始化,所以数据源里的password还是空的。
而MyAnnotationService是@Service,它的初始化时机比BeanPropertiesUtil的afterPropertiesSet晚,所以工具类处理它的时候,字段还没被用到,赋值后自然能正常获取。
解决方案:改用BeanPostProcessor处理自定义注解
BeanPostProcessor是Spring的核心扩展点,它能在每个Bean实例化完成后、初始化方法执行前对Bean做处理,完美覆盖配置类的场景。
修改BeanPropertiesUtil类
把原来的InitializingBean和BeanFactoryAware换成BeanPostProcessor:
@Component public class BeanPropertiesUtil implements BeanPostProcessor { private static final Logger logger = LoggerFactory.getLogger(BeanPropertiesUtil.class); public static Map<String,Object> configMap = new HashMap<>(); @PostConstruct public void init() throws Exception { configMap.put("spring.datasource.password","123321"); configMap.put("orderNo","2018051929991"); } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 在Bean初始化方法执行前注入字段 Field[] declaredFields = bean.getClass().getDeclaredFields(); for(Field field : declaredFields) { ConfigValue configValue = field.getAnnotation(ConfigValue.class); if(configValue != null) { field.setAccessible(true); try { Object value = configMap.get(configValue.name()); logger.info("field name :"+field.getName() +" field value:"+value); field.set(bean, value); } catch (IllegalAccessException e) { logger.error("注入字段失败", e); } } } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 无需额外处理,直接返回Bean return bean; } }
为什么这个方案有效?
postProcessBeforeInitialization会在所有Bean的初始化逻辑(包括@Configuration类的@Bean方法执行)之前被调用- 当Spring处理
MysqlConf时,会先调用这个方法给password字段赋值,之后才会执行druidDataSource()创建数据源,这时候password已经有值了,数据源就能正确初始化。
额外优化建议
如果你的配置值不是硬编码,而是来自配置文件,建议通过Environment来读取,更符合Spring规范:
@Autowired private Environment environment; @PostConstruct public void init() throws Exception { configMap.put("spring.datasource.password", environment.getProperty("spring.datasource.password")); // 其他配置项同理 }
内容的提问来源于stack exchange,提问作者Mr Cai
相关产品推荐
相关产品推荐

