• Spring 源码阅——忽略感知接口对应成员变量的自动装配


    BeanFactory 的实例化是在 AbstractRefreshableApplicationContext#createBeanFactory 方法中完成的,这里会通过构造方法创建一个 DefaultListableBeanFactory 类型的 BeanFactory,如下:

    1. protected DefaultListableBeanFactory createBeanFactory() {
    2. return new DefaultListableBeanFactory(getInternalParentBeanFactory());
    3. }

    这里的 getInternalParentBeanFactory() 获取的是当前容器的父容器对应的 BeanFactory,在创建当前容器的时候,父容器是空的。

    DefaultListableBeanFactory 的构造方法源码如下:

    1. public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    2. super(parentBeanFactory);
    3. }

    这里直接调用了父类 AbstractAutowireCapableBeanFactory 的构造方法:

    1. public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    2. this();
    3. setParentBeanFactory(parentBeanFactory);
    4. }

    代码中有两个步骤,这篇文章主要分析一下这两部分的源码。

    顺便把 DefaultListableBeanFactory 的类继承关系放在这里供参考:

    无参构造方法

    第一个步骤,是调用了本身的无参构造方法,源码如下:

    1. public AbstractAutowireCapableBeanFactory() {
    2. super();
    3. ignoreDependencyInterface(BeanNameAware.class);
    4. ignoreDependencyInterface(BeanFactoryAware.class);
    5. ignoreDependencyInterface(BeanClassLoaderAware.class);
    6. }

    这里首先调用了父类的构造方法,不过父类的构造方法是一个空方法,因此我们重点关注下面的逻辑,也就是 3 次 ignoreDependencyInterface 方法的调用。我们查看 ignoreDependencyInterface 的源码:

    1. public void ignoreDependencyInterface(Class ifc) {
    2. this.ignoredDependencyInterfaces.add(ifc);
    3. }

    逻辑很简单,就是当前的 BeanFactory 有一个集合类型的成员变量 ignoredDependencyInterfaces,三次调用这个方法就是把 BeanNameAwareBeanFactoryAwareBeanClassLoaderAware 这三个接口放在了这个集合里面。

    接下来,我们的任务就是分析出将接口放在 ignoredDependencyInterfaces 的作用是什么。

    感知接口

    先看被添加到 ignoredDependencyInterfaces 中的三个接口。

    这三个接口的名字都是 XXXAware,都继承自 Aware 接口,并且都只包含一个与接口名对应的 setXXX 方法。

    相信使用 Spring 框架的小伙伴对 Aware 接口都非常了解,这里在简单总结一下。

    在 Spring 中,Aware 接口也被叫做感知接口,当一个 Bean 实现了感知接口的时候,Spring 创建 Bean 的时候,就会通过调用感知接口的方法注入相应的数据。

    举个例子,我需要在一个 Bean 中使用当前 Bean 在 Spring 容器中的名称,也就是 beanName,那么,我在开发这个类的时候,就可以实现 BeanNameAware 接口,并实现其 setBeanName(String name) 方法。当 Spring 实例化这个类的时候,发现其实现了 BeanNameAware 接口,然后,就会通过调用 setBeanName 方法,将 Bean 的名称传递进来。

    忽略指定接口的自动感知功能

    了解了感知接口及其作用,我们再来看 ignoredDependencyInterfaces 的作用。通过在 IDE 中查找 ignoredDependencyInterfaces 的用法,发现除了调用 add 和 addAll 方法向集合中添加接口意外,只有 isExcludedFromDependencyCheck 方法地方用到了它:

    我们找到这个方法的源码:

    1. /**
    2. * Determine whether the given bean property is excluded from dependency checks.
    3. * <p>This implementation excludes properties defined by CGLIB and
    4. * properties whose type matches an ignored dependency type or which
    5. * are defined by an ignored dependency interface.
    6. * @param pd the PropertyDescriptor of the bean property
    7. * @return whether the bean property is excluded
    8. * @see #ignoreDependencyType(Class)
    9. * @see #ignoreDependencyInterface(Class)
    10. */
    11. protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
    12. return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
    13. this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||
    14. AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));
    15. }

    根据方法的名称和注释判断,这个方法的作用是判断一个 Bean 的成员变量是否要从依赖检查中排除掉。我们只看跟 ignoredDependencyInterfaces 有关的最后一个判断条件:

    1. AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces)

    被调用的 AutowireUtils.isSetterDefinedInInterface 方法的源码如下:

    1. /**
    2. * Return whether the setter method of the given bean property is defined
    3. * in any of the given interfaces.
    4. * @param pd the PropertyDescriptor of the bean property
    5. * @param interfaces the Set of interfaces (Class objects)
    6. * @return whether the setter method is defined by an interface
    7. */
    8. public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
    9. Method setter = pd.getWriteMethod();
    10. if (setter != null) {
    11. Class<?> targetClass = setter.getDeclaringClass();
    12. for (Class<?> ifc : interfaces) {
    13. if (ifc.isAssignableFrom(targetClass) && ClassUtils.hasMethod(ifc, setter)) {
    14. return true;
    15. }
    16. }
    17. }
    18. return false;
    19. }

    方法中传入的两个参数,一个表示要进行判断的属性,另一个则是 ignoredDependencyInterfaces。首先,获取给定属性的 setter 方法及其对应的类,遍历 ignoredDependencyInterfaces 中的每一个接口,并判断这个类是否实现了当前遍历到的接口,以及接口中是否包含同样的 setter 方法。如果以上两个判断的结果都是 true,整个方法的返回值也就是 true,就代表这个属性需要通过感知接口的方法来进行诸如,Spring 就不会在创建这个 Bean 的时候通过自动装配来给这个属性注入值。

    setParentBeanFactory

    第二个步骤是通过调用 parentBeanFactory 将构造方法参数中的 parentBeanFactory 赋值给对应的成员变量:

    1. @Override
    2. public void setParentBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    3. if (this.parentBeanFactory != null && this.parentBeanFactory != parentBeanFactory) {
    4. throw new IllegalStateException("Already associated with parent BeanFactory: " + this.parentBeanFactory);
    5. }
    6. if (this == parentBeanFactory) {
    7. throw new IllegalStateException("Cannot set parent bean factory to self");
    8. }
    9. this.parentBeanFactory = parentBeanFactory;
    10. }

    这一步骤比较简单。

  • 相关阅读:
    Linux笔记 Linux中的进程
    Spring学习从练气到化虚
    PV、EV、AC、BAC、EAC、ETC等计算公式含义
    全开源无加密跨境电商购物网站系统源码(无货源模式+多语言+多货币)
    LeetCode-895. 最大频率栈以及HashMap的存值取值操作
    一行log日志,结果引发了P1的线上事故!
    电力输送、材料和互连领域即将发生巨大变化
    通过对比3PL和4PL,来了解什么是4PL
    【力扣-1337打卡】
    【算法练习Day48】回文子串&&最长回文子序列
  • 原文地址:https://blog.csdn.net/Java_ttcd/article/details/126423280