【细品springboot源码】彻底弄懂spring bean的创建过程(上)

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


如果你觉得对bean的创建非常熟悉了,那么可以看一下实战篇【追根究底】 为什么@Transactional注解失效了?【追根究底】@Lazy注解为什么会失效?【追根究底】使用@Lazy注解为什么会产生两层代理?



  • JavaBean的前世今生
    第一块内容,思考了很久,自己的见解实在有限,不能在这么短的篇幅,将它的前世今生(POJO、JAVABEAN、EJB)讲清楚。但是不去了解这些历史,又心有不甘,所以还是选择站在前辈们的肩膀上,做一次总结。以出现的时间线来说

    • JavaBean

      JavaBean 是一种JAVA语言写成的可重用组件。为写成JavaBean,类必须是具体的和公共的,并且具有无参数的构造器。JavaBean 通过提供符合一致性设计模式的公共方法将内部域暴露成员属性,set和get方法获取。-- 百度百科

    • Enterprise Java Bean(EJB)
      可以看EJB到底是什么,真的那么神秘吗??这篇文章。读完这篇文章后,我感觉我懂了,但是再看评论,我又模糊了。历史的事情,你要是没经历过,真的婆说婆有理,公说公有理,完全搞不懂……,算了,让它随风而去吧。
    • Plain Ordinary Java Object(POJO)

      使用POJO名称是为了避免和EJB混淆起来, 而且简称比较直接. 其中有一些属性及其getter setter方法的类,没有业务逻辑,有时可以作为VO(value -object)或dto(Data Transform Object)来使用.当然,如果你有一个简单的运算属性也是可以的,但不允许有业务方法,也不能携带有connection之类的方法。 – 百度百科

    单纯的了解感觉还行,但是一和实际开发结合起来,就又模糊了,这玩意真的是……,我还是选择破立而生,重新开始吧……,各位看官可忽略这一段吐槽。

  • Spring Bean的前世今生
    突然觉得这个的历史可比JavaBean简单多了,我也找到一篇讲得比较好的文章为什么要有Spring?,可以作为简单了解。其实真正想去深入了解,还得去看书,单单是看网上的博客,只能是片面的知识。

  • bean创建的地方?
    好了,终于到源码剖析的部分了,尽情享受吧!
    代码,就简单的三个类

    @Component
    public class A {
        
        @Autowired
        B b;
    }	
    
    @Component
    public class B {
    }
    	
    @Aspect
    @Component
    public class C {
        @Pointcut("execution(public * com.acme.lazydemo.A.*(..))")
        public void myAnnotationPointcut(){
        }
    
        @Around("myAnnotationPointcut()")
        public void around(JoinPoint joinPoint){
        }
    }	
    
    • 第一个创建bean的地方
      将断点打到AbstractBeanFactory->getBean中(有三个,都打上),debug运行,马上就会到这里。
      在这里插入图片描述
      接着,如何找到是哪里调用getBean的呢?可以看到左下角有个栈,我们可以回溯回去。发现在prepareContext这里的时候就有bean的创建了。这是系统的bean,我们现在不需要管。
    • 第二个创建bean的地方
      继续往下运行,发现第二个创建bean的地方invokeBeanFactoryPostProcessors,这个创建bean工厂后置处理器的地方,目前也不需要管。
      在这里插入图片描述
      但是值得一说的是,如果你自己实现了一个BeanFactoryPostProcessor,那么也会在这个地方被创建。例如
      @Component
      public class D implements BeanFactoryPostProcessor{
          
          @Override
          public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
      
          }
      }
      
      在这里插入图片描述
    • 第三个创建bean的地方
      接着继续运行,发现第三个创建bean的地方registerBeanPostProcessors,这里是创建bean后置处理器的地方,和第二个同理,只要实现了BeanPostProcessorbean都会在这里创建。
      在这里插入图片描述
    • 第四个创建bean的地方
      接着继续运行,发现第四个创建bean的地方preInstantiateSingletons,这里就是创建bean的最后一个地方,也是最集中的一个地方,所有非懒加载的bean都在这里创建了。
      在这里插入图片描述
      可以看一下这个方法的源码,看注释可以知道,这个地方是实例化所有非懒加载的单例。
      @Override
      public void preInstantiateSingletons() throws BeansException {
      	……
      	// Iterate over a copy to allow for init methods which in turn register new bean definitions.
      	// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
      	List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
      
          // 实例化所有非懒加载的单例
      	// Trigger initialization of all non-lazy singleton beans...
      	for (String beanName : beanNames) {
      	    //合并bean的定义
      		RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); 
      		if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      		    //先判断是否是FactoryBean
      			if (isFactoryBean(beanName)) {
      			    //拿到FactoryBean本身的实例
      				Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
      				……
      				if (isEagerInit) {
      				    //如果需要提前创建,那么就通过不传&,拿到真实的bean,也就是FactoryBean中的getObject方法返回的对象
      					getBean(beanName);
      				}
      			}
      			else {
      			    //普通Bean创建
      				getBean(beanName);
      			}
      		}
      	}
      
      	// Trigger post-initialization callback for all applicable beans...
      	……
      }	
      
  • 剖析创建Bean的源码
    代码入口是preInstantiateSingletons方法,这里不重复贴出来。

    • 为什么要有getMergedLocalBeanDefinition这个方法?
      可以看到,BeanDefinition的实现类有很多,所以getMergedLocalBeanDefinition方法只是将它们的定义合并,并没有做了什么高大上的事情。在这里插入图片描述

    • FactoryBean为什么会单独处理?
      这里可以加个类来测试一下

      @Component
      public class F implements FactoryBean<E> {
          @Override
          public E getObject() throws Exception {
              return new E();
          }
      
          @Override
          public Class<E> getObjectType() {
              return E.class;
          }
      }
      

      接下来,断点跟踪一下,可以看到,返回的是E@3589这个对象。

      注:FactoryBean的作用就是,告诉spring,从我这里拿bean。也就是从getObject方法中返回的对象。

      在这里插入图片描述
      然后跳过所有,运行到测试的地方,可以看到,实际返回的E@4023这个对象。这就证明了,FactoryBean返回的实际上是getObject创建的对象。

      注:FactoryBean不能被切,如果被AOP了,那么就拿不到getObject中创建的对象,这是实测结果。所以开头的C类,只切了A这个类。

      在这里插入图片描述
      所以在这里单独处理就是因为这个原因,可以看到代码中先判断是否是FactoryBean,然后拿到FactoryBean本身的实例,再判断是否需要提前实例化,然后通过getBean拿到getObject中的对象。

    • 普通bean的创建过程一
      这里就拿bean A的创建演示。重新启动,断点继续下到preInstantiateSingletons中,如图
      在这里插入图片描述
      接着继续跟进去,getBean->doGetBean,说到这里,已经基本到高潮部分了,部分精彩剧情都在这里。
      在这里插入图片描述
      因为这个方法的代码实在太长了,所以把这个代码主要关注部分贴出来慢慢解释就行,我会进行标号,对着标号说。

      protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      	@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
          //1.获取真实的BeanName
      	final String beanName = transformedBeanName(name);
      	Object bean;
        
          //2.尝试从缓存中获取单例
      	// Eagerly check singleton cache for manually registered singletons.
      	Object sharedInstance = getSingleton(beanName);
      	if (sharedInstance != null && args == null) {
      		……
      		//3.这一步是为了类似FactoryBean这种特殊的Bean而做的处理
      		//由之前的分析可以知道,sharedInstance可能是FactoryBean本身,并不是getObject方法返回的对象,所以需要拿到真实的对象
      		bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
      	}
      
      	else {
      	    //4.只有在单例情况才会尝试解决循环依赖,原型模式情况下是不支持的
      		// Fail if we're already creating this bean instance:
      		// We're assumably within a circular reference.
      		if (isPrototypeCurrentlyInCreation(beanName)) {
      			throw new BeanCurrentlyInCreationException(beanName);
      		}
             
              //5.如果beanDefinitionMap中也就是在所有已经加载的类中不包括beanName则尝试从parentBeanFactory中查找
      		// Check if bean definition exists in this factory.
      		BeanFactory parentBeanFactory = getParentBeanFactory();
      		if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      			……
      		}
              //6.类型检查不需要标记该bean已创建
      		if (!typeCheckOnly) {
      			markBeanAsCreated(beanName);
      		}
      
      		try {
      			……
      			//7.这里就是@DependsOn注解的实现之处,如果A必须依赖于B才能创建,那
      			//么必须先创建B,不能使用@DependsOn造成显示的循环依赖,直接报错。
      			//例如A中@DependsOn("b"),B中@DependsOn("a")
      			// Guarantee initialization of beans that the current bean depends on.
      			String[] dependsOn = mbd.getDependsOn();
      			if (dependsOn != null) {
      				for (String dep : dependsOn) {
      					if (isDependent(beanName, dep)) {
      						……
      					}
      					registerDependentBean(dep, beanName);
      					try {
      						getBean(dep);
      					}
      					catch (NoSuchBeanDefinitionException ex) {
      						……
      					}
      				}
      			}
      			
      			//8.单例的创建
      			// Create bean instance.
      			if (mbd.isSingleton()) {
      				sharedInstance = getSingleton(beanName, () -> {
      					try {
      						return createBean(beanName, mbd, args);
      					}
      					catch (BeansException ex) {
      						……
      					}
      				});
      				bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      			}
                  //9.原型bean创建
      			else if (mbd.isPrototype()) {
      				// It's a prototype -> create a new instance.
      				Object prototypeInstance = null;
      				try {
      					beforePrototypeCreation(beanName);
      					prototypeInstance = createBean(beanName, mbd, args);
      				}
      				finally {
      					afterPrototypeCreation(beanName);
      				}
      				bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      			}
                   //10.指定的scope上实例化bean
      			else {
      				String scopeName = mbd.getScope();
      				final Scope scope = this.scopes.get(scopeName);
      				……
      			}
      	}
      
      	//11.检查需要的类型是否符合bean的实际类型,如果不符合,会尝试使用类型转换器转换
      	// Check if required type matches the type of the actual bean instance.
      	if (requiredType != null && !requiredType.isInstance(bean)) {
      		try {
      			T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
      			……
      			return convertedBean;
      		}
      		catch (TypeMismatchException ex) {
      			……
      		}
      	}
      	return (T) bean;
      }
      
    1. 获取真实的BeanName
      为什么会有这一步呢?前面说过,FactoryBean本身创建的时候,传的是&BeanName,那么就需要通过这个,拿到真实的BeanName。还有一种情况是,当为Bean起别名到时候,也需要通过这个拿到真实的BeanName
    2. 尝试从缓存中获取单例
      为什么会有这一步呢?第一个是为了解决循环依赖,这个很好理解。第二个是为了不重复创建,比如A依赖了BC也依赖了B,在创建A的时候,已经把B创建好了,所以,创建C的时候直接从缓存拿不是更高效吗?
    3. 拿到真实的bean
      为什么会有这一步呢?前面说FactoryBean的时候,我们其实想要的beangetObject返回的那一个,而不是FactoryBean本身,所以这里需要再处理一下。这里如果还不懂的,自己写一个测试例子,跟进去看一下就知道了(别忘了,例子我已经在上面给出啦)。
    4. 原型模式循环依赖检测
      原型模式不支持循环依赖检测,因为原型bean是不会放在缓存里的,就没有提前暴露引用这么一说,所以肯定是会报错的。
    5. 尝试从parentBeanFactory中查找bean定义
      这个说实话,我没接触过这类case,所以这个跳过了,不说了。
    6. 是否是类型检查
      这个我不是很理解,是不是类型检查,不是都接着往下走,创建bean了吗?可能水平有限,没找到对应的源码吧。
    7. @DependsOn注解的实现
      没看源码之前,我也没想到这个注解居然实现如此简单,就是一个判断,如果某个类型有这个注解,然后解析注解中的类,并先创建它们,之后再回来继续创建自己。值得注意的是,不能使用@DependsOn造成显示的循环依赖。类似下面这样。
      @DependsOn("b")
      @Component
      public class A {
      }
      
      @DependsOn("a")
      @Component
      public class B {
      }
      
    8. 单例的创建
      这个是重点,下面会单独讲。
    9. 原型bean创建
      这个用得较少,跳过。(实际项目中没用过,也没研究过,所以不献丑了)
    10. 指定的scope上创建bean
      同上
    11. 是否需要类型转换
      引用书上的解释,这个我实际也没怎么遇到过

      通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的 bean 其实是个 String,但是 requiredType 却传入 Integer类型,那么这时候本步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型。当然,String转换为Integer是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。-- spring源码深度解析

    • 普通bean的创建过程二
      啊,感觉路才刚刚走了1/5,这bean的创建过程真的是太复杂了……,站起来,继续撸!接下来,看看单例的创建,方法是createBean,同样,将需要关注部分的源码贴出来,标号,对着说
      在这里插入图片描述
      @Override
      protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
      		throws BeanCreationException {
      	RootBeanDefinition mbdToUse = mbd;
          
          //1. 解析`beanClass`
      	// Make sure bean class is actually resolved at this point, and
      	// clone the bean definition in case of a dynamically resolved Class
      	// which cannot be stored in the shared merged bean definition.
      	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
      	if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      		mbdToUse = new RootBeanDefinition(mbd);
      		mbdToUse.setBeanClass(resolvedClass);
      	}
      
      	// Prepare method overrides.
      	try {
      	   //2.对override属性进行标记及验证
      		mbdToUse.prepareMethodOverrides();
      	}
      	catch (BeanDefinitionValidationException ex) {
      		……
      	}
      
      	try {
      	    //3.给BeanPostProcessor改变bean的机会
      		// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
      		Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
      		if (bean != null) {
      			return bean;
      		}
      	}
      	catch (Throwable ex) {
      		……
      	}
      
      	try {
      	    //4.开始创建bean
      		Object beanInstance = doCreateBean(beanName, mbdToUse, args);
      		return beanInstance;
      	}
      	catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
      		……
      	}
      	catch (Throwable ex) {
      		……
      	}
      }
      
      1. 解析beanClass
        这里我其实也不是很懂,按照英文注释翻译过来就是,有些bean的定义是动态的,不会存在mdb里面,所以这里需要尝试进行解析。因为不知道case,所以也演示不了。

      2. override属性进行标记及验证
        这个我也没用过,书里倒是解释了一番,这里引用一下

        很多读者可能会不知道这个方法的作用,因为在 Spring 的配置里面根本就没有诸如override-method之类的配置,那么这个方法到底是干什么用的呢?
        其实在 Spring 中确实没有 override-method 这样的配置,但是如果读过前面的部分,可能会有所发现,在Spring配置中是存在lookup-method和replace-method的,而这两个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性里,而这个函数的操作其实也就是针对于这两个配置的。-- spring源码深度解析

        书中花了一大段来讲prepareMethodOverrides的源码,最后得出了这么一个结论。所以感觉这一部分还是挺有意思的,但可惜没研究过,所以这里就不展开了。

        但是,这里要提到的是,对于方法的匹配来讲,如果一个类中存在若干个重载方法,那么,在函数调用及增强的时候还需要根据参数类型进行匹配,来最终确认当前调用的到底是哪个函数。但是,Spring将一部分匹配工作在这里完成了,如果当前类中的方法只有一个,那么就设置重载该方法没有被重载,这样在后续调用的时候便可以直接使用找到的方法,而不需要进行方法的参数匹配验证了,而且还可以提前对方法存在性进行验证,正可谓一箭双雕。-- spring源码深度解析

      3. 给BeanPostProcessor改变bean的机会
        这个就好理解了,不理解的,推荐看一下springboot之BeanPostProcessor功能及例子(一)这篇文章,就马上明白了。
        resolveBeforeInstantiation方法里还有一些好玩的地方,但是碍于篇幅,以及不是主线剧情,我就不展开讲它了。

      4. 开始创建bean
        这个才是主菜,但是碍于篇幅,还是扔到下篇再讲吧,如果精简了,就太没意思了。


==>【细品springboot源码】彻底弄懂spring bean的创建过程(下)

  • 2
    点赞
  • 13
    收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:Age of Ai 设计师:meimeiellie 返回首页
评论 4

打赏作者

_acme_

试着玩,各位看官随意

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值