前言俗话说:金三银四,在这个季节,一种叫做程序员的生物活跃起来。这两天同事出去面试了,她回来问我:为什么这些面试官老是问Spring,特别是Bean的生命周期,Bean的生命周期是什么,说说吧。那我们就来说说这个话题吧。这篇文章主要分为两点:1.什么是bean的生命周期?2、什么是bean的生命周期?让我先向您展示完整的bean生命周期。不懂没关系我稍后再说。Bean的生命周期是怎样的?我们知道在Java中,万物皆对象,而这些对象都有一个生命周期:实例化->gc回收,Bean在Java中也是对象,但同时Spring赋予了它更多的意义。于是乎,我们把这个Bean的生命周期称为从Spring创建Bean到Bean被销毁结束。那么在Spring中创建bean的过程是怎样的呢?bean的生命周期是怎样的?在Spring中,创建bean的过程看起来很复杂,但其实逻辑很清晰。如果我们把所有的扩展过程都扔掉,你会发现只剩下两个过程:对象实例化和属性填充。我们在《深入浅出Spring架构设计》文章中手写的Spring只完成了这两个过程,足以说明只有这两个过程才能完成一个简单的Spring框架,那么其他的过程是什么呢?这些是什么?功能?那么我们将从这两个核心流程入手,尝试完善整个SpringBean的生命周期。在推理过程的开始,我们只有两个过程:对象实例化和属性填充。我们知道,对象的实例化就是在Java中使用类构造函数来创建一个对象。而一个类中可能有很多构造函数,那么我们怎么才能知道使用哪个构造函数来实例化对象呢?所以,在实例化之前,我们首先要做一件事:确定候选构造函数,也称为构造函数推断。构造函数推理功能说明:查找beanClass中所有满足候选条件的构造函数。负责角色:AutowiredAnnotationBeanPostProcessor。候选条件:构造函数中添加了@Autowired注解。推理过程:1.获取beanClass中的所有构造函数进行遍历,判断构造函数上是否标注了@Autowired注解,如果是则将该构造函数添加到候选构造函数集中。2、进一步判断Autowired注解中的required属性是否为true(默认为true),如果为true,说明该beanClass已经有一个指定的构造函数用于实例化,不允许有其他带有@Autowired注解的构造函数。如果存在,则抛出异常。3.如果Autowired注解中的required属性为false,可以继续添加@Autowired(required=false)标识的其他构造函数。4.如果候选构造函数集不为空(带有Autowired标志的构造函数),并且beanClass中存在一个空构造函数,那么也将空构造函数添加到候选构造函数集中。5、如果候选构造函数集为空,但beanClass中只有一个构造函数,且该构造函数有参数,则将该构造函数添加到候选构造函数集中。流程图:遍历完构造函数后,还有一些逻辑。上面有很多判断条件,但始终围绕着这样一个逻辑:这个beanClass中是否有Autowired标记的构造函数,如果有,则要求true或false,如果为true,则不需要其他构造函数。如果为false,请根据需要添加任意数量的构造函数。咦,如果没有Autowired标记的构造函数怎么办?该框架是关于底线的。这里是看beanClass中是否只有一个构造函数,并且有参数。那么如果我只有一个无参数的构造函数呢?确实没有候选构造函数,但是Spring最后又一次抄底,在没有候选构造函数的情况下默认使用无参构造函数。如果我有很多构造函数怎么办?spring说不知道用哪个,也进入了底线策略:使用无参构造函数(不会抛出异常)。那么这就是构造函数推理过程,我们将其添加到流程图中。得到候选构造函数后,就可以实例化对象了,那么实例化过程是怎样的呢?对象实例化功能说明:根据候选构造函数集中的构造函数优先级实例化beanClass。负责角色:ConstructorResolver。在对象实例化的过程中主要需要关注两个方面:1.构造函数的优先级是多少?2.如果有多个构造函数,则构造函数需要的部分bean在Spring容器中并不存在。发生了什么?即,如何处理异常?首先,构造函数的优先级是什么?在Java中,多个构造函数被称为构造函数重载。重载有两种方式:参数个数不同,参数类型不同。在Spring中,优先级由构造函数修饰符(public或private)和参数个数决定。规则如下:1.public修饰构造函数>private修饰构造函数。2.修饰符相同的情况下,参数多的优先。这个过程很简单,只有两行代码://如果一个是public,另一个不是,那么public优先intresult=Boolean.compare(Modifier.isPublic(e2.getModifiers()),Modifier.isPublic(e1.getModifiers()));//都是public的,参数多的优先returnresult!=0?结果:Integer.compare(e2.getParameterCount(),e1.getParameterCount());本文描述的规则是public>private,只是为了更好的理解,实际比较的是public和non-public。2、Spring如何处理实例化异常?当一个beanClass中出现了多个构造函数,但是有些构造函数需要的bean在Spring容器中并不存在,这时候会发生什么?比如以下几种情况,InstanceA有3个构造方法,而InstanceB没有注入Spring。@ComponentpublicclassInstanceA{@Autowired(required=false)publicInstanceA(InstanceBinstanceB){System.out.println("实例B...");}@Autowired(required=false)publicInstanceA(InstanceCinstanceC){System.out.println("实例C...");}@Autowired(required=false)publicInstanceA(InstanceBinstanceB,InstanceCinstanceC,InstanceDInstanceD){System.out.println("实例BCD...");}}那么,是不是启动就报错了?还是只选择InstanceC的构造函数进行实例化?运行结果会告诉你:Spring最后只用了InstanceC的构造函数。这部分的具体过程如下:1.遍历按照优先级规则排序的构造函数。2.尝试查找构造函数中需要的bean是否都在Spring容器中一一存在。如果成功找到,则将构造函数标记为有效构造函数,并立即退出遍历。3.否则,记录异常,继续尝试使用下一个构造函数。4.当遍历完所有的构造函数,没有找到有效的构造函数时,抛出记录异常。5.使用有效的构造函数进行实例化。推导过程就到这里了,beanClass实例化了一个bean,接下来要做的就是给bean赋值,但是我们知道在Spring中可以赋值的对象不仅有@Autowired标记的属性,还有@Value、@Resource、@Inject等。为此,为了实现可伸缩性,Spring将获取注解标识的属性的过程与实际赋值的过程分开。这个过程在Spring中称为processingbeanDefinition。ProcessbeanDefinition功能说明:处理BeanDefinition的元数据信息。负责的角色:1.AutowiredAnnotationBeanPostProcessor:处理@Autowire、@Value、@Inject注解。2.CommonAnnotationBeanPostProcessor:处理@PostConstruct、@PreDestroy、@Resource注解。这两个后处理器的处理过程非常相似。下面以AutowiredAnnotationBeanPostProcessor为例:1、遍历beanClass(java中统称为Member)中的所有Field和Methods。2、判断Member是否标识了@Autowird、@Value、@Inject注解。3.如果是,将Member保存起来,封装到一个名为InjectionMetadata的类中。4.判断Member是否已经被解析。比如一个Member同时被@Autowired和@Resource注解,那么这个Member会被两个后处理器处理,导致重复保存。5.如果没有解析过,则将Member放入checked元素集合中,然后直接从这里获取所有待注入的Member,用于后续的属性填充。其中,AutowiredAnnotationBeanPostProcessor和InjectionMetadata的结构如下:同样,我们也将这部分流程添加到流程图中。现在,beanClass中所有可注入的属性都已经找出来了,接下来就是实际填充属性了。属性填充功能:在bean中填充需要自动组装的属性。作用:1.AutowiredAnnotationBeanPostProcessor。2.CommonAnnotationBeanPostProcessor。在前面的过程中,我们已经找到了所有需要自动组装的成员,所以这个过程非常简单。我们也以AutowiredAnnotationBeanPostProcessor为例。1.使用beanName作为键从缓存中获取InjectionMetadata。2.遍历InjectionMetadata中的checkedElements集合。3、取出Element中的Member,根据Member的类型在Spring中获取Bean。4.使用反射将获取到的Bean设置为属性。推导过程在Spring中,Bean也可以做一些属性填充后的初始化逻辑,比如Spring的线程池ThreadPoolTask??Executor填充属性后创建线程池逻辑,RedisTemplate设置默认值。Spring的初始化逻辑分为四部分:1.invokeAwareMethods:调用实现特定Aware接口的方法。2.applyBeanPostProcessorsBeforeInitialization:初始化前的处理。3.invokeInitMethods:调用初始化方法。4.applyBeanPostProcessorsAfterInitialization:初始化后处理。invokeAwareMethods的逻辑非常简单。我只是把源码贴出来给大家看看。privatevoidinvokeAwareMethods(StringbeanName,Objectbean){if(beaninstanceofAware){if(beaninstanceofBeanNameAware){((BeanNameAware)bean).setBeanName(beanName);}if(beaninstanceofBeanClassLoaderAware){ClassLoaderbcl=getBeanClassLoader();如果(bcl!=null){((BeanClassLoaderAware)bean).setBeanClassLoader(bcl);}}if(beaninstanceofBeanFactoryAware){((BeanFactoryAware)bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);}}}初始化前处理函数:调用初始化方法前的一些操作。作用:1.InitDestroyAnnotationBeanPostProcessor:处理@PostContrust注解。2、ApplicationContextAwareProcessor:处理Aware接口的一系列回调方法。这一步的功能不是很相关。完全根据用户自己的意愿来决定在初始化方法之前做什么。我们一个一个来了。1、这里InitDestroyAnnotationBeanPostProcessor的逻辑和属性填充过程很相似。属性填充过程是将自动组装相关的InjectionMetadata取出来进行处理,这一步是取出@PostContrust相关的Metadata进行处理。这个Metadata也是处理BeanDefinition过程解析缓存的。1.取出处理BeanDefinition流程解析的LifecycleMetadata。2.遍历LifecycleMetadata中的checkedInitMethods集合。3.使用反射调用。2、ApplicationContextAwareProcessor这一步和invokeAwareMethods类似,只不过是一些其他的Aware接口,代码也是直接贴出来:privatevoidinvokeAwareInterfaces(Objectbean){if(beaninstanceofEnvironmentAware){((EnvironmentAware)bean)。setEnvironment(this.applicationContext.getEnvironment());}if(beaninstanceofEmbeddedValueResolverAware){((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if(beaninstanceofResourceLoaderAware){((ResourceLoaderAware)bean).setResourceLoaderContext(this.app;}if(beaninstanceofApplicationEventPublisherAware){((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);}if(beaninstanceofMessageSourceAware){((MessageSourceAware)bean).setMessageSource(this.applicationContext);ApplicationContextAware){((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);}}Spring中的初始化方法初始化方法有两种:1.实现InitializingBean接口的afterPropertiesSet方法2.@Bean注解中的initMethod属性。调用顺序是先调用afterPropertiesSet,再调用initMethod。1、判断Bean是否实现了InitializingBean接口。2.如果是,将Bean转换为InitializingBean,调用afterPropertiesSet方法。3.判断BeanDefinition中是否有initMethod。4.如果有,找到对应的initMethod,通过反射调用。初始化后的处理是在Spring内置的后处理器中进行的。这一步只有ApplicationListenerDetector有对应的处理逻辑:将实现了ApplicationListener接口的bean添加到事件监听列表中。如果使用到Aop相关的函数,就会使用AbstractAutoProxyCreator,来创建代理对象。ApplicationListenerDetector的流程如下:1.判断Bean是否为ApplicationListener。2.如果是,将bean存放到applicationContext的listener列表中。补充流程图至此,Bean生命周期的主要部分已经介绍完毕,我们来补充流程图。还有其他逻辑。1.挂起创建bean的过程这个过程处于bean生命周期的最开始。作用:后处理器返回bean,达到暂停创建bean的效果。作用:无,在Spring内置的后处理器中,没有实现。bean的生命周期非常复杂。Spring允许你直接拦截,也就是在创建bean之前,自定义的后处理器直接返回一个bean给Spring,然后Spring会使用你给的bean,不会走bean的生命周期。过程。案例演示:@ComponentpublicclassCar{@AutowiredprivatePersonperson;publicvoidcheckPerson(){if(person==null){System.out.println("personisnull");}}}由于添加了@Autowired,所以正常情况下person不能为空,因为必须注入。现在我们自定义一个BeanPostProcessor用于拦截。@Componentpublic类InterruptBeanPostProcessor实现InstantiationAwareBeanPostProcessor{@OverridepublicObjectpostProcessBeforeInstantiation(Class>beanClass,StringbeanName)throwsBeansException{if("car".equals(beanName)){try{returnbeanInpitionClass.newInstance();|IllegalAccessExceptione){e.printStackTrace();}}返回空值;}}测试结果如下:2.预先缓存新实例化的对象。此步骤遵循bean的Spring实例化并缓存bean。解决循环依赖问题。这个过程暂时不列出来,在循环依赖一节中单独提出。3、暂停填充属性的操作和暂停创建bean的逻辑是一样的。Spring还允许您在填充属性之前进行拦截。在Spring的内置处理器中也没有这样的实现。实现方法是实现InstantiationAwareBeanPostProcessor接口,在postProcessAfterInstantiation方法中返回false。@ComponentpublicclassInterruptBeanPostProcessorimplementsInstantiationAwareBeanPostProcessor{@OverridepublicbooleanpostProcessAfterInstantiation(Objectbean,StringbeanName)throwsBeansException{if(beanName.equals("car")){返回假;在Spring中,不仅有创建bean时的@PostContrust、afterProperties、initMethod初始化方法,还有销毁bean时的@PreDestory、destroy、destroyMethod。所以在Bean生命周期的最后一步,Spring会在销毁集合中注册带有这些销毁方法的Bean,用于系统关闭时的回调。比如线程池的关闭,连接池的关闭,注册中心的注销都是通过它来实现的。完整流程图最后附上开头Bean生命周期的完整流程图,是不是清楚多了?
