在Spring框架中,bean的生命周期是一个从创建到销毁的过程,其中包含了多个阶段和接口回调。以下是Spring中bean生命周期的详细步骤:
- Bean元信息配置阶段(Bean信息的定义):
- 使用XML文件:在Spring的XML配置文件中,使用
<bean>
标签定义Bean。这包括指定Bean的类名、作用域、初始化方法和销毁方法等。 - 使用注解:Spring也支持使用注解来定义Bean。常见的注解有
@Component、@Service、@Repository和@Controller
等,它们都可以用于定义Bean。 - Java配置类:Spring 3.0引入了Java配置的概念,允许使用Java类来替代XML文件。通过
@Configuration注解的类可以包含@Bean注解的方法
,这些方法会返回需要被Spring管理的对象实例。
- Bean元信息解析阶段:
Bean元信息解析阶段是Spring框架中Bean生命周期的一个重要环节。在这一阶段,Spring容器会将各种方式定义的Bean配置信息解析为BeanDefinition对象,这个对象包含了Bean的元数据信息,即如何创建Bean实例、Bean属性的值以及Bean之间的依赖关系等。以下是Bean元信息解析阶段的详细步骤和说明:- 解析方式:
- XML方式:使用
XmlBeanDefinitionReader
类来解析XML文件中定义的Bean。这个类需要传入一个BeanDefinitionRegistry,解析过程中生成的BeanDefinition会注册到这个注册器中。 - Properties文件方式:虽然较少使用,但Spring也提供了
PropertiesBeanDefinitionReader
来解析properties文件中定义的Bean。 - 注解方式:对于注解方式定义的Bean,Spring使用
AnnotatedBeanDefinitionReader
来进行解析。
- XML方式:使用
- 解析过程:
- 读取配置:无论是XML、Properties文件还是注解,解析器都会首先读取这些配置信息。
- 转换为BeanDefinition:然后,解析器会将读取到的配置信息转换为Spring内部的BeanDefinition对象。BeanDefinition是Bean的元数据描述,包含了Bean的类名、作用域、属性、构造函数参数、初始化方法、销毁方法等信息。
- 注册BeanDefinition:解析完成后,生成的BeanDefinition会被注册到Spring的BeanDefinitionRegistry中,以便后续的Bean实例化、依赖注入等操作。
- 注意事项:
- 在解析过程中,如果遇到配置错误或不符合规范的地方,Spring会抛出异常并终止解析过程。
- 由于XML和Properties文件是静态的,因此它们的解析通常只在容器启动时进行一次。而注解方式则可能在容器启动后的某个时间点进行解析,这取决于注解的扫描配置和时机。
- 总结:Bean元信息解析阶段是Spring框架将外部配置的Bean信息转换为内部可处理的BeanDefinition对象的过程。通过这一过程,Spring能够知道如何创建、配置和管理Bean实例。
- 解析方式:
- 将Bean注册到容器中:
- 将解析后的BeanDefinition注册到Spring容器中
。在Spring框架中,将解析后的BeanDefinition注册到容器中是通过BeanDefinitionRegistry接口实现的
。BeanDefinitionRegistry是Spring内部用于注册和管理BeanDefinition的接口。以下是如何将BeanDefinition注册到Spring容器中的一般步骤:获取BeanDefinitionRegistry:首先,你需要获取一个BeanDefinitionRegistry的实例。这通常是通过ApplicationContext或ConfigurableApplicationContext来间接实现的,因为这些接口通常都包含了BeanDefinitionRegistry的功能。
创建BeanDefinition:你可以使用RootBeanDefinition、GenericBeanDefinition等类来手动创建BeanDefinition。这些类提供了设置Bean的类名、作用域、构造函数参数、属性等信息的方法。
注册BeanDefinition:BeanDefinitionRegistry的registerBeanDefinition方法将BeanDefinition注册到容器中。这个方法接受两个参数:Bean的名称和BeanDefinition对象。
以下是一个简单的示例,演示如何手动创建并注册一个BeanDefinition:
package com.springIoc.BeanDefinitionDemo;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class BeanRegistrationExample {
public static void main(String[] args) {
// 1、创建一个ApplicationContext,它同时也实现了BeanDefinitionRegistry
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
///2、创建一个BeanDefinition
RootBeanDefinition beanDefinition = new RootBeanDefinition(MyBean.class);
// 可以通过beanDefinition设置更多的属性,例如作用域、初始化方法等
// beanDefinition.setScope(...);
// beanDefinition.setInitMethodName(...);
// 3、获取ConfigurableListableBeanFactory,它提供了registerBeanDefinition方法
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory();
// 4、注册BeanDefinition到容器中
beanFactory.registerBeanDefinition("myBean", beanDefinition);
// 启动Spring容器
context.refresh();
// 现在可以从容器中获取Bean
MyBean myBean = context.getBean(MyBean.class);
myBean.doSomething();
// 使用Bean...
// 关闭容器
context.close();
}
static class MyBean {
void doSomething() {
System.out.println("doSomething ... ");
}
// ...
}
}
在上面的示例中,我们使用了AnnotationConfigApplicationContext作为ApplicationContext的实现,并通过强制类型转换将其转换为ConfigurableListableBeanFactory,从而可以访问registerBeanDefinition方法。然后,我们创建了一个RootBeanDefinition对象,并将其注册到容器中。最后,我们启动了Spring容器,并从中获取了Bean。
需要注意的是,在大多数情况下,我们不需要直接操作BeanDefinitionRegistry来注册Bean。相反,我们通常会使用@Component、@Service、@Repository等注解来自动注册Bean,或者使用@Bean注解在配置类中声明Bean。这些方式都是基于Java配置的方式,它们会在Spring容器启动时自动解析并将Bean注册到容器中。
- BeanDefinition合并阶段(如有多个定义源):
- 在Spring框架中,BeanDefinition的合并通常不是直接通过开发者手动操作的,而是由Spring容器在内部自动处理的。当Spring容器进行组件扫描或解析配置时,它可能会遇到多个定义相同Bean名称的情况,此时Spring容器会尝试合并这些BeanDefinition以确保最终只有一个实例被注册。
- Bean Class加载阶段:
Bean Class加载阶段是Spring框架中Bean生命周期的一个重要阶段。在这个阶段,Spring容器会将Bean的类名(通常存储在BeanDefinition的beanClass字段中)转换为实际的Class类型的对象。以下是对Bean Class加载阶段的详细解释:- 阶段描述:
- Bean Class加载阶段发生在Spring容器处理BeanDefinition的过程中。
- 在这个阶段,Spring容器会将BeanDefinition中定义的Bean类名转换为Class对象。
- 这个转换过程通常通过Java的类加载器(ClassLoader)完成。
- 处理过程:
- Spring容器首先会检查BeanDefinition中的beanClass字段。
- 如果beanClass字段的值是一个Class对象(即已经是一个Class类型),则不需要进一步解析。
- 如果beanClass字段的值是一个类名的字符串,则Spring容器会利用Java的类加载机制来加载该类,并获取对应的Class对象。
- 源码位置(参考自参考文章1):
- 在Spring框架中,Bean Class加载阶段的源码通常位于org.springframework.beans.factory.support.AbstractBeanFactory#resolveBeanClass方法中。
- 这个方法会对BeanDefinition中的beanClass字段进行解析,将类名转换为Class对象。
- 注意事项:
- 如果在类加载过程中出现问题(例如类不存在、类加载器无法访问等),Spring容器会抛出异常。
- 开发者需要确保BeanDefinition中定义的Bean类名是可用的,并且类加载器能够正确加载该类。
- 后续阶段:
- 一旦Bean Class加载阶段完成,Spring容器就会进入Bean的实例化阶段。
- 在实例化阶段,Spring容器会根据BeanDefinition中的配置信息来创建Bean的实例。
- 总的来说,Bean Class加载阶段是Spring框架中Bean生命周期的一个重要组成部分,它负责将Bean的类名转换为实际的Class对象,为后续的Bean实例化阶段做准备。
- 阶段描述: