IOC容器就像是一个工厂,里面有很多流水线生产出一个个产品(bean)。bean的加载流程大概分为:

  • 容器启动阶段
  • bean加载阶段

容器启动阶段:

1.配置元信息
当你生产物品的时候总得知道产品得规格型号信息,元信息就是这个意思。来源于XML文件,注解等。

2.BeanDefination:
相当于java中得pojo,当元信息被加载到内存以后就是以BeanDefination的形式存在。

3、BeanDefinationReader:如果我们要读取xml配置元信息,那么可以使用XmlBeanDefinationReader。如果我们要读取properties配置文件,那么可以使用PropertiesBeanDefinitionReader加载。而如果我们要读取注解配置元信息,那么可以使用 AnnotatedBeanDefinitionReader加载。我们也可以很方便的自定义BeanDefinationReader来自己控制配置元信息的加载。总的来说,BeanDefinationReader的作用就是加载配置元信息,并将其转化为内存形式的BeanDefination,并存在BeanDefinationRegistry中。通过键值对的方式,键是通过特定的bean定义的id,映射到相应的BeanDefination。

4、BeanDefinationRegistry:

执行到这里,Spring已经将存在于各处的配置元信息加载到内存,并转化为BeanDefination的形式,这样我们需要创建某一个对象实例的时候,找到相应的BeanDefination然后创建对象即可。那么我们需要某一个对象的时候,去哪里找到对应的BeanDefination呢?这种通过Bean定义的id找到对象的BeanDefination的对应关系或者说映射关系又是如何保存的呢?这就引出了BeanDefinationRegistry了。

Spring通过BeanDefinationReader将配置元信息加载到内存生成相应的BeanDefination之后,就将其注册到BeanDefinationRegistry中,BeanDefinationRegistry就是一个存放BeanDefination的大篮子,它也是一种键值对的形式,通过特定的Bean定义的id,映射到相应的BeanDefination。

5、BeanFactoryPostProcessor:

BeanFactoryPostProcessor是容器启动阶段Spring提供的一个扩展点,主要负责对注册到BeanDefinationRegistry中的一个个的BeanDefination进行一定程度上的修改与替换。例如我们的配置元信息中有些可能会修改的配置信息散落到各处,不够灵活,修改相应配置的时候比较麻烦,这时我们可以使用占位符的方式来配置。例如配置Jdbc的DataSource连接的时候可以这样配置:

<bean id="dataSource"  class="org.apache.commons.dbcp.BasicDataSource"  destroy-method="close">  <property name="maxIdle" value="${jdbc.maxIdle}"></property>  <property name="maxActive" value="${jdbc.maxActive}"></property>  <property name="maxWait" value="${jdbc.maxWait}"></property>  <property name="minIdle" value="${jdbc.minIdle}"></property>  <property name="driverClassName"  value="${jdbc.driverClassName}">  </property>  <property name="url" value="${jdbc.url}"></property>  <property name="username" value="${jdbc.username}"></property>  <property name="password" value="${jdbc.password}"></property>
</bean>

BeanFactoryPostProcessor就会对注册到BeanDefinationRegistry中的BeanDefination做最后的修改,替换$占位符为配置文件中的真实的数据。

二、Bean的获取阶段
在容器的启动阶段就完成了bean的注册,以BeanDefination储存在BeanDefinationRegister中,如果不是选择懒加载(BeanFactory是懒加载)的话,在容器启动完成后执行finishBeanFactoryInitialization()方法,隐式的调用所有对象的getBean()方法实例化所有配置的Bean,完成类的加载。
1.doGetBean():bean的获取
doGetBean的方法就是创建Bean之前,先去缓存中或BeanFactory工厂中查看是否存在bean,如果存在,则返回,不存在则进行相应的创建流程。

  1. 检查是不是别名,需要进行解析转换
  2. 尝试从缓存中加载实例,如果获取直接返回,在缓存中获取的bean是原始bean还需要进行实例化,如合并RootBeandefination、BeanPostProcessor进行实例前置处理,实例化,实例后置处理。
  3. 如果缓存中没有对应的bean,则先进行依赖循环检查,存在则报错,如果缓存中没有数据,就会转到父类加工厂(有点像双亲委任制),如果当前的xml文件不报换beanname所对应的配置,就只能到父类工厂中尝试加载
  4. 存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition,即为合并父类定义。
  5. 初始化依赖的bean,即进行被依赖bean的实例化初始化流程。
  6. 创建bean,默认是单例
protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {/*1、转换beanName(别名转换):传入的参数name可能只是别名,也可能是FactoryBean,所以需要进行解析转换:(1)消除修饰符,比如工厂引用前缀 String FACTORY_BEAN_PREFIX = "&";(2)解决spring中alias标签的别名问题*/ final String beanName = transformedBeanName(name);Object bean;//2、尝试从缓存中去加载实例,如果获取到了就直接返回// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);//如果缓存中存在对应的beanif (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}//3、缓存渠道的bean的实例化。从缓存中获取的bean是原始状态的bean,需要在这里对bean进行bean实例化。// 此时会进行 合并RootBeanDefinition、BeanPostProcessor进行实例前置处理、实例化、实例后置处理。bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}// 如果缓存中没有对应beanelse { //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、如果缓存中没有数据,就会转到父类工厂去加载//获取父工厂// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();//!containsBeanDefinition(beanName)就是检测如果当前加载的xml配置文件中不包含beanName所对应的配置,就只能到parentBeanFacotory去尝试加载bean。if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}//6、存储XML配置文件的GernericBeanDefinition转换成RootBeanDefinition,即为合并父类定义。try {// XML配置文件中读取到的bean信息是存储在GernericBeanDefinition中的,但Bean的后续处理是针对于RootBeanDefinition的,所以需要转换后才能进行后续操作。final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.//7、初始化依赖的beanString[] dependsOn = mbd.getDependsOn();//bean中可能依赖了其他bean属性,在初始化bean之前会先初始化这个bean所依赖的bean属性。if (dependsOn != null) {for (String dependsOnBean : dependsOn) {if (isDependent(beanName, dependsOnBean)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");}registerDependentBean(dependsOnBean, beanName);getBean(dependsOnBean);}}//8、创建bean// Create bean instance.if (mbd.isSingleton()) {sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {try {return createBean(beanName, mbd, args);}catch (BeansException ex) {// Explicitly remove instance from singleton cache: It might have been put there// eagerly by the creation process, to allow for circular reference resolution.// Also remove any beans that received a temporary reference to the bean.destroySingleton(beanName);throw ex;}}});bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);}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);}else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type [" +ClassUtils.getQualifiedName(requiredType) + "]", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}

2.在缓存中获取单例bean 调用getSingleton(beanname,allowearlyReference)方法
1).从一级缓存中获取,若为空,则检查是否在创建中
2).检查的时候要加锁,防止并发创建
3).从二级bean中获取bean,如果正在加载,则不处理,否则调用addSingletonFactory 方法将对应的objectFactory 初始化策略存储在 singletonFactories,也就是三级缓存中,三级缓存存储的也是早期bean。在解决循环依赖中,三级缓存是可以对早期的类对象进行自定义处理,将处理完的对象,放在二级缓存中,若还有循环依赖的处理,拿的是二级循环中的对象。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {// 从缓存池中获取bean:singletonObjects 一级缓Object singletonObject = this.singletonObjects.get(beanName);// 如果一级缓存中为null,再判断是否正在创建if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {// 加锁,防止并发创建synchronized (this.singletonObjects) {// 从二级缓存中获取bean,如果此 bean 正在加载则不处理singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {// 当某些方法需要提前初始化,调用 addSingletonFactory 方法将对应的objectFactory 初始化策略存储在 singletonFactoriesObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return singletonObject;
}

Bean实例化阶段执行的顺序

Spring的bean加载流程相关推荐

  1. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  2. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  3. Spring源码分析:Bean加载流程概览及配置文件读取

    很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事的都是Java Web的工作,对于程序员来说,一个Web项目用到Spring,只是配置一下配置文件而已 ...

  4. Spring控制Bean加载顺序

    spring容器载入bean顺序是不确定的,spring框架没有约定特定顺序逻辑规范. 首先要了解depends-on或@DependsOn作用,是用来表示一个bean A的实例化依赖另一个bean ...

  5. spring cloud feign 加载流程

    一.如果只想加入feign,不要载入hystrix,则在引包时排除掉hystrix的包. <dependency><groupId>org.springframework.cl ...

  6. spring boot实战(第十篇)Spring boot Bean加载源码分析

    前言 前面的文章描述了Application对应Bean的创建,本篇将阐述spring boot中bean的创建过程 refresh 首先来看SpringApplication#run方法中refre ...

  7. Spring框架—SpringBean加载过程

    原文作者:RunAlgorithm 原文地址:图文并茂,揭秘 Spring 的 Bean 的加载过程 1. 概述 Spring 作为 Ioc 框架,实现了依赖注入,由一个中心化的 Bean 工厂来负责 ...

  8. 深入Spring:自定义注解加载和使用

    转自:https://blog.csdn.net/z69183787/article/details/53784845 前言 在工作中经常使用Spring的相关框架,免不了去看一下Spring的实现方 ...

  9. 看看Spring的源码(一)——Bean加载过程

    首先Web项目使用Spring是通过在web.xml里面配置 org.springframework.web.context.ContextLoaderListener初始化IOC容器的. <l ...

最新文章

  1. [BZOJ4033][HAOI2015]树上染色
  2. Linux C编程之一:Linux下c语言的开发环境
  3. Activity传递对象的方法
  4. antd 文本域超长问题_「自然语言处理(NLP)」阿里团队--文本匹配模型(含源码)...
  5. 算法练习day2——190319(大顶堆、冒泡、选择、插入)
  6. 推荐一位零基础学 NLP 的大佬,内含成长历程
  7. POJ1683 Puzzlestan ——Floyd传递闭包+Dfs
  8. 陌生人社会_陌生人之旅
  9. 一个漂亮的电子钟,纪念我们逝去的青春(含软硬件资料)
  10. python selenium canvas_selenium webdriver 实现Canvas画布自动化测试
  11. ffmpeg下载安装教程及介绍
  12. 有一个做饭好吃的妈妈是一种什么体验?
  13. 在嵌入式linux上玩OpenGL
  14. Android 模拟器硬件加速
  15. pyinstaller打包原理,常见问题。
  16. 私有云服务器搭建及ssh连接
  17. rmvb压制中高级技巧
  18. 网页防封链接制作的原理有哪些?
  19. cocos creator利用ShareSDK实现微信登录功能
  20. ecnu 3260 袋鼠妈妈找孩子

热门文章

  1. 鸿蒙系统nova3i,AI四摄加持 华为nova3i 6+128版本今日预售
  2. Striving For Simplicity-the All Convolutional Net 笔记
  3. 腾讯会议开发工程师认证考试题库
  4. 还在为积分商城运营发愁,这篇干货给你参考!
  5. 基准测试框架JMH使用详解
  6. 如何成为ERP选型高手
  7. 服务器例行维修时间,端游公告丨8月11日服务器例行维护,1314绑点、随心染匣送送送...
  8. c和java共同点_java和c语言有什么共同点?
  9. java与c语言的区别
  10. chrome插件——xpath