SpringBoot和Bean的初始化过程

本文详细解析了SpringBoot项目的启动过程,包括入口方法、Web项目构建所需的基础依赖及自动配置机制。深入探讨了@Autowired依赖注入的工作原理,展示了完整的调用栈,揭示了SpringBoot如何管理Bean的创建和依赖解决。

1、SpringBoot的入口

SpringApplication.run(Application.class, args);

2、SpringBootWeb项目

最少需要导入jar包

  • org.springframework.boot:spring-boot-starter-web:2.0.5.RELEASE

这个jar包默认依赖以下jar

  • org.springframework.boot:spring-boot-starter-json:2.0.5.RELEASE
  • org.springframework.boot:spring-boot-starter:2.0.6.RELEASE
  • org.springframework.boot:spring-boot-starter-tomcat:2.0.5.RELEASE
  • org.hibernate.validator:hibernate-validator:6.0.12.Final
  • org.springframework:spring-webmvc:5.0.9.RELEASE

实现自动配置的类:WebMvcAutoConfiguration

 

3、当使用@Autowired注入bean的时候,调用栈,调用线程main

	  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(AutowiredAnnotationBeanPostProcessor.java:231)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1013)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:547)
	  - locked <0x312e> (a java.lang.Object)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	  at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$134.178917238.getObject(Unknown Source:-1)
	  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	  - locked <0x172a> (a java.util.concurrent.ConcurrentHashMap)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	  at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:251)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1135)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1062)
	  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:581)
	  at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90)
	  at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:370)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1336)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:572)
	  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
	  at org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$134.178917238.getObject(Unknown Source:-1)
	  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
	  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
	  at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:759)
	  at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
	  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:548)
	  - locked <0x3139> (a java.lang.Object)
	  at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
	  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
	  at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242)
	  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230)

可以看到栈顶是AutowiredAnnotationBeanPostProcessor 的postProcessMergedBeanDefinition方法

