当前位置: 代码迷 >> 综合 >> 全网最细致的spring源码分析(二):spring在创建bean对象的之前的骚操作
  详细解决方案

全网最细致的spring源码分析(二):spring在创建bean对象的之前的骚操作

热度:57   发布时间:2023-10-18 06:34:58.0

一、回顾

        上回说到Spring在获取单例Bean的时候,有一种情况会调用ObjectFactory下的createBean方法创建bean。

        当然不可能指望在一个方法中就完成创建bean的复杂逻辑,而且跟踪如此之多的spring代码,多多少少也能发现一些规律,一层套一层的验证判断、各种类之间的协作,直到出现一个以do开头的方法来真正的完成工作,其他的方法只是在做全局的统筹工作。createBean也不会出现意外,让我们看看它都做了那些事情。

        为了更好的理解这个方法,我们先来回顾一下类型为RootBeanDefinition的参数mbd是个什么玩意?

        RootBeanDefinition实际上是用来创建Bean的对象,它存储了创建bean所需要的信息:

  1. 定义了bean的id、别名和Bean的对应关系( BeanDefinitionHolder)

  2. Bean的注解( AnnotatedElement)

  3. 具体的工厂方法(class类型),包括工厂方法的 返回类型,工厂方法的Method对象

  4. 构造函数、构造函数形参类型

  5. Bean的class对象

二、createBean统筹处理

protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {//记录日志if (logger.isDebugEnabled()) {logger.debug("Creating instance of bean '" + beanName + "'");}//初始化在这个方法中真正会使用的mbd,默认情况下它就是调用时传来的参数RootBeanDefinition mbdToUse = mbd;//resolveBeanClass方法解析加载了mbd但是并没有生产Bean,它会生产一个特定的classLoad来加载mbd生成classClass<?> resolvedClass = resolveBeanClass(mbd, beanName);//判断了是否是动态解析的bean和是否是共享合并的bean,在这种情况下mbd是无法储存的解析出来class的,所以这里克隆一下mbd对象。if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {mbdToUse = new RootBeanDefinition(mbd);mbdToUse.setBeanClass(resolvedClass);}//准备覆盖的方法try {mbdToUse.prepareMethodOverrides();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),beanName, "Validation of method overrides failed", ex);}try {// 允许后处理器(BeanPostProcessors)来用代理对象替换掉真正的beanObject bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}}catch (Throwable ex) {throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,"BeanPostProcessor before instantiation of bean failed", ex);}//创建beanObject beanInstance = doCreateBean(beanName, mbdToUse, args);if (logger.isDebugEnabled()) {logger.debug("Finished creating instance of bean '" + beanName + "'");}return beanInstance;
}

        果不其然,createBean方法只不过做了一个统筹的安排,总结一下这个方法的具体功能和步骤。

  1. 根据设置的class属性和className解析class

  2. 对override属性进行标记和验证,这里有所不同的是Spring的配置里面根本没有override-method之类的配置,但是在spring配置中存在lookup-method和replace-method,这两个配置会被统一存放在beanDefinition中的methodOverrides属性里,这个方法也就是对这两个配置做操作。

  3. 初始化前使用后处理器,做短路操作,判断是否需要替换掉bean

  4. 调用doCreateBean创建bean

        我们还需要先详细研究一下mbd的prepareMethodOverrides方法以及用后处理器来狸猫换太子的resolveBeforeInstantiation方法,才能完全理解这个方法的工作原理。

三、prepareMethodOverrides方法的方法校验和预处理

        查看在AbstractBeanDefinition中的prepareMethodOverrides方法

