Spring的bean创建顺序

本文详细解析了Spring中Bean的加载与初始化顺序,包括不同类型的Bean如何按顺序加载、BeanFactoryPostProcessor与BeanPostProcessor的作用时机,以及单个Bean的具体加载流程。
部署运行你感兴趣的模型镜像

解答以下疑惑:

  • Spring配置文件里bean,究竟是按什么样的顺序加载呢?
  • Spring项目在部署时,究竟创建了多少各beanFactory呢?按什么顺序创建?


长话短说,总结成如下几点:

Rule1

首先读取WEB-INF/web.xml文件,该文件内一般会配置spring-configspring-mvc。按顺序加载对应的xml文件。


Rule2

web.xml中还有除springmvc和默认的servlet之外的servlet(如servlet-test),那么这些servlet会按照定义的顺序执行,但一定是在默认servlet之后,springmvc之前执行,并且,若这些servlet都会分别对应一个ApplicationContext,当然也意味着分别拥有一个beanFactory。这些ApplicationContext(包括springmvc的那个),他们的parent ApplicationContext均是默认servlet对应的那个ApplicationContextRoot ApplicationContext)。默认的servlet,是通过参数contextConfigLocation来指定一个xml文件。

因此,若springmvc里的某个Controller尝试通过auto wire注解来注入servlet-test里面的service,那么在运行时会抛出”Could not autowire field …”异常,因为springspringmvc那个servlet中的beanFactory(包括其父beanFactory)中找不到对应的bean


Rule3

在加载某个包含beanxml文件时,按照bean的类型1BeanFactoryPostProcessor类的bean2BeanPostProcessor类的bean3)普通bean,包括import进来的(bean标签和scan标签指定的);的顺序进行加载。同类型的bean按照定义顺序加载。所有bean默认是单例的。

因此,对于BeanFactoryPostProcessorBeanPostProcessor类型的bean,即使被放置在最后面,也会先加载哦。这种设计还是挺人性化的^_^


Rule4

component-scan生成的bean的默认id是类名(首字母小写),例如testService1

<bean>标签生成的bean的默认id:包名.类名#数字,例如qk.spring.beanFactory.service1.TestService1#0

如果component-scanbean标签生成的bean有冲突(即beanid相同),并且都是单例(默认是单例),那么不会重复创建,只保留最先创建出来的那个,同一个属性的话,后续的会覆盖前面的。


Rule5:

创建BeanFactory时,按照如下顺序(beanpostprocessor不会处理beanFactory,虽然他也是个bean)):

见(org.springframework.context.support.AbstractApplicationContext

(见AbstractAutowireCapableBeanFactory#initializeBean(…)。里面分为AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization(…)AbstractAutowireCapableBeanFactory#postProcessObjectFromFactoryBean(…)

0)创建beanFactory,并尽可能初始化,此时普通bean还未创建;

1)创建spring自己的部分BeanPostProcessor;

2)创建并执行BeanFactoryPostProcessor(包括自定义的)

3)创建并注册剩余的内置BeanPostProcessorApplicationContextAwareProcessor(见AbstractApplicationContext#prepareBeanFactory(…)),ServletContextAwareProcessor见(AbstractRefreshableWebApplicationContext#postProcessBeanFactory(…));

4)创建并注册自定义的BeanPostProcessor(如top代理,config)(AbstractApplicationContext#refresh());

5)创建普通bean(同时应用bean post processor)。





Rule6:单个bean加载过程

按照如下顺序:

1)构造函数;

2BeanPostProcessor#postProcessBeforeInitialization(…)

3)设置property

4InitializingBean#afterPropertiesSet();

5BeanPostProcessor#postProcessAfterInitialization(…)

6FactoryBean#getObject()的顺序构造bean实例。



这里是样例project


您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

