Spring 源码学习(四) bean 的加载

spring 系列 转载自掘金 VipAugus https://juejin.cn/user/2348212565601415/posts

解析完配置后,来看下 bean 是如何加载的


前言

既然我们 Spring 辛辛苦苦将 bean 进行了注册,当然需要拿出来进行使用,在使用之前还需要经过一个步骤,就是 bean 的加载。

在第一篇笔记提到了,完成 bean 注册到 beanDefinitionMap 注册表后,还调用了很多后处理器的方法,其中有一个方法 finishBeanFactoryInitialization(),注释上面写着 Instantiate all remaining (non-lazy-init) singletons,意味着非延迟加载的类,将在这一步实例化,完成类的加载。

而我们使用到 context.getBean("beanName")方法,如果对应的 bean 是非延迟加载的,那么直接就能拿出来进行使用,而延迟加载的 bean 就需要上面的步骤进行类的加载,加载完之后才能进行使用~

下面一起来看下这两个步骤中, bean 是如何进行加载的。


时序图

我们的代码分析都是围绕着这个方法,请同学们提前定位好位置:

org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean

这个 bean 加载的代码量是有点多的,已经超过 100 行,所以整理了时序图,希望能对加载流程有个清晰的概览:

这个时序图介绍了 bean 加载的大体流程,还有很多细节没在图中进行展示。我们先对整体流程有个了解,然后跟着代码一起深入分析吧。


代码分析

再提示一下:由于代码量很多,每次贴大段代码看起来会比较吃力,所以展示的是我认为的关键代码,下载项目看完整注释,跟着源码一起分析~

码云 Gitee 地址

Github 地址


FactoryBean 的使用

在分析加载流程之前,有个前置概念要了解下,在一般情况下,Spring 是通过反射机制利用 beanclass 属性指定实现类来实例化 bean

引用书本:

在某些情况下,实例化 bean 比较复杂,例如有多个参数的情况下,传统方式需要在配置文件中,写很多配置信息,显得不太灵活。在这种情况下,可以使用 Spring 提供的 FactoryBean 接口,用户可以通过实现该接口定制实例化 bean 的逻辑。

FactoryBean 接口定义了三个方法:

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<?> getObjectType();
    default boolean isSingleton() {
        return true;
    }
}
复制代码

主要讲下用法吧:

当配置文件中的 <bean>class 属性实现类是 FactoryBean 时,通过 getBean() 方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 方法返回的对象。

使用 demo 代码请看下图:

扩展 FactoryBean 之后,需要重载图中的两个方法,通过泛型约定返回的类型。在重载的方法中,进行自己个性化的处理。

在启动类 Demo,通过上下文获取类的方法 context.getBean("beanName"),使用区别是 beanName 是否使用 & 前缀,如果有没有 & 前缀,识别到的是 FactoryBean.getObject 返回的 car 类型,如果带上 & 前缀,那么将会返回 FactoryBean 类型的类。

验证和学习书中的概念,最快的方式是运行一遍示例代码,看输出结果是否符合预期,所以参考书中的例子,自己手打代码,看最后的输出结果,发现与书中说的一致,同时也加深了对 FactoryBean 的了解。


为什么要先讲 BeanFactory 这个概念呢?

从时序图看,在 1.5 那个步骤,调用了方法:

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

在这一步中,会判断 sharedInstance 类型,如果属于 FactoryBean,将会调用用户自定义 FactoryBean 的 getObject() 方法进行 bean 初始化。

实例化的真正类型是 getObjectType() 方法定义的类型,不是 FactoryBean 原来本身的类型。最终在容器中注册的是 getObject() 返回的 bean。

提前讲了这个概念,希望大家在最后一步时不会对这个有所迷惑。


从缓存中获取单例 bean

// Eagerly check singleton cache for manually registered singletons.
// 检查缓存中或者实例工厂是否有对应的实例或者从 singletonFactories 中的 ObjectFactory 中获取
Object sharedInstance = getSingleton(beanName);

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    Object singletonObject = this.singletonObjects.get(beanName);
    // 检查缓存中是否存在实例
    if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
        // 记住,公共变量都需要加锁操作,避免多线程并发修改
        synchronized (this.singletonObjects) {
            // 如果此 bean 正在加载则不处理
            singletonObject = this.earlySingletonObjects.get(beanName);
            if (singletonObject == null && allowEarlyReference) {
                // 当某些方法需要提前初始化,调用 addSingletonFactory 方法将对应的
                // objectFactory 初始化策略存储在 singletonFactories
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    singletonObject = singletonFactory.getObject();
                    this.earlySingletonObjects.put(beanName, singletonObject);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
    }
    return singletonObject;
}
复制代码

