@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开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
最后
我还通过一些渠道整理了一些大厂真实面试主要有:蚂蚁金服、拼多多、阿里云、百度、唯品会、携程、丰巢科技、乐信、软通动力、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)]