2026/5/21 19:56:47
网站建设
项目流程
好的外国设计网站推荐,建造师考试,网站域名是网站架构吗,万网做网站怎么样网络上对于spring生命周期的总结很多#xff0c;对初学者来说看起来眼花缭乱#xff0c;实际上Spring Bean的生命周期只有四个阶段。把这四个阶段和每个阶段对应的扩展点糅合在一起虽然没有问题#xff0c;但是这样非常凌乱#xff0c;难以记忆。要彻底搞清楚Spring的生命周…网络上对于spring生命周期的总结很多对初学者来说看起来眼花缭乱实际上Spring Bean的生命周期只有四个阶段。把这四个阶段和每个阶段对应的扩展点糅合在一起虽然没有问题但是这样非常凌乱难以记忆。要彻底搞清楚Spring的生命周期首先要把这四个阶段牢牢记住。实例化和属性赋值对应构造方法和setter方法的注入初始化和销毁是用户能自定义扩展的两个阶段。在这四步之间穿插的各种扩展点稍后会讲。实例化 Instantiation属性赋值 Populate初始化 Initialization销毁 Destruction用一张图就能贯穿我们将要学习的spring生命周期全流程BeanDifinition定义阶段元信息的配置和解析是一块很大的内容后续还会专门开一篇章节分析其中的逻辑本节只是基本介绍一下有那些类型和方案。1.元信息配置面向资源有三种面向资源的bean定义信息读取方式由于不常用可以忽略groovy的方式。XML方式配置定义bean注册beanpublic static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();// 实例化 基于properties资源的 BeanDefinitionReaderXmlBeanDefinitionReader beanDefinitionReader new XmlBeanDefinitionReader(beanFactory);String location “/META-INF/dependency-lookup-context.xml”;// 加载 Properties 文件,用resource的方式可以避免乱码// 指定字符编码 UTF-8Resource resource new ClassPathResource(location);EncodedResource encodedResource new EncodedResource(resource, “UTF-8”);int beanNumbers beanDefinitionReader.loadBeanDefinitions(encodedResource);System.out.printf(“已加载的 BeanDefinition 数量: %d%n”, beanNumbers);// 通过 bean id 和类型进行依赖查找User user beanFactory.getBean(“user”, User.class);System.out.println(user);}Propeties配置定义bean注册bean定义信息public static void main(String[] args) {DefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();// 实例化 基于properties资源的 BeanDefinitionReaderPropertiesBeanDefinitionReader beanDefinitionReader new PropertiesBeanDefinitionReader(beanFactory);String location “/META-INF/user-bean.properties”;// 加载 Properties 文件,用resource的方式可以避免乱码// 指定字符编码 UTF-8Resource resource new ClassPathResource(location);EncodedResource encodedResource new EncodedResource(resource, “UTF-8”);int beanNumbers beanDefinitionReader.loadBeanDefinitions(encodedResource);System.out.printf(“已加载的 BeanDefinition 数量: %d%n”, beanNumbers);// 通过 bean id 和类型进行依赖查找User user beanFactory.getBean(“user”, User.class);System.out.println(user);}面向注解componentimportbean面向Api命名方式可以指定bean的名称BeanDefinitionBuilder#registerBeanDefinition非命名方式不指定bean的名称用spring默认的bean名称生成BeanDefinitionReaderUtils.registerWithGeneratedName配置类方式AnnotatedBeanDefinitionReader.register()public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(AwareConfig.class);//命名方式registerBeanDefinition(context,“user”);//非命名方式registerBeanDefinition(context);//配置类方式context.register(Student.class);MapString, Student beansOfType context.getBeansOfType(Student.class);System.out.println(beansOfType);}public static void registerBeanDefinition(AnnotationConfigApplicationContext context,String beanName){BeanDefinitionBuilder beanDefinitionBuilder BeanDefinitionBuilder.genericBeanDefinition(Student.class);beanDefinitionBuilder.addPropertyValue(“name”,“zzb”).addPropertyValue(“age”,18);if(StringUtils.isEmpty(beanName)){//非命名方式BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinitionBuilder.getBeanDefinition(),context);}else {//命名方式context.registerBeanDefinition(beanName,beanDefinitionBuilder.getBeanDefinition());}}public static void registerBeanDefinition(AnnotationConfigApplicationContext context){registerBeanDefinition(context,null);}2.元信息解析针对资源的解析有两个解析器XmlBeanDefinitionReader和PropertiesBeanDefinitionReader他们都继承于AbstractBeanDefinitionReader针对于注解的解析有AnnotatedBeanDefinitionReader他不继承与任何类因为注解的扫描和资源的扫描是完全不同的AnnotationConfigApplicationContext上下文中默认的reader就是AnnotatedBeanDefinitionReader3.元信息注册我们的ioc容器支持定义信息的直接注册DefaultListableBeanFactory#registerBeanDefinition这里注意一点为什么有了map还需要有个names的集合是为了保证bean注册的顺序性这样之后就能顺序的获取bean的定义信息了。4.元信息合并在我们注入的类如果出现了继承的情况下我们需要读取父类的BeanDifinition配置合并到子类来。再什么时候会进行BeanDifinition的合并呢其实在BeanDifinition注册的时候还不会进行合并只有在第一次实例化bean的时候才会将BeanDifinition合并这也就解释了为什么我们把合并这一小节放在元信息注册的后面。下图所有的方法都属于AbstractBeanFactory这里mergedBeanDefinitions字段做了一层对bean定义的缓存如果有就不需要再去merge一遍了。深入下去看所有生成的定义信息默认都是GenericBeanDefinition如果没有parent的话就直接包装成一个新的RootBeanDefinition就返回了。如果是有父节点的就会递归去先加载父节点的定义信息最终还是包装成RootBeanDefinition返回。这里会将父节点信息copy一份然后再用子节点的信息去覆盖父节点的一些属性比如override方法比如一些基本属性点进去看自行了解AbstractBeanDefinition#overrideFromBean的加载阶段我们的bean在定义的时候他的beanClass 都是string类型的一个描述字段那么他是什么时候转为Class类型的呢在创建单例bean时可以看到这里会针对jdk规范做一些安全校验但是我们一般都没用到可以选择跳过拿着AbstractBeanFactory的classloader去找到自己的类类型并且更新自己的beanClass属性由string变成具体的class类型Bean 实例化阶段知道了bean的class类型我们就可以对它进行实例化了实例化前实现InstantiationAwareBeanPostProcessor接口重写postProcessBeforeInstantiation方法就可以截断bean的实例化返回一个新对象返回的不为null就会跳过createBean步骤实例化中实例化的核心是该用无参实例化还是构造器实例化如果用了构造器实例化其中传入的内容是以参数为准还是以bean类型为准去进行依赖查找。可以自行搭配源码探索并且可以参考spring反射拿到参数名的方式。实例化后实现InstantiationAwareBeanPostProcessor接口重写postProcessAfterInstantiation方法在bean实例化后属性注入前进行截断阻止属性的注入。属性赋值前实现InstantiationAwareBeanPostProcessor接口重写postProcessProperties方法在bean实例化后属性注入前可以根据属性描述动态的新增删除修改一些属性。Bean的初始化阶段初始化中会涉及大量的后置处理器和aware接口回调所有的初始化逻辑都会在》实例化后》属性赋值后》初始化Aware回调初始化前实现BeanPostProcessor的postProcessBeforeInstantiation方法的bean会被会执行该方法返回的对象会替换原有的实例化后的bean。初始化中bean的初始化会做以下三件事PostConstruct实际上PostConstruct 这个方法是靠CommonAnnotationBeanPostProcessor实现的而这个类实在bean的初始化前就进行的回调实现**InitializingBean**的**afterPropertiesSet**方法自定义初始化有指定初始化方法的就会回调bena内的方法回调位置初始化后初始化完成初始化完成是bean生成生命周期的最后一道关口并且这个回调必须是显示调用了DefaultListableBeanFactory#preInstantiateSingletons的方法对单例bean进行一个提前的初始化Bean的销毁阶段beanFactory.destroySingletons()当调用这个方法后会做的一些事情将所有容器中的单例bean相关信息移除回调所有相关的后置处理器和bean的销毁方法。销毁前销毁中销毁中有三个回调PreDestroy其实也是包装在CommonAnnotationBeanPostProcessor中回调的bean销毁的后置处理器先后顺序就看后置处理器注册时候的顺序了先后顺序可以从图中看出DisposableBean指定的销毁Bean的垃圾回收关闭spring容器并不会立刻进行垃圾回收需要等待系统GC我们也可以手动调用系统的GC。为了验证是否真的被gc我们还可以让ben重写finalize方法看看对应的情况。总结Spring Bean的生命周期分为四个阶段和多个扩展点。扩展点又可以分为影响多个Bean和影响单个Bean。整理如下四个阶段实例化 Instantiation属性赋值 Populate初始化 Initialization销毁 Destruction多个扩展点影响多个BeanBeanPostProcessorInstantiationAwareBeanPostProcessor影响单个BeanAwareAware Group1BeanNameAwareBeanClassLoaderAwareBeanFactoryAwareAware Group2EnvironmentAwareEmbeddedValueResolverAwareApplicationContextAware(ResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware)生命周期InitializingBeanDisposableBean参考参考博客