单例模式在代码设计中经常用到,在 Spring 中,同一个容器的单例只会被创建一次,后续再获取 bean 直接从单例缓存 singletonObjects 中进行获取。

而且因为单例缓存是公共变量,所以对它进行操作的时候,都进行了加锁操作,避免了多线程并发修改或读取的覆盖操作。

还有这里有个 earlySingletonObjects 变量,它也是单例缓存,也是用来保存 beanName 和 创建 bean 实例之间的关系。

与 singletonFactories 不同的是,当一个单例 bean 被放入到这 early 单例缓存后,就要从 singletonFactories 中移除,两者是互斥的,主要用来解决循环依赖的问题。(循环依赖下一篇再详细讲吧)


从 bean 的实例中获取对象

getBean 方法中,getObjectForBeanInstance 是个高频方法,在单例缓存中获得 bean 还是 根据不同 scope 策略加载 bean,都有这个方法的出现,所以结合刚才说的 BeanFactory 概念,一起来看下这个方法做了什么。

org.springframework.beans.factory.support.AbstractBeanFactory#getObjectForBeanInstance

// 返回对应的实例,有时候存在诸如 BeanFactory 的情况并不是直接返回实例本身
// 而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
复制代码

具体方法实现,搜索 注释 4.6 看代码中的注释吧:

交代一下这个方法的流程:

  • 验证 bean 类型:判断是否是工厂 bean
  • 对非 FactoryBean 不做处理
  • 对 bean 进行转换
  • 处理 FactoryBean 类型:委托给 getObjectFromFactoryBean 方法进行处理。

在这个方法中,对工厂 bean 有特殊处理,处理方法跟上面提到的 FactoryBean 使用一样,最终获取的是 FactoryBean.getObject() 方法返回的类型。

对于第四个步骤,委托给 getObjectFromFactoryBean 方法进行处理不深入分析,但里面有三个方法值得一说:

// 单例操作,前置操作
beforeSingletonCreation(beanName);
try {
    object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
    throw new BeanCreationException(beanName,
            "Post-processing of FactoryBean's singleton object failed", ex);
}
finally {
    // 单例模式,后置操作
    afterSingletonCreation(beanName);
}
复制代码

代码中在类的加载时,有前置操作和后置操作,之前在第一篇笔记看过,很多前置和后置操作都是空方法,等用户自定义扩展用的。

但在这里的不是空方法,在两个方法是用来保存和移除类加载的状态,是用来对循环依赖进行检测的。

同时,这两个方法在不同 scope 加载 bean 时也有使用到,也是个高频方法。

try {
    object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
    throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
}
复制代码

这是一个执行后处理的方法,我接触的不多,先记下概念:

Spring 获取 bean 的规则中有一条:尽可能保证所有 bean 初始化后都会调用注册的 BeanPostProcessor 的 postProcessAfterInitialization 方法进行处理。在实际开发中,可以针对这个特性进行扩展。


获取单例

现在来到时序图中的 1.3 步骤:

// Create bean instance. 创建 bean 实例
// singleton 单例模式(最常使用)
if (mbd.isSingleton()) {
    // 第二个参数的回调接口,接口是 org.springframework.beans.factory.ObjectFactory#getObject
    // 接口实现的方法是 createBean(beanName, mbd, args)
    sharedInstance = getSingleton(beanName, () -> {
        return createBean(beanName, mbd, args);
        // 省略了 try / catch
    });
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
复制代码

来看 getSingleton 方法做了什么:

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
    Assert.notNull(beanName, "Bean name must not be null");
    // 注释 4.7 全局变量,加锁
    synchronized (this.singletonObjects) {
        // 检查是否已经被加载了,单例模式就是可以复用已经创建的 bean
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null) {
            // 初始化前操作,校验是否 beanName 是否有别的线程在初始化,并加入初始化状态中
            beforeSingletonCreation(beanName);
            boolean newSingleton = false;
            boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = new LinkedHashSet<>();
            }
            // 初始化 bean,这个就是刚才的回调接口调用的方法,实际执行的是 createBean 方法
            singletonObject = singletonFactory.getObject();
            newSingleton = true;
            if (recordSuppressedExceptions) {
                this.suppressedExceptions = null;
            }
            // 初始化后的操作,移除初始化状态
            afterSingletonCreation(beanName);
            if (newSingleton) {
                // 加入缓存
                addSingleton(beanName, singletonObject);
            }
        }
        return singletonObject;
    }
}
复制代码