<think> 首先,用户的问题是:“我想了解Spring框架中Bean创建顺序”,这源于之前的对话。用户引用了几个参考内容,这些内容讨论了Spring Bean的生命周期、加载过程和控制加载顺序的方法。 关键点从引用中提取: - 引用[1]:提到Bean通过解析XML或扫描路径生成,并存储在容器中。开发人员可以通过扩展点干预Bean的解析过程。 - 引用[2]:介绍了@DependsOn注解,用于控制Bean的初始化顺序。 - 引用[3]:讨论了Bean加载阶段:扫描生成BeanDefinition,存储在容器中,然后实例化。 - 引用[4]:详细描述了BeanPostProcessor接口,包括postProcessBeforeInitialization和postProcessAfterInitialization方法,并给出了一个示例。 用户的核心需求是了解Spring框架中Bean创建顺序。我需要确保回答真实可靠,基于Spring框架的原理。 Spring Bean创建顺序涉及几个阶段: 1. **BeanDefinition加载**:Spring容器启动时,通过配置文件(如XML)或组件扫描(如注解)加载BeanDefinition。 2. **依赖解决**:容器解析Bean之间的依赖关系。 3. **Bean实例化**:容器根据BeanDefinition创建Bean实例。 4. **属性设置**:注入依赖属性。 5. **初始化**:调用初始化方法(如@PostConstruct、InitializingBean的afterPropertiesSet)。 6. **BeanPostProcessor回调**:在初始化前后调用相关方法。 控制顺序的方法包括: - 使用@DependsOn注解指定依赖。 - 实现BeanPostProcessor自定义处理。 - 使用@Order注解或在配置中定义顺序。 回答结构: - 由于用户要求“逐步解决问题”,我将以清晰、分步的方式组织回答。 - 使用中文。 - 遵守系统指令:行内数学表达式使用$...$,独立公式使用$$...$$(但这里可能不涉及复杂数学),确保LaTeX语法正确。 - 在回答中引用段落末尾添加引用标识。 - 最后生成相关问题。 回答大纲: 1. **介绍Bean创建顺序概述**。 2. **详细步骤分解**: - BeanDefinition加载阶段。 - Bean实例化阶段。 - 依赖注入阶段。 - 初始化阶段。 - BeanPostProcessor角色。 3. **如何控制Bean创建顺序**:使用@DependsOn、@Order、BeanPostProcessor等。 4. **示例说明**。 5. **生成相关问题**。 确保真实可靠:基于Spring官方文档或权威知识。 最后,生成相关问题:基于用户问题。 </think>### Spring框架中Bean创建顺序Spring框架中,Bean创建顺序是一个严谨的过程,涉及多个阶段。这个过程确保依赖关系得到正确处理,并允许通过扩展点进行干预。以下是基于Spring生命周期原理的详细分步解释(参考自相关引用)。 #### 1. **BeanDefinition加载阶段** - 容器启动时,Spring通过解析配置文件(如XML)或组件扫描(如注解)加载Bean定义(BeanDefinition)。这些定义包含Bean的元数据,包括名、作用域(如singleton或prototype)、依赖关系等。 - 例如,扫描到@Component注解的会生成BeanDefinition,并存储在容器内部[^3]。 - **顺序影响**:BeanDefinition的加载顺序通常由扫描路径或配置文件顺序决定,但不直接影响Bean实例化顺序。 #### 2. **依赖解决阶段** - Spring容器分析Bean之间的依赖关系(如通过构造函数注入或setter注入)。如果存在循环依赖,Spring会尝试解决,但可能导致异常。 - 使用@DependsOn注解可以显式指定依赖顺序。例如,`@DependsOn("beanA")`表示在初始化当前Bean前,必须先初始化`beanA`[^2]。 - **数学表示**:假设有n个Bean,依赖关系可建模为有向无环图(DAG),Spring通过拓扑排序确定初始化顺序: $$ G = (V, E) \text{,其中顶点 } V \text{ 表示Bean,边 } E \text{ 表示依赖关系} $$ 顺序由$ \text{topologicalSort}(G) $决定。 #### 3. **Bean实例化阶段** - 容器根据BeanDefinition创建Bean实例。这通常通过反射调用构造函数完成。 - 顺序规则: - 无依赖的Bean先实例化。 - 依赖其他BeanBean后实例化(例如,BeanB依赖BeanA时,BeanA先创建)。 - 如果多个Bean无依赖,顺序不确定,由容器内部实现决定(可通过@Order注解或Ordered接口控制优先级)[^1][^3]。 #### 4. **属性注入和初始化阶段** - **属性注入**:Spring注入依赖属性(如通过@Autowired)。 - **初始化回调**: - 调用@PostConstruct注解的方法。 - 如果Bean实现InitializingBean接口,调用afterPropertiesSet()方法。 - **BeanPostProcessor干预**:自定义BeanPostProcessor可在初始化前后执行逻辑: - postProcessBeforeInitialization():在初始化前调用(如执行自定义验证)。 - postProcessAfterInitialization():在初始化后调用(如代理Bean)[^4]。 - 示例:一个BeanPostProcessor可以打印日志,监控Bean生命周期[^4]。 #### 5. **完整生命周期顺序总结** 1. BeanDefinition加载(扫描和解析)。 2. 依赖关系分析(@DependsOn生效)。 3. Bean实例化(构造函数调用)。 4. 属性注入(依赖注入)。 5. BeanPostProcessor.postProcessBeforeInitialization()。 6. 初始化回调(@PostConstruct或afterPropertiesSet)。 7. BeanPostProcessor.postProcessAfterInitialization()。 8. Bean就绪,可用于应用程序。 #### 如何控制Bean创建顺序 - **@DependsOn**:显式指定依赖Bean,确保顺序[^2]。 - **@Order或Ordered接口**:定义Bean或组件的优先级(值越小优先级越高)。 - **BeanPostProcessor**:自定义处理逻辑,在初始化前后干预顺序(例如,通过调整Bean引用来改变依赖)[^1][^4]。 - **配置顺序**:在XML或Java配置中,手动定义Bean顺序。 #### 示例场景 假设有两个Bean:`ServiceA`和`ServiceB`,其中`ServiceB`依赖`ServiceA`。 ```java @Service public class ServiceA { /* ... */ } @Service @DependsOn("serviceA") // 确保ServiceA先初始化 public class ServiceB { /* ... */ } ``` - 创建顺序:1. ServiceA实例化 → 2. ServiceA属性注入 → 3. ServiceA初始化 → 4. ServiceB实例化(依赖已满足)→ 5. ServiceB属性注入 → 6. ServiceB初始化。 **注意**:Spring默认使用懒加载(lazy-init)时,Bean在首次使用时才创建;否则,在容器启动时创建顺序控制对单例Bean最有效[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值