之前咱们曾经剖析SpringBoot在run办法时,它会执行的refresh()容器的操作。
在SpringBoot中,refresh()有十几个办法,每个办法的大重要水平是不一样的,咱们通过抓大放小的形式,剖析解决上图3个外围逻辑。
并且曾经钻研完了invokeBeanFactoryPostProcessors和onRefresh的逻辑,剖析它们的原理和设计思维。
之前次要剖析的:
原理有对SpringBoot的主动拆卸配置如何做到的、第三方技术如何进行扩大的、tomcat如何启动的
设计思维有SpringBoot扩大接口设计、有对Tomcat组件的扩大设计、Spring容器形象思维的设计、SpringBoot和第三方技术整合的扩大设计等等。
refresh()还有一个十分要害的操作,就是bean的实例化,明天咱们就来看下refresh最初一个办法—finishBeanFactoryInitialization。
看看它如何执行Bean实例化的流程和设计的。
finishBeanFactoryInitialization之前和之后的操作详情
能够看到,bean的实例化前后,还是做了一些事件的,次要执行的是一些扩大点,比方listener的扩大点执行、LifycycleProcessor的执行。
这一节咱们外围关系的是bean创立流程和设计,所以咱们抓大放小,过就能够。间接来看上面创立bean吧。
preInstantiateSingletons办法的外围脉络
其实bean的实例化大家或多或少都晓得一些。所以我不会特地具体的每一个办法都带大家看。
咱们还是本着先脉络后细节,最初思考的思维来剖析Bean的实例化,当你用这种办法剖析玩后,和你之前剖析对吧下有什么区别,能够感触下。
如果之后大家有诉求须要精读Bean实例化的逻辑,我之后能够思考放在Spring成长记中为大家认真带来Bean实例化的剖析。
这里咱们次要过下它的外围源码就能够了。
因为SpringBoot是为了更好的应用Spring,它是基于Spring的。如果你懂Spring实例化,这块其实十分好了解的。
让咱们来看下吧!
finishBeanFactoryInitialization次要的代码如下所示:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { // Initialize conversion service for this context. if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) && beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) { beanFactory.setConversionService( beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)); } // Register a default embedded value resolver if no bean post-processor // (such as a PropertyPlaceholderConfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanFactory.hasEmbeddedValueResolver()) { beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal)); } // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early. String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false); for (String weaverAwareName : weaverAwareNames) { getBean(weaverAwareName); } // Stop using the temporary ClassLoader for type matching. beanFactory.setTempClassLoader(null); // Allow for caching all bean definition metadata, not expecting further changes. beanFactory.freezeConfiguration(); // Instantiate all remaining (non-lazy-init) singletons. beanFactory.preInstantiateSingletons(); }
这个办法的脉络其实比较清楚,其实最要害的只有一句话:
// Instantiate all remaining (non-lazy-init) singletons.beanFactory.preInstantiateSingletons();
其余的都是给beanFactory补充点货色而已,不是很要害。
这句话从正文很分明的说了,是依据BeanDefinition实例化所有残余的单例非提早初始化的bean。
整个办法我通过先脉络的思维,给大家概括了下:
public void preInstantiateSingletons() throws BeansException { List<String> beanNames = new ArrayList<>(this.beanDefinitionNames); //遍历所有beanDefinition,基于beanDefinition创立bean for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); if (isFactoryBean(beanName)) { //做一些解决FactoryBean //再从容器中获取bean,如果不存在就创立 getBean(beanName); }else{ //从容器中获取bean,如果不存在就创立 getBean(beanName); } } // 执行Bean的扩大操作 for (String beanName : beanNames) { Object singletonInstance = getSingleton(beanName); if (singletonInstance instanceof SmartInitializingSingleton) { smartSingleton.afterSingletonsInstantiated(); } } }
下面的代码是我对源码精简后的逻辑,它的脉络十分清晰了整体如下图所示:
创立bean的外围流程
当你晓得了preInstantiateSingletons办法的外围脉络后,它次要触发的是getBean办法,之后触发了doGetBean。
doGetBean整个办法还是比较复杂的,我还是通过先脉络的思维,抓大放小后,给大家精简了下源码,精简后如下:
@Overrid public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { final String beanName = transformedBeanName(name); Object bean; //尝试获取bean,如果容器中有了,就不须要创立了 Object bean = getSingleton(beanName); if(bean == nul){ //getFromParentBeanfacotry 以后容器没有bean对应的单例对象,尝试从父容器获取,如果父容器为空,则不做解决 //默认父容器空,这里略过 //以后bean的依赖dependsOn解决,递归调用getBean final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { getBean(dep); } // 单例还是多例的形式创立bean if (mbd.isSingleton()) { bean = createBean(beanName, mbd, args); }else if (mbd.isPrototype()) { bean = createBean(beanName, mbd, args); }else { String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); bean = createBean(beanName, mbd, args); } } //bean的扩大,应用转换器解决bean T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); return (T) bean;}
doGetBean整体代码办法通过,抓大放小,剖析脉络后,其实曾经很清晰了。次要做了这么几件事:
1)getSingleton从容器Beanfactory中的map属性,获取bean,如果非空,间接就能够应用
2)如果容器中没有这个bean,通过判断是否单例,来执行对应的创立bean办法createBean
3)如果以后bean的依赖dependsOn解决,递归调用getBean
4)最初执行了bean的扩大,应用转换器解决bean
doGetBean办法的大体脉络,基本上就是这四步,如下图所示:
真正创立bean的逻辑,到当初咱们还是没有看到,须要持续向下找。doGetBean之后上面就会执行createBean
同理咱们应用之前的办法持续梳理脉络、抓大放小失去如下代码:
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {、 //Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance;}
这里createBean其实次要执行了resolveBeforeInstantiation和doCreateBean办法。
1)resolveBeforeInstantiation办法从注解上看,是给动静代理创立一个对象的机会,也就说,能够通过BeanPostProcessor应用动静代理对某些bean间接进行创立。这个十分有意思,也很要害,你想想是不是有的技术就是利用这里进行创立的呢?
2)如果不满足第一个条件,就会应用doCreateBean来创立Bean
整个逻辑如下图所示:
这里终于找到了一种bean创立的形式了,之后应该还有其余形式,比方反射。咱们持续来看doCreateBean。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; //创立bean,bean的实例化 if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } //bean属性的填充 populateBean(beanName, mbd, instanceWrapper); //bean 扩大点的触发 initializeBean(beanName, exposedObject, mbd); //bean 扩大点的增加 registerDisposableBeanIfNecessary(beanName, bean, mbd); return exposedObject;}
整个doCreateBean办法,通过咱们之前的思路,一样精简完后,脉络也很分明。次要有:
1)创立bean,bean的实例化
2)bean属性的解决
3)bean 扩大点的触发
4)bean 扩大点的增加
doCreateBean的脉络如下图所示:
这里能够看到,除了之前动静代理的截胡,终于找到了bean实例化,创立bean的中央了。
其余对bean属性解决和扩大点,咱们先不看。重点钻研分明bean的创立再说。
createBeanInstance同样被咱们抓大放小后的代码如下:
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. Class<?> beanClass = resolveBeanClass(mbd, beanName); //instanceSupplier形式创立bean Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } //FactoryMethod形式创立bean if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } //反射的形式创立bean if (resolved) { if (autowireNecessary) { return autowireConstructor(beanName, mbd, null, null); } else { return instantiateBean(beanName, mbd); } } return instantiateBean(beanName, mbd);}
你会发现有多种创立的bean的形式,并不是只有反射,形式次要有:
1)instanceSupplier形式创立bean
2)FactoryMethod形式创立bean
3)反射的形式创立bean
那么再算上之前动静代理创立bean、Factorybean创立的bean。一共曾经有5种能够创立bean的形式, 然而个别咱们还都是通过反射创立的居多。
你可能没有见过其余的形式创立的bean,然而我在一些技术中也见过一些,给大家分享下:
比方
Shiro框架应用Factorybean创立的就比拟多
动静代理创立bean、dubbo就有很多这么创立的
FactoryMethod创立的bean,SpringBoot主动拆卸的时候有时候会用到,之前tomcat启动的时候,TomcatServletWebServerFactory是不是就用到了。
instanceSupplier是Spring5之后才有的,目前我还没有见到啥框架用到过....
好了不论如何,最终你取得了如下的一张图,总结bean的创立形式:
到这里bean 的创立的流程,咱们就大体剖析完了,因为咱们不是剖析Spring,所以就不会再对这外面的每个细节进行剖析了。
之后有机会出Spring成长记的时候,我能够在带大家详细分析吧。
只是相熟SpringBoot中,Spring实例化bean的流程理解到这里根本就能够了。
创立的bean整个流程能够总结下图的几步:
Bean实例化的扩大点设计
最初咱们来看下Bean扩大设计吧,这个其实网上都有一大堆了,然而你肯定要留神,你得会辨别优劣的文章、提炼关键点,要对这些有本人的思考才行。
就像我当初给大家分享的,就是我对扩大点的思考。这个是我始终给大家强调的。
好了,我来简略说下,我对Bean的扩大点设计的思考和了解吧。
在Spring中,Bean实例化的时候,有很多扩大点,这些扩大点其实还是很要害的。
比方:在Spring的生态系统中,很多技术都是通过Bean的扩大点来实现的。而且包含第三方的技术,比方bytetcc分布式事物框架的实现原理和Bean扩大点BeanPostProcessor就有很大的关系、大企业自研框架,能够实现自定义注解的解决、自定义配置文件的解决、给本人开发的bean设置属性等等。
那Bean的扩大点设计了哪些呢?我给大家花了一个图,根本就能详情常见的扩大点了,当然可能还有一些其余的扩大点,不论有多少个,它们都是扩大点,正当利用就好了 ,这个是要害。
Bean实例化时,常见的扩大点的设计如图所示:
小结
如果你去看Bean的实例化的整个流程,其实其中的细节很简单的,如果在简单中找到关键点,是SpringBoot成长记以来,始终想要教给大家的。
最初通过对Bean实例化的剖析,让大家纯熟的利用了之前的学到的先脉络后细节、抓大放小、连蒙带猜、画外围组件图、流程图、看正文等思维和办法。
而且每看一阵子逻辑,要对它做出思考,思考它的设计,它的扩大、它的思维理念等等。
这个是罕用的一套方法论,可能不适宜所有场景,然而大多状况能够让你浏览源码或者钻研技术原理的时候,不那么不安,不会感觉它们太难,能够让你有方向、有办法。
明天的内容,其实没有什么要总结的,比拟重要的就是最初bean 的扩大设计,我就不在反复了。
好了,咱们下一节再见!
本文由博客一文多发平台 OpenWrite 公布!