来梳理一下流程:

  • 检查缓存是否已经加载过
  • 没有加载,记录 beanName 的加载状态
  • 调用回调接口,实例化 bean
  • 加载单例后的处理方法调用:这一步就是移除加载状态
  • 将结果记录到缓存并删除加载 bean 过程中所记录到的各种辅助状态

对于第二步和第四步,在前面已经提到,用来记录 bean 的加载状态,是用来对 循环依赖 进行检测的,这里先略过不说。

关键的方法在于第三步,调用了 ObjectFactorygetObject() 方法,实际回调接口实现的是 createBean() 方法,需要往下了解,探秘 createBean()


准备创建 bean

对于书中,有句话说的很到位:

Spring 源码中,一个真正干活的函数其实是以 do 开头的,比如 doGetBeandoGEtObjectFromFactoryBean,而入口函数,比如 getObjectFromFactoryBean,其实是从全局角度去做统筹工作。

有了这个概念后,看之后的 Spring 源码,都知道这个套路,在入口函数了解整体流程,然后重点关注 do 开头的干活方法。

按照这种套路,我们来看这个入口方法 createBean()

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])

protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    RootBeanDefinition mbdToUse = mbd;
    // 有道翻译:确保此时bean类已经被解析,并且克隆 bean 定义,以防动态解析的类不能存储在共享合并 bean 定义中。
    // 锁定 class,根据设置的 class 属性或者根据 className 来解析 Class
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
        mbdToUse = new RootBeanDefinition(mbd);
        mbdToUse.setBeanClass(resolvedClass);
    }
    // Prepare method overrides.
    // 验证及准备覆盖的方法
    mbdToUse.prepareMethodOverrides();
    // 让 beanPostProcessor 有机会返回代理而不是目标bean实例。
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
        // 短路操作,如果代理成功创建 bean 后,直接返回
        return bean;
    }
    
    // 创建 bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    return beanInstance;
}
复制代码

先来总结这个流程:

  • 根据设置的 class 属性或者根据 className 来解析 Class
  • 验证及准备覆盖的方法 这个方法是用来处理以下两个配置的:我们在解析默认标签时,会识别 lookup-methodreplaced-method 属性,然后这两个配置的加载将会统一存放在 beanDefinition 中的 methodOverrides 属性里。
  • 应用初始化前的后处理器,解析指定 bean 是否存在初始化前的短路操作
  • 创建 bean

下面来讲下这几个主要步骤


处理 Override 属性

public void prepareMethodOverrides() throws BeanDefinitionValidationException {
    // Check that lookup methods exists.
    if (hasMethodOverrides()) {
        Set<MethodOverride> overrides = getMethodOverrides().getOverrides();
        synchronized (overrides) {
            for (MethodOverride mo : overrides) {
                // 处理 override 属性
                prepareMethodOverride(mo);
            }
        }
    }
}


可以看到,获取类的重载方法列表,然后遍历,一个一个进行处理。具体处理的是 lookup-methodreplaced-method 属性,这个步骤解析的配置将会存入 beanDefinition 中的 methodOverrides 属性里,是为了待会实例化做准备。

如果 bean 在实例化时,监测到 methodOverrides 属性,会动态地位当前 bean 生成代理,使用对应的拦截器为 bean 做增强处理。

(我是不推荐在业务代码中使用这种方式,定位问题和调用都太麻烦,一不小心就会弄错=-=)


实例化前的前置处理

// 让 beanPostProcessor 有机会返回代理而不是目标bean实例。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
    // 短路操作,如果代理成功创建 bean 后,直接返回
    return bean;
}

protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
    Object bean = null;
    if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
        // Make sure bean class is actually resolved at this point.
        if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
            Class<?> targetType = determineTargetType(beanName, mbd);
            if (targetType != null) {
                // 执行前置拦截器的操作
                bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                if (bean != null) {
                    // 执行后置拦截器的操作
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
        }
        mbd.beforeInstantiationResolved = (bean != null);
    }
    return bean;
}