<think>嗯,用户的问题是关于在Spring Boot中Bean初始化时无法获取@Value注入的值,需要解决方案。首先,我得回忆一下Spring Bean的生命周期,尤其是初始化阶段属性注入的顺序。 根据之前的引用,比如引用[1]提到,在生成Bean类的时候,参数可能还没注入,导致无法获取。这可能是因为某些初始化方法执行得太早,比如构造函数或者@PostConstruct方法,这时候@Value的值还没被处理。用户可能在Bean初始化方法中尝试访问这些值,导致null。 接下来,解决方案可能有几种。比如使用InitializingBean接口的afterPropertiesSet方法,因为这时候所有属性已经注入完成,引用[2]中的例子就是用这个接口来执行初始化逻辑。或者用@PostConstruct注解的方法,但要确保这时候属性已经注入。不过根据引用[1]中的情况,可能有些Bean初始化阶段较早,需要调整执行顺序。 另外,检查配置是否正确也很重要。比如属性文件是否被正确加载,@PropertySource是否使用正确,或者是否是YAML文件格式问题。引用[3][4]提到了@Value的使用方式,比如${}#{}的区别,可能需要检查表达式是否正确。 还有一种情况是作用域问题,比如原型Bean可能每次都会重新初始化,但用户的问题可能更集中在单例Bean上。另外,环境变量的覆盖也可能导致属性值被覆盖,需要检查优先级。 然后,用户可能需要具体的步骤,比如推荐使用InitializingBean接口,或者结合@ConfigurationProperties来批量注入属性,避免逐个使用@Value。同时,延迟初始化也是一个选项,确保Bean在需要的时候属性已经注入完毕。 最后,应该提醒用户检查依赖注入是否成功,比如用调试输出或者日志来确认属性值是否正确注入。可能还需要注意Spring Boot版本的问题,或者是否有自定义的BeanPostProcessor影响注入时机。 总结起来,解决方案可能括调整初始化方法的位置、使用正确的接口或注解、检查配置属性源、以及考虑作用域延迟初始化。需要把这些点整理成结构清晰的步骤,并给出示例代码,帮助用户理解应用。</think>在Spring Boot中,Bean初始化时无法获取@Value注入值的根本原因是属性注入与初始化方法的执行顺序问题。以下是具体分析及解决方案: ### 一、问题原因分析 1. **生命周期顺序**:Spring Bean初始化流程为: ```text 实例化 -> 属性注入(@Value) -> 初始化方法(@PostConstruct/InitializingBean) ``` 如果在构造函数或`@PostConstruct`方法中访问@Value值,可能因属性注入未完成导致获取失败[^1][^2] 2. **配置加载时机**:当使用`@PropertySource`加载外部配置时,若配置加载延迟或路径错误,会导致@Value无法解析占位符 3. **作用域问题**:原型作用域(prototype)Bean每次获取新实例时,若初始化逻辑与属性注入存在时序冲突,可能导致值未正确注入 ### 二、解决方案及示例 #### 方案1:使用InitializingBean接口 ```java @Component public class MyBean implements InitializingBean { @Value("${app.timeout}") private Integer timeout; @Override public void afterPropertiesSet() { // 此处可以安全使用timeout System.out.println("初始化完成,timeout=" + timeout); } } ``` **原理**:`afterPropertiesSet()`方法在所有属性注入完成后执行[^2] #### 方案2:延迟初始化配置 ```yaml # application.yml spring: main: lazy-initialization: true ``` ```java @Component @Lazy public class LazyBean { @Value("${app.url}") private String url; @PostConstruct public void init() { // 延迟初始化确保属性已加载 } } ``` #### 方案3:使用环境变量直接获取 ```java @Component public class EnvBean implements EnvironmentAware { private Environment env; @Override public void setEnvironment(Environment env) { this.env = env; } public void showValue() { String value = env.getProperty("app.key"); } } ``` #### 方案4:配置类预处理(推荐) ```java @Configuration @PropertySource("classpath:custom.properties") public class AppConfig { @Bean public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } @Bean public MyService myService(@Value("${service.threads}") int threads) { return new MyService(threads); // 构造时已注入值 } } ``` ### 三、验证与调试技巧 1. **诊断注入状态**: ```java @Autowired private ApplicationContext context; public void checkValues() { String value = context.getEnvironment().getProperty("your.key"); System.out.println("环境变量值:" + value); } ``` 2. **使用条件断点**: ```java @Value("${debug.flag}") private Boolean debugFlag; // 在IDE中设置条件断点:debugFlag == true ``` 3. **配置检查清单**: - 确认`@ComponentScan`含目标 - 检查属性文件编码(推荐UTF-8) - 验证YAML缩进格式(必须使用空格) - 测试占位符解析:`curl localhost:8080/actuator/env | grep your.key` ### 四、进阶优化建议 1. **使用@ConfigurationProperties替代@Value** ```java @ConfigurationProperties(prefix = "app") @Component public class AppConfig { private int timeout; private String endpoint; // getters/setters } ``` 2. **自定义属性解析器** ```java public class CustomPropertyResolver implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { ConfigurableEnvironment env = beanFactory.getBean(ConfigurableEnvironment.class); env.getPropertySources().addFirst(new MyCustomSource()); } } ``` 3. **控制初始化顺序** ```java @Bean(initMethod = "init") @DependsOn("propertyConfigurer") public DependentBean dependentBean() { return new DependentBean(); } ``` ### 五、常见错误场景 1. **静态变量注入**(错误示例): ```java @Component public class WrongBean { @Value("${static.value}") private static String VALUE; // 无法注入静态变量 // 正确方式应使用setter方法+@Value private static String value; @Value("${static.value}") public void setValue(String val) { WrongBean.value = val; } } ``` 2. **构造函数中使用@Value值**: ```java @Component public class ConstructorBean { private final String config; public ConstructorBean(@Value("${app.config}") String config) { this.config = config; // 正确方式! } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值