Spring源码刨析之配置文件的解析和bean的创建以及生命周期

   public void test1(){
        XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        user u = xmlBeanFactory.getBean("user",org.xhpcd.user.class);
       // System.out.println(u.getStu());
    }

先介绍一个类XmlBeanFactory,这个类负责配置文件的解析和bean的创建和初始化。在Spring中对象信息会被封装为一个BeanDefinition对象,这个对象会保存配置文件中所描述的类的信心比如属性名属性值类名等,以便创建bean时根据BeanDefinition对象的信息反射创建并赋值。

配置文件解析

   xmlbeanfactory内部会创建XmlBeanDefinitionReader用来解析配置文件资源。

把xml文件封装为Document,接着调用注册方法。此方法内部再经过调用会调用到parseBeanDefinitions方法。

 这里的Node就是<bean>标签的信息的封装parseDefaultElement(ele, delegate)方法用来解析常规标签,delegate.parseCustomElement(ele)用来解析自定义标签比如<mvc:annotation-driven></mvc:annotation-driven>这种的或者自定义的标签。

信息封装完毕后,会被存放在XmlBeanFactory的Map文件中,keybeanname,value是beanDefinition。到这里基本xml文件都被解析了。

Bean的创建和赋值

 XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        user u = xmlBeanFactory.getBean("user",org.xhpcd.user.class);

代码在getBean内部有体现

首先transformedBeanName(name)的作用是可能用户提供的是别名转化为beannaem也就是配置文件中bean的id,beanFactory会把创建的bean存放起来以便复用,所以底层使用的就是Map数据结构,根据beanname和bean对象进行存储,主要关注getSingleton(beanName)方法

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

这里就是所谓的三级缓存,它还有一个重要功能解决循环依赖等问题。

由于我们这里主要讲解bean的生命周期和创建,第一次获取肯定是获取不到。上上个图中if分支是判断bean的类型是不是BeanFactory,如果是的话就调用工厂的getObject方法,else分支内就是父子容器的概念,如果在创建XmlBeanFactory时值定了其它的容器那么会把它存放在parentBeanFactory中,那么我们子容器再查找时就会先找自己的内部看是否有,如果没有就找父类的。

接下来注意markBeanAsCreated(beanName)和final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);方法

先看getMergedLocalBeanDefinition(beanName)方法其内部本质是获取对应的bean信息,然后会去查看bean是否有指定继承的类

  <bean id="abs" class="" abstract="true">
        <property name="id" value="1">

        </property>
    </bean>
    <bean id="stu" name="u" class="org.xhpcd.student" parent="abs">

    </bean>

就像这样存在继承的类,spring做的就是把当前类的bean定义信息和父类的bean定义信息组合在一起并返回,然后这里就是完整的bean定义信息

以此递归调用获取父类bean信息,然后会把这些信息存放在mergedBeanDefinitions中。

接下来看之前的markBeanAsCreated(beanName)方法

将指定的 Bean 标记为已创建(或即将创建)。这允许 Bean 工厂优化其缓存,以便重复创建指定的 Bean。现在我们实际上正在创建 bean,让我们重新合并 bean 定义......以防万一它的某些元数据在此期间发生了变化。(注解原话)

接下来 mbd.getDependsOn()方法就是获取bean标签中的depend-on信息解析,不过用的不多。

这段代码是真正创建bean的代码,首先判断bean是单例还是多例又或者是session,request等作用域。先看单例吧,首先getSingleton方法内部传递了一个lambda表达式,作用是为了后续回调此方法,接下来分析getSingleton方法

首先先看 beforeSingletonCreation(beanName) 方法,

protected void beforeSingletonCreation(String beanName) {
    if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
       throw new BeanCurrentlyInCreationException(beanName);
    }
}

这段代码就是判断当前类是否被排除并且把它加入到singletonsCurrentlyInCreation集合中,防止在bean的生命周期完成前被多次创建。

然后就是singletonObject = singletonFactory.getObject();此方法就是对传入的表达式的一个方法回调,回调createBean方法,接下来分析此方法

首先解析bean定义信息获取Class信息,

mbdToUse.prepareMethodOverrides()方法是和方法替换有关但平时用的少我会在其他文章进行讲述。

再接下来就是

// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);

这段代码涉及Aop,通过@EnableAspect注解注册的bean后处理器,提前返回一个代理对象,代理对象不经过后面其它的后处理器等。以后文章会讲解aop这部分

接下来就是重点 doCreateBean(beanName, mbdToUse, args) 方法

首先创建一个包装对象,里面有一个属性就是我们的bean对象,只是此时还没没进行属性赋值初始化。包装类的作用主要就是用于类型转换,配置文件中把字符串转化为int等。

紧接着创建对应工厂放入三级缓存中,主要用来解决代理循环依赖问题。这里用不到

然后就是popilateBean方法了,这个方法内部进行属性的注入;

先看这个方法此方法内部是获取所有InstantiationAwareBeanPostProcessors

类型的后处理器,然后在最开始进行一些修改。

类型转换

首先尝试获取自定义类型转换器,紧接着对bean定义信息中set方法获取到转换的类型的属性的封装信息PropertyValue进行逐个遍历,内部通过set方法获取类型,然后或取真实的类型转换信息进行转换并赋值给PropertyValue,最终把类型转换后的结果存放在beanwrapper的属性中并进行属性赋值

首先根据  GenericTypeAwarePropertyDescriptor 拿到可写的方法就是set方法,然后利用反射进行属性赋值。

紧接着调用initializeBean方法

执行aware注入方法,紧接着执行Bean后置处理器的before方法然后调用init方法再调用后处理器的after方法,就基本完成创建的生命周期

到这里上面所说的回调方法基本分析完毕。

然后调用 afterSingletonCreation(beanName) 

protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

移除之前上面正在创建bean的信息,然后调用add方法放入一级缓存。到这里bean就创建完毕了

相关推荐

  1. SpringBean生命周期

    2024-04-15 00:22:04       22 阅读
  2. SpringBean生命周期

    2024-04-15 00:22:04       9 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-15 00:22:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-15 00:22:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-15 00:22:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-15 00:22:04       18 阅读

热门阅读

  1. python_day27

    2024-04-15 00:22:04       14 阅读
  2. 如何做一个自己的开源项目

    2024-04-15 00:22:04       13 阅读
  3. Qt中显示hex数据的控件

    2024-04-15 00:22:04       13 阅读
  4. C++:运算符与表达式 (信奥赛练习)

    2024-04-15 00:22:04       15 阅读
  5. LeetCode 61. 旋转链表

    2024-04-15 00:22:04       11 阅读
  6. Python装饰器

    2024-04-15 00:22:04       13 阅读
  7. Vue EasyUI插件 学习笔记(基础)详细版

    2024-04-15 00:22:04       17 阅读
  8. jQuery学习笔记(2.0)

    2024-04-15 00:22:04       12 阅读
  9. Nginx 基础应用实战 04 在公网配置配置HTTPS

    2024-04-15 00:22:04       15 阅读
  10. 前端npm常用命令总结

    2024-04-15 00:22:04       12 阅读