“@Value 注入失败,Java理论知识思维导图

@Value(“${my.value}”)

private String myValue;

@Bean

public MyBeanFactoryPostProcessor myBeanFactoryPostProcessor() {

return new MyBeanFactoryPostProcessor();

}

public static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

}

}

}

输出结果:

Config(myValue=null)

这就是我项目上遇到的问题,在配置类中再生成一个BeanFactoryPostProcessor后,@Value就注入不成功了

但只要把这个方法写成static就可以了,如下:

@Configuration

@Data

public class Config {

@Value(“${my.value}”)

private String myValue;

@Bean

public static MyBeanFactoryPostProcessor myBeanFactoryPostProcessor() {

return new MyBeanFactoryPostProcessor();

}

public static class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

}

}

}

输出结果:

Config(myValue=hello)

看看为什么没有注入成功

@Value是由AutowiredAnnotationBeanPostProcessor.postProcessProperties()处理的,所以我们就以这里为入口进行调试。

我们先把static去掉:

发现没有执行到上述方法,那我们再把static加上,看一下成功的情况:

可以看到,是可以到这个方法的,而且知道这个方法是被AbstractAutowireCapableBeanFactory.populateBean()调用的,我们再看一下这里的情况:

从上图可以看出,getBeanPostProcessorCache().instantiationAware是有AutowiredAnnotationBeanPostProcessor这个实例的

那我们再来看一下不加static这里的情况:

果然,没有注入成功的原因是在创建config实例的时候,还没有创建AutowiredAnnotationBeanPostProcessor实例

我们来看一下这个getBeanPostProcessorCache().instantiationAware是什么东西,又是如何生成的

发现只有在AbstractBeanFactory.getBeanPostProcessorCache()这个方法会将InstantiationAwareBeanPostProcessor添加到instantiationAware,如下:

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {

BeanPostProcessorCache getBeanPostProcessorCache() {

BeanPostProcessorCache bpCache = this.beanPostProcessorCache;

if (bpCache == null) {

bpCache = new BeanPostProcessorCache();

for (BeanPostProcessor bp : this.beanPostProcessors) {

if (bp instanceof InstantiationAwareBeanPostProcessor) {

bpCache.instantiationAware.add((InstantiationAwareBeanPostProcessor) bp);

if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {

bpCache.smartInstantiationAware.add((SmartInstantiationAwareBeanPostProcessor) bp);

}

}

if (bp instanceof DestructionAwareBeanPostProcessor) {

bpCache.destructionAware.add((DestructionAwareBeanPostProcessor) bp);

}

if (bp instanceof MergedBeanDefinitionPostProcessor) {

bpCache.mergedDefinition.add((MergedBeanDefinitionPostProcessor) bp);

}

}

this.beanPostProcessorCache = bpCache;

}

return bpCache;

}

}

从上面的代码看出,本质还是从this.beanPostProcessors获取的,我们来看一下什么时候会把AutowiredAnnotationBeanPostProcessor添加到容器中,如下:

从上图可知:AutowiredAnnotationBeanPostProcessor是在refresh()方法中的registerBeanPostProcessors()方法注入的

我们再来看一下加static方法的config类是什么时候加载的:

再来看一下不加static方法的config类是什么时候加载的

我们来总结一下提到的方法在refresh()方法中的顺序:

invokeBeanFactoryPostProcessors(); ——> 不加static的时候,在这一步加载config类

registerBeanPostProcessors(); ——> 注册AutowiredAnnotationBeanPostProcessor

finishBeanFactoryInitialization(); 加static的时候,在这一步加载config类

所以我们就知道原因了:当不加static字段时候,加载config类的时候,我们的AutowiredAnnotationBeanPostProcessor还没有注册,所以就会不成功,而当加上static后,我们加载config类的时候,我们的AutowiredAnnotationBeanPostProcessor已经注册好了。

为什么加static和不加static的加载顺序是不一样的呢

spring容器会在invokeBeanFactoryPostProcessors()这一步会加载所有的BeanFactoryPostProcessor,如果用static修饰的话,则不会加载config类,反之会加载。原因如下:

上图已经给出了原因,如果生成bean的工厂方法是static方法就不会加载,反之会加载。

我们不加static,能不能也让它注入成功呢?

那无非就是在加载config类之前,把AutowiredAnnotationBeanPostProcessor提前加载到容器就可以了,那我们来看一下源码是怎么加载这个实例的:

我们同样可以依葫芦画瓢,看看在哪里提前加载比较合适,发现postProcessBeanFactory()这个方法比较合适,于是改成:

public class Main {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class) {

@SneakyThrows

@Override

public void initPropertySources() {

YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

List<PropertySource<?>> propertySources = loader.load(“my-properties”,

new FileSystemResource(“/Users/tiger/spring-boot-study/src/main/resources/application.yml”));

getEnvironment().getPropertySources().addLast(propertySources.get(0));

}

@Override

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {

String ppName = “org.springframework.context.annotation.internalAutowiredAnnotationProcessor”;

beanFactory.addBeanPostProcessor(getBean(ppName, BeanPostProcessor.class));

}

};

Config config = context.getBean(Config.class);

System.out.println(config);

}

}

输出:

Config(myValue=${my.value})

从结果来看,还是没注入成功啊,经过一番调试,发现是在下面步骤中出了问题:

我们来看一下加载成功的情况:

embeddedValueResolver是在下面步骤中被添加进去的:

可以看出是在refresh()中的finishBeanFactoryInitialization()这个方法里面添加进去的,所以我们也要提前搞一下:

public class Main {

public static void main(String[] args) {

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class) {

@SneakyThrows

@Override

public void initPropertySources() {

YamlPropertySourceLoader loader = new YamlPropertySourceLoader();

List<PropertySource<?>> propertySources = loader.load(“my-properties”,

new FileSystemResource(“/Users/tiger/spring-boot-study/src/main/resources/application.yml”));

getEnvironment().getPropertySources().addLast(propertySources.get(0));

}

@Override

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {

String ppName = “org.springframework.context.annotation.internalAutowiredAnnotationProcessor”;

beanFactory.addBeanPostProcessor(getBean(ppName, BeanPostProcessor.class));

beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));

}

};

Config config = context.getBean(Config.class);

System.out.println(config);

}

}

输出:

Config(myValue=hello)

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

新鲜出炉的蚂蚁金服面经,熬夜整理出来的答案,已有千人收藏

)]
[外链图片转存中…(img-mNlBlRKf-1710743390790)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-AZ4Agubf-1710743390790)]

最后

我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、OPPO、银盛支付、中国平安等初,中级,高级Java面试题集合,附带超详细答案,希望能帮助到大家。

[外链图片转存中…(img-CbA6ybyI-1710743390791)]

还有专门针对JVM、SPringBoot、SpringCloud、数据库、Linux、缓存、消息中间件、源码等相关面试题。

[外链图片转存中…(img-CDFKmZFu-1710743390791)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值