doCreateBean 方法前,有一个短路操作,如果后处理器成功,将会返回代理的 bean

resolveBeforeInstantiation 方法中,在确保 bean 信息已经被解析完成,执行了两个关键方法,从注释中看到,一个是前置拦截器的操作,另一个就是后置拦截器的操作。

如果第一个前置拦截器实例化成功,就已经将单例 bean 放入缓存中,它不会再经历普通 bean 的创建过程,没有机会进行后处理器的调用,所以在这里的第二个步骤,就是为了这个 bean 也能应用后处理器的 postProcessAfterInitialization 方法。


创建 bean

终于到了关键的干活方法:doGetBean。在通过上一个方法校验,没有特定的前置处理,所以它是一个普通 bean, 常规 bean 进行创建在 doGetBean 方法中完成。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) {
    // Instantiate the bean.
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        // 注释 4.8 根据指定 bean 使用对应的策略创建新的实例 例如跟进方法去看,有工厂方法,构造函数自动注入,简单初始化
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    final Object bean = instanceWrapper.getWrappedInstance();
    Class<?> beanType = instanceWrapper.getWrappedClass();
    if (beanType != NullBean.class) {
        mbd.resolvedTargetType = beanType;
    }
    // 允许后处理程序修改合并的bean定义
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            mbd.postProcessed = true;
        }
    }
    // 是否需要提前曝光,用来解决循环依赖时使用
    boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
            isSingletonCurrentlyInCreation(beanName));
    if (earlySingletonExposure) {
        // 第二个参数是回调接口,实现的功能是将切面动态织入 bean
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
    }
    Object exposedObject = bean;
    // 对 bean 进行填充,将各个属性值注入
    // 如果存在对其它 bean 的依赖,将会递归初始化依赖的 bean
    populateBean(beanName, mbd, instanceWrapper);
    // 调用初始化方法,例如 init-method
    exposedObject = initializeBean(beanName, exposedObject, mbd);
    
    if (earlySingletonExposure) {
        Object earlySingletonReference = getSingleton(beanName, false);
        // earlySingletonReference 只有在检测到有循环依赖的情况下才 不为空
        if (earlySingletonReference != null) {
            if (exposedObject == bean) {
                // 如果 exposedObject 没有在初始化方法中被改变,也就是没有被增强
                exposedObject = earlySingletonReference;
            }
            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                String[] dependentBeans = getDependentBeans(beanName);
                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                for (String dependentBean : dependentBeans) {
                    // 检查依赖
                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                        actualDependentBeans.add(dependentBean);
                    }
                }
                // bean 创建后,它所依赖的 bean 一定是已经创建了
                // 在上面已经找到它有依赖的 bean,如果 actualDependentBeans 不为空
                // 表示还有依赖的 bean 没有初始化完成,也就是存在循环依赖
                if (!actualDependentBeans.isEmpty()) {
                    throw new BeanCurrentlyInCreationException(beanName);
            }
        }
    }
    // Register bean as disposable.
    // 根据 scope 注册 bean
    registerDisposableBeanIfNecessary(beanName, bean, mbd);
    return exposedObject;
}


看到这么长的代码,感觉有点头晕,所以先来梳理这个方法的流程:

  1. 如果加载的 bean 是单例,要清除缓存
  2. 实例化 bean,将 BeanDifinition 转化成 BeanWrapper
  3. 后处理器修改合并后的 bean 定义: bean 合并后的处理,Autowired 注解正式通过此方法实现诸如类型的预解析
  4. 依赖处理
  5. 属性填充:将所有属性填充到 bean 的实例中
  6. 循环依赖检查
  7. 注册 DisposableBean:这一步是用来处理 destroy-method 属性,在这一步注册,以便在销毁对象时调用。
  8. 完成创建并返回

从上面流程可以看出,这个方法做了很多事情,以至于代码超过了 100 多行,给人的阅读体验差,所以尽量还是拆分小方法,在入口方法尽量简洁,说明做的事情,具体在小方法中完成。

因为这个创建过程的代码很多和复杂,我挑重点来理解和学习,详细的还有待深入学习


创建 bean 的实例