public void prepareMethodOverrides() throws BeanDefinitionValidationException {//获取方法的重写对象MethodOverrides methodOverrides = getMethodOverrides();if (!methodOverrides.isEmpty()) {for (MethodOverride mo : methodOverrides.getOverrides()) {//标记未重载的方法prepareMethodOverride(mo);}}
}protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {//查看对应类中叫这个名字的方法的个数int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());if (count == 0) {//没有就抛出异常throw new BeanDefinitionValidationException("Invalid method override: no method with name '" + mo.getMethodName() +"' on class [" + getBeanClassName() + "]");}else if (count == 1) {//将其标记为没有重载,用来减少开系统开销mo.setOverloaded(false);}
}

        这两个方法的实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性(lookup-method和replace-method两个配置会被存放到其中),动态的为当前bean生成代理并使用对应的拦截器为bean做增强处理,具体的实现逻辑在bean的实例化部分。如果一个类中存在若干个重载方法(就是count大于1的情况下),会在方法调用及增强的时候还需要根据参数类型进行批评,最终确认当前调用的到底是哪个方法。

        但spring将其中一种特别情况在这里做了优化处理,没有重载方法的时候(count等于1的情况),并标记为没有重载,这样在后续的使用中直接就可以无需在根据参数进行判断是哪个具体方法,还提前做了方法存在性的校验。

四、resolveBeforeInstantiation实例化前的处理

        在调用doCreate方法之前,使用了一个resolveBeforeInstantiation方法对BeanDefinigiton中的属性做了些前置处理。当然其中是否有相应的逻辑实现还需要继续深入,在开始真正的bean前后留有预处理函也是可扩展的一种体现。在这个方法调用之后,出现了一个重要的短路判断,这是极为关键的部分。如果前置处理后返回了不为空的结果,那么bean变会被偷偷换掉,我们熟悉的AOP功能就是基于这里的判断完成了偷天换日。

Object bean = resolveBeforeInstantiation(beanName, mbdToUse);//这里出现了短路判断if (bean != null) {return bean;}

        进入resolveBeforeInstantiation方法查看,这个方法并不复杂,需要注意的是applyBeanPostProcessorsBeforeInstantiation方法和applyBeanPostProcessorsAfterInitialization方法,这里我们不需要太多的了解这里两个方法的实现,以后会重点说,只要知道它们连会对原本的bean包装加工就可以了。

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {Object bean = null;//判断是否存在后处理器是否实例化if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {// 确保bean已经被解析if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {//确定bean的目标类型Class<?> targetType = determineTargetType(beanName, mbd);if (targetType != null) {//实例化前的后处理器   这里生成bean后交给  实例化后的后处理器加工bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);if (bean != null) {//实例化后的后处理器   加工完之后 这个bean会用来替换原本正常逻辑中的beanbean = applyBeanPostProcessorsAfterInitialization(bean, beanName);}}}//设置已经完成了在实例化之前的解决了mbd.beforeInstantiationResolved = (bean != null);}return bean;
}

        applyBeanPostProcessorsBeforeInstantiation方法,在bean的实例化之前调用,也就是将AbsractBeanDefinition转换成BeanWrapper的前面做的处理。经过处理之后BeanDefinition有可能会被修改,此时的bean可能不在是之前预计生成的bean,会成为一个代理后的bean。

protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName)throws BeansException {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);if (result != null) {return result;}}}return null;
}

      applyBeanPostProcessorsAfterInitialization方法则有些不同,在上一篇文章获取单例bean的时候讲过,bean的初始化后尽可能的让注册的后处理器的postProcessorsAfterInitialization方法应用到bean中,因为如果返回的bean不为空,那么就会得到一个无法进行普通创建过程的bean,所以只能通过postProcessorsAfterInitialization方法就行创建。

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {result = beanProcessor.postProcessAfterInitialization(result, beanName);if (result == null) {return result;}}return result;
}

        这样一来我们就和上一次文章联系起来了,创建bean之前的操作就是这些了,虽然不多但都很值得研究,有扩展性的增强、更有性能优化和校验的一箭双雕的操作。下一次将bean创建之前我们需要详细的将一个重要知识点“循环依赖”,bean创建的时候最难以理解的便是循环依赖和对其解决的方法。

                                                                                                                                转载请联系我,拒绝非法盗用,违者必究。

  相关解决方案