SpringBoot成长记10:Bean实例化的流程和设计

file

之前我们已经分析SpringBoot在run方法时,它会执行的refresh()容器的操作。

file

在SpringBoot中,refresh()有十几个方法,每个方法的大重要程度是不一样的,我们通过抓大放小的方式,分析处理上图3个核心逻辑。

并且已经研究完了invokeBeanFactoryPostProcessors和onRefresh的逻辑,分析它们的原理和设计思想。

之前主要分析的:

原理有对SpringBoot的自动装配配置如何做到的、第三方技术如何进行扩展的、tomcat如何启动的

设计思想有SpringBoot扩展接口设计、有对Tomcat组件的扩展设计、Spring容器抽象思想的设计、SpringBoot和第三方技术整合的扩展设计等等。

refresh()还有一个非常关键的操作,就是bean的实例化,今天我们就来看下refresh最后一个方法—finishBeanFactoryInitialization。

看看它如何执行Bean实例化的流程和设计的。

finishBeanFactoryInitialization之前和之后的操作概况

file

可以看到,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();
		
			}
		}
	}

上面的代码是我对源码精简后的逻辑,它的脉络非常清晰了整体如下图所示:

file

创建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方法的大体脉络,基本上就是这四步,如下图所示:

file

真正创建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

整个逻辑如下图所示:

file

这里终于找到了一种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的脉络如下图所示:

file

这里可以看到,除了之前动态代理的截胡,终于找到了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的创建方式:

file

到这里bean 的创建的流程,我们就大体分析完了,由于我们不是分析Spring,所以就不会再对这里面的每个细节进行分析了。

之后有机会出Spring成长记的时候,我可以在带大家详细分析吧。

只是熟悉SpringBoot中,Spring实例化bean的流程了解到这里基本就可以了。

创建的bean整个流程可以总结下图的几步:

file

Bean实例化的扩展点设计

最后我们来看下Bean扩展设计吧,这个其实网上都有一大堆了,但是你一定要注意,你得会区分优劣的文章、提炼关键点,要对这些有自己的思考才行。

就像我现在给大家分享的,就是我对扩展点的思考。这个是我一直给大家强调的。

好了,我来简单说下,我对Bean的扩展点设计的思考和理解吧。

在Spring中,Bean实例化的时候,有很多扩展点,这些扩展点其实还是很关键的。

比如:在Spring的生态系统中,很多技术都是通过Bean的扩展点来实现的。而且包括第三方的技术,比如bytetcc分布式事物框架的实现原理和Bean扩展点BeanPostProcessor就有很大的关系、大企业自研框架,可以实现自定义注解的处理、自定义配置文件的处理、给自己开发的bean设置属性等等。

那Bean的扩展点设计了哪些呢?我给大家花了一个图,基本就能概况常见的扩展点了,当然可能还有一些其他的扩展点,不管有多少个,它们都是扩展点,合理利用就好了 ,这个是关键。

Bean实例化时,常见的扩展点的设计如图所示:

file

小结

如果你去看Bean的实例化的整个流程,其实其中的细节很复杂的,如果在复杂中找到关键点,是SpringBoot成长记以来,一直想要教给大家的。

最后通过对Bean实例化的分析,让大家熟练的应用了之前的学到的先脉络后细节、抓大放小、连蒙带猜、画核心组件图、流程图、看注释等思想和方法。

而且每看一阵子逻辑,要对它做出思考,思考它的设计,它的扩展、它的思想理念等等。

这个是常用的一套方法论,可能不适合所有场景,但是大多情况可以让你阅读源码或者研究技术原理的时候,不那么不安,不会觉得它们太难,可以让你有方向、有方法。

今天的内容,其实没有什么要总结的,比较重要的就是最后bean 的扩展设计,我就不在重复了。

好了,我们下一节再见!

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

posted @ 2021-10-03 23:02  _繁茂  阅读(374)  评论(0编辑  收藏  举报