在上面第二个步骤,做的是实例化 bean,然后返回 BeanWrapper

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    // Shortcut when re-creating the same bean...
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            // 如果一个类有多个构造函数,每个构造函数都有不同的参数,调用前需要进行判断对应的构造函数或者工厂方法
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    // 如果已经解析过,不需要再次解析
    if (resolved) {
        if (autowireNecessary) {
            // 实际解析的是 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
            // 构造函数自动注入(如果参数有很多个,在匹配构造函数可复杂了,不敢细看=-=)
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            // 使用默认的构造函数
            return instantiateBean(beanName, mbd);
        }
    }
    // Candidate constructors for autowiring? 需要根据参数解析构造函数
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    // Preferred constructors for default construction?
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        // 构造函数注入
        return autowireConstructor(beanName, mbd, ctors, null);
    }
    // No special handling: simply use no-arg constructor. 没有特殊的处理,使用默认构造函数构造
    return instantiateBean(beanName, mbd);
}


大致介绍功能:

  • 如果存在工厂方法则使用工厂方法进行初始化
  • 一个类有多个构造函数,每个构造函数都有不同的参数,所以需要根据参数锁定构造函数进行 bean 的实例化: 在这一步我是真心服,为了匹配到特定的构造函数,下了很大的功夫,感兴趣的可以定位到这个函数观看 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor
  • 如果即不存在工厂方法,也不存在带有参数的构造函数,会使用默认的构造函数进行 bean 的实例化

在这个流程中,通过两种方式,一种是工厂方法,另一种就是构造函数,将传进来的 RootBeanDefinition 中的配置二选一生成 bean 实例

具体的不往下跟踪,来看下一个步骤


处理循环依赖

// 是否需要提前曝光,用来解决循环依赖时使用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
        isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
    // 第二个参数是回调接口,实现的功能是将切面动态织入 bean
    addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}


关键方法是 addSingletonFactory,完成的作用:在 bean 初始化完成前将创建实例的 ObjectFactory 加入单例工厂

一开始就讲过, ObjectFactory 是创建对象时使用的工厂。在对象实例化时,会判断自己依赖的对象是否已经创建好了,判断的依据是查看依赖对象的 ObjectFactory 是否在单例缓存中,如果没有创建将会先创建依赖的对象,然后将 ObjectFactory 放入单例缓存。

这时如果有循环依赖,需要提前对它进行暴露,让依赖方找到并正常实例化。

循环依赖解决方案在下一篇再细讲吧。


属性注入

这也是个高频方法,在初始化的时候要对属性 property 进行注入,贴一些代码片段:

populateBean(beanName, mbd, instanceWrapper);

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // 给 awareBeanPostProcessor 后处理器最后一次机会,在属性设置之前修改bean的属性
    boolean continueWithPropertyPopulation = true;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        ...
        if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
            continueWithPropertyPopulation = false;
            break;
        }
        ...
    }
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        // Add property values based on autowire by name if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            // 根据名字自动注入
            autowireByName(beanName, mbd, bw, newPvs);
        }
        // Add property values based on autowire by type if applicable.
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            // 根据类型自动注入
            autowireByType(beanName, mbd, bw, newPvs);
        }
        pvs = newPvs;
    }
    // 后处理器已经初始化
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    // 需要依赖检查
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    PropertyDescriptor[] filteredPds = null;
    // 从 beanPostProcessors 对象中提取 BeanPostProcessor 结果集,遍历后处理器
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        ...
    }
    // 在前面也出现过,用来进行依赖检查
    filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    checkDependencies(beanName, mbd, filteredPds, pvs);
    // 将属性应用到 bean 中,使用深拷贝,将子类的属性一并拷贝
    applyPropertyValues(beanName, mbd, bw, pvs);
}


由于代码太长,感兴趣的小伙伴定位到 注释 4.11 位置查看吧

介绍一下处理流程:

  1. 调用 InstantiationAwareBeanPostProcessor 处理器的 postProcessAfterInstantiation 方法,判断控制程序是否继续进行属性填充
  2. 根据注入类型(byName/byType),提取依赖的 bean,统一存入 PropertyValues 中
  3. 判断是否需要进行 BeanPostProcessor 和 依赖检查:
  • 如果有后处理器,将会应用 InstantiationAwareBeanPostProcessor 处理器的 postProcessProperties 方法,对属性获取完毕填充前,对属性进行再次处理。
  • 使用 checkDependencies 方法来进行依赖检查
  1. 将所有解析到的 PropertyValues 中的属性填充至 BeanWrapper 中。

