当前位置: 首页 > 后端技术 > Node.js

说说Springbean的生命周期

时间:2023-04-04 00:30:45 Node.js

在所有对Spring的解读中,Beans的生命周期是重中之重,甚至有人将Spring称为管理bean的容器。Bean的生命周期之所以被反复提及,是因为Spring具有对象创建(IOC)、属性注入(DI)、初始化方法调用、代理对象生成(AOP)等功能的核心能力,都在bean的生命周期中完成。知道了bean的生命周期,就可以知道Spring神奇的魔力是什么,如何一步步赋能,让原本普通的java对象最终成为拥有超能力的bean。Bean生命周期?Spring的生命周期大致分为几个核心阶段:创建->属性填充->初始化bean->使用->销毁。我们先简单了解一下这几个阶段的作用:创建阶段主要是创建对象。这里我们看到创建对象的权限是由Spring来管理的,不再是我们手动new的了。这也是IOC的理念。属性填充阶段主要是依赖注入,从Spring容器中找出当前对象所依赖的bean对象,然后填充到对应的属性中。bean初始化阶段要做的事情比较复杂,包括回调各种Aware接口,回调各种初始化方法,生成AOP代理对象。这个阶段主要是完成初始化回调,后面我们会分析。bean阶段的使用主要是程序运行过程中创建bean并提供服务的阶段。在bean销毁阶段,容器关闭或停止服务,bean被销毁。当然bean的生命周期还包括其他过程,比如暴露工厂对象,但是相对来说,都是在为其他功能做铺垫和准备。当我们说到相应的功能时,我们是在做详细的分析。1.1创建bean对象的创建是bean生命周期的第一步。毕竟必须先有1才能有0。创建对象的方式有很多种,比如new、reflection、clone等,那么Spring是如何创建对象的呢?大多数情况下,Spring通过反射创建对象,但是如果我们提供了Supplier或者factory方法,Spring也会直接使用我们提供的创建方法。我们从源码入手,看看Spring是如何选择创建方式的://源码位于AbstractAutowireCapableBeanFactory.javaprotectedBeanWrappercreateBeanInstance(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args){//再次解析BeanDefinition类确保类已经被ResolvedClassbeanClass=resolveBeanClass(mbd,beanName);//1:如果提供了Supplier,对象SupplierinstanceSupplier=mbd.getInstanceSupplier();if(instanceSupplier!=null){returnobtainFromSupplier(instanceSupplier,beanName);}//2:如果有工厂方法,则使用工厂方法生成对象//@Configration中配置@Bean的方法也会parsedasFactoryMethodif(mbd.getFactoryMethodName()!=null){returninstantiateUsingFactoryMethod(beanName,mbd,args);}//...省略部分代码//3:推断构造方法//3.1执行post-processor获取候选构造方法Constructor[]ctors=determineConstructorsFromBeanPostProcessors(beanClass,beanName);//3.2需要自动注入的情况ctUtils.isEmpty(args)){returnautowireConstructor(beanName,mbd,ctors,args);}//3.3默认使用不带参数的构造方法returninstantiateBean(beanName,mbd);}复制代码我们跟踪源码后,我们发现Spring推断的创建方法还是比较聪明的。具体逻辑是:先判断是否提供了Supplier,如果有则通过Supplier生成对象然后判断是否提供了工厂方法,如果有则使用工厂方法生成对象。如果没有提供,则需要推断构造方法。其逻辑是:如果只有一个构造方法,则直接使用该构造方法(如果构造方法有参数,则自动注入依赖参数)如果有多个构造参数,则判断是否有构造@Autowired注解的参数:如果没有,Spring默认选择无参构造方式;如果存在,并且存在@Autowired(required=true)的构造方法,则选择该构造方法;如果有,但是没有对于@Autowired(required=true)的构造方法,Spring会从所有带@Autowired的构造方法中选择一个匹配参数最多,类型最准确的构造方法,根据个数构造函数参数和类型匹配程度等。关于创建bean时如何选择构造方法,本文不做详细展开。因为本文的主要目的是分析bean的生命周期,所以我们只需要简单知道Spring会选择一种构造方法,然后通过反射创建对象即可。其实小伙伴们在阅读Spring源码的时候,也要学会抓大放小,关注核心流程。战术上可以先忽略细节,必要时再回过头来分析也不迟。不要卡住。进去迷路了。这是一个流程图,供有兴趣的人使用。有兴趣的也可以留言,后面我们单独分析。1.2合并BeanDefinition这个阶段是Spring提供的扩展点。通过MergedBeanDefinitionPostProcessor类型的后处理器,可以修改bean对应的BeanDefinition。Spring本身也充分利用了这个扩展点,做了很多初始化操作(不修改BeanDefinition),比如找到@Autowired、@Resource、@PostConstruct、@PreDestory标记的属性和方法,方便后续的属性注入和初始化回调。当然我们也可以自定义实现来修改BeanDefinition信息或者我们需要的初始化操作。有兴趣的小伙伴可以自行尝试。protectedvoidapplyMergedBeanDefinitionPostProcessors(RootBeanDefinitionmbd,ClassbeanType,StringbeanName){for(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofMergedBeanDefinitionPostProcessor){MergedBeanDefinitionPostProcessorbdp=(MergedBeanDefinitionPostProcessor)bp;bdp.postProcessMergedBeanDefinition(mbd,beanType,beanName);}}}复制代码1.3Exposefactoryobjects这个阶段主要是将早期的bean对象提前放入三级缓存singletonFactories中,以支持循环依赖。在后续的属性填充中,如果出现循环依赖,可以通过getObject()从三级缓存中获取bean,完成循环依赖场景下的自动注入。booleanearlySingletonExposure=(mbd.isSingleton()&&this.allowCircularReferences&&isSingletonCurrentlyInCreation(beanName));if(earlySingletonExposure){if(logger.isTraceEnabled()){logger.trace("Eagerlycachingbean'"+beanName+"'到allowforresolvingpotentialcircularreferences");}//为了支持循环依赖,将早期实例化bean的ObjectFactory添加到单例工厂(三级缓存)addSingletonFactory(beanName,()->getEarlyBeanReference(beanName,mbd,bean));}复制代码这个阶段完全是为了支持循环依赖。是Spring解决循环依赖的伏笔,在Bean生命周期中完全可以忽略。为了完整起见,我会和朋友们简单提一下。如果你不是很清楚Spring是如何解决循环依赖的,可以看笔者另一篇讲Spring循环依赖的文章,详细分析了Spring循环依赖的解决方案,并详细描述了本阶段的内容。1.4属性填充这个阶段完成了Spring的核心功能之一:依赖注入,包括自动注入、@Autowired注入、@Resource注入等。Spring会根据bean注入模型(不是默认自动注入)。然后调用InstantiationAwareBeanPostProcessor#postProcessProperties()完成@Autowired和@Resource的属性注入。protectedvoidpopulateBean(StringbeanName,RootBeanDefinitionmbd,@NullableBeanWrapperbw){//省略部分代码//获取bean的注入类型intresolvedAutowireMode=mbd.getResolvedAutowireMode();//1:自动注入//如果适用,按名称添加基于自动装配的属性值。if(resolvedAutowireMode==AUTOWIRE_BY_NAME){//注入autowireByName,mbdbeanw,newPvs);}//如果适用,按类型添加基于自动装配的属性值。if(resolvedAutowireMode==AUTOWIRE_BY_TYPE){//注入autowireByType(beanName,mbd,bw,newPvs);}pvs=newPvs;}//2:调用BeanPostProcessor完成@Autowired@Resource属性填充PropertyDescriptor[]filteredPds=null;如果(hasInstAwareBpps){如果(pvs==null){pvs=mbd.getPropertyValues();}对于(BeanPostProcessorbp:getBeanPostProcessors()){if(bpinstanceofInstantiationAwareBeanPostProcessor){InstantiationAwareBeanPostProcessoribp=(InstantiationAwareBeanPostProcessor)bp;//关键点:完成@Autowired@Resource属性填充PropertyValuespvsToUse=ibp.postProcessProperties(pvs,bw.getUrappedInstance(),ifbeanvsName)){if(filteredPds==null){//需要的属性injected会过滤掉Aware接口中包含的属性(通过ignoreDependencyInterface添加)filteredPds=filterPropertyDescriptorsForDependencyCheck(bw,mbd.allowCaching);}pvsToUse=ibp.postProcessPropertyValues(pvs,filteredPds,bw.getWrappedInstance(),beanName);如果(pvsToUse==null){返回;}}pvs=pvsToUse;}}}//3:依赖检查}checkDependencies(beanName,mbd,filteredPds,pvs);}//4:Applypropertiestobeansif(pvs!=null){applyPropertyValues(beanName,mbd,bw,pvs);}}复制代码关于依赖注入,笔者在ChattingSpringdependencyinjection中有详细分析。小伙伴们可以先体验一下Spring依赖注入的奇妙1.5Initializebean这个阶段主要进行bean的初始化操作,包括:回调Aware接口、回调初始化方法、生成代理对象等。invokeAwareMethods():回调BeanNameAware、BeanClassLoaderAware,BeanFactoryAware感知接口。回调后处理器的前置方法,其中:ApplicationContextAwareProcessor:回调EnvironmentAware、ResourceLoaderAware、ApplicationContextAware、ApplicationEventPublisherAware、MessageSourceAware、EmbeddedValueResolverAware感知接口。InitDestroyAnnotationBeanPostProcessor:调用@PostConstruct标记的方法。invokeInitMethods()调用初始化方法:如果bean是InitializingBean的子类,首先调用afterPropertiesSet()。回调一个自定义的initMethod,比如@Bean(initMethod="xxx")指定的初始化方法。回调到后处理器的post方法,可能返回一个代理对象。AbstractAutoProxyCreator和AbstractAdvisingBeanPostProcessor都可以生成代理对象。比如InfrastructureAdvisorAutoProxyCreator完成@Transactional代理对象的生成,AsyncAnnotationBeanPostProcessor完成@Async代理对象的生成。protectedObjectinitializeBean(StringbeanName,Objectbean,@NullableRootBeanDefinitionmbd){//1:回调Aware接口中的方法//完成Aware方法(BeanNameAware,BeanClassLoaderAware,BeanFactoryAware)的回调invokeAwareMethods(beanName,bean);对象wrappedBean=bean;if(mbd==null||!mbd.isSynthetic()){//2:调用before...方法//ApplicationContextAwareProcessor:其他Aware方法的回调//InitDestroyAnnotationBeanPostProcessor:@PostConstruct方法的回调wrappedBean=applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName);}try{//3:完成xml版本,回调@bean(initMethod)的init方法invokeInitMethods(beanName,wrappedBean,mbd);}//4:调用after方法//关键点:AOP生成代理对象if(mbd==null||!mbd.isSynthetic()){wrappedBean=applyBeanPostProcessorsAfterInitialization(wrappedBean,beanName);}returnwrappedBean;}复制代码初始化完成后,将bean放入单例池并正式启动自己的任务:为项目服务,比如接收http请求,进行增删改查等,后面用到bean的地方,也是直接从单例池中获取,不会再次创建bean(只有单例).beans的来龙去脉2.1beans的扫描阶段既然知道了Springbean是如何创建的,那么这些bean是什么时候创建的呢?是不是遵循了懒加载的思想,在实际使用中创建它们?其实不是的,因为bean和生命周期的复杂关系,Spring会在容器启动的时候实例化这些bean,然后放到单例池中,随时可以使用。并且创建前、创建中、创建后都会做很多检查,确保创建的bean符合要求,这里不再赘述。言归正传,细心的你一定会发现,在创建bean的时候,主要是从参数RootBeanDefinitionmbd中获取bean的相关信息。其实这就是大名鼎鼎的BeanDefinition,封装了bean的元数据信息。关于BeanDefinition,我们后面会单独讲解,这里先理解为bean的元数据信息。那么这些元数据信息是什么时候解析出来的呢?这是关于Spring的类扫描。大致流程是:通过ASM字节码技术扫描所有类->找出需要用@Component注解的类(简单理解)->封装到BeanDefinition中->存入集合。后面实例化bean的时候,可以遍历这个集合,得到BeanDefinition,然后创建bean。关于处理类扫描的ConfigurationClassPostProcessor后处理器以及ConfigurationClassParser和ComponentScanAnnotationParser的Scanner的具体细节,后面会单独讲解,与本章关系不大,先简单了解一下。2.2实例化后的回调我们在前面的章节中已经分析过:容器中的bean被实例化并放入单例池后,创建阶段的bean的生命周期正式结束,进入in-use阶段,并开始完成服务。路。确实,这就是创建bean的全过程。如果有人看过笔者之前关于Spring事件的文章(聊聊Spring事件机制),会发现@EventListener处理器的识别和注册是在afterSingletonsInstantiated阶段完成的。的。其实这也是一个扩展点。我们可以实现SmartInitializingSingleton#afterSingletonsInstantiated()。bean初始化完成后,会回调这个方法来触发我们自己的业务逻辑,这里单独说一下。不清楚的朋友请先移步了解一下。2.3bean销毁阶段protectedObjectdoCreateBean(StringbeanName,RootBeanDefinitionmbd,@NullableObject[]args)throwsBeanCreationException{//...省略代码try{//为bean注册DisposableBean,容器调用destroy()registerDisposableBeanIfNecessary(beanName,bean,mbd);}catch(BeanDefinitionValidationExceptionex){thrownewBeanCreationException(mbd.getResourceDescription(),beanName,"Invaliddestructionsignature",ex);}returnexposedObject;}复制代码创建bean时,它会判断如果bean是DisposableBean的子类,AutoCloseable,或者有destroy-method等,就会注册为可破坏的bean。当容器关闭时,会调用相应的方法来销毁bean。

最新推荐
猜你喜欢