在这个方法中,根据不同的注入类型进行属性填充,然后调用后处理器进行处理,最终将属性应用到 bean 中。

这里也不细说,继续往下走,看下一个方法


初始化 bean

在配置文件中,在使用 <bean> 标签时,使用到了 init-method 属性,这个属性的作用就是在这个地方使用的:bean 实例化前,调用 init-method 指定的方法来根据用户业务进行相应的实例化。来看下入口方法 initializeBean

// 调用初始化方法,例如 init-method
exposedObject = initializeBean(beanName, exposedObject, mbd);

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    // 注释 4.12 securityManage 是啥,不确定=-=
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareMethods(beanName, bean);
            return null;
        }, getAccessControlContext());
    }
    else {
        // 如果没有 securityManage,方法里面校验了 bean 的类型,需要引用 Aware 接口
        // 对特殊的 bean 处理:Aware/ BeanClassLoader / BeanFactoryAware
        invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 熟悉么,后处理器又来了
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    // 激活用户自定义的 init-method 方法
    invokeInitMethods(beanName, wrappedBean, mbd);
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}


这个方法主要是用来进行我们设定的初始化方法的调用,不过在方法内部,还做了其它操作,所以一起来讲下流程:

1、激活 Aware 方法

Spring 中提供了一些 Aware 接口,实现了这个接口的 bean,在被初始化之后,可以取得一些相对应的资源,例如 BeanFactoryAware,在初始化后, Spring 容器将会注入 BeanFactory 的实例。所以如果需要获取这些资源,请引用 Aware 接口。

2、执行后处理器

相信这个大家已经不陌生了,我们可以在诸如 PostProcessor 等后处理器里面自定义,实现修改和扩展。例如 BeanPostProcessor 类中有 postProcessBeforeInitializationpostProcessAfterInitialization,可以对 bean 加载前后进行逻辑扩展,可以将它理解成切面 AOP 的思想。

3、激活自定义的 init 方法

这个方法用途很明显,就是找到用户自定义的构造函数,然后调用它。要注意的是,如果 beanInitializingBean 类型话,需要调用 afterPropertiesSet 方法。

执行顺序是先 afterPropertiesSet,接着才是 init-method 定义的方法。


注册 disposableBean

这是 Spring 提供销毁方法的扩展入口,Spring 爸爸将我们能考虑和想扩展的口子都给预留好。除了通过 destroy-method 属性配置销毁方法外,还可以注册后处理器 DestructionAwareBeanPostProcessor 来统一处理 bean 的销毁方法:

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
        if (mbd.isSingleton()) {
            // 单例模式
            // 注册 DisposableBean
            registerDisposableBean(beanName,
                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        }
        else {
            // A bean with a custom scope...
            Scope scope = this.scopes.get(mbd.getScope());
            scope.registerDestructionCallback(beanName,
                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
        }
    }
}

这里就是往不同的 scope 下, 进行 disposableBean 的注册。


总结

本篇笔记总结了类加载的过程,结合时序图和代码分析,希望对它能有一个更深的了解。

同时对代码编写也有一点感触:

  1. 不要写过长的方法,尽量拆分成小方法,清晰意图

从一开始看 Spring 源码的时候,就惊叹于它代码的整洁和逻辑清晰,入口方法展示需要做的事情,然后工作具体逻辑细分,体现了代码设计者的高超设计。

所以在看到有几个方法超过 100 行,心中小小吐槽了一下,看来我跟大佬们写的代码也有共同点,那就是还可以进行优化哈哈哈~

  1. 要在关键地方都打上日志,方便排查和定位

我截取的代码片段,由于篇幅原因,有些逻辑判断和日志处理都给摘掉了,但是日志管理是很重要的一环,在关键地方打印日志,在之后排查问题和分析数据都会有帮助。

如果懒得打印日志,在关键的地方没有打印日志,即便出现了问题,也不知道从何查起,导致问题的原因迟迟无法暴露,造成用户的投诉,那就得不偿失了。

由于个人技术有限,如果有理解不到位或者错误的地方,请留下评论,我会根据朋友们的建议进行修正

spring-analysis-note 码云 Gitee 地址

spring-analysis-note Github 地址

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容