实现 Spring Beans (2)
   一些代码   0 评论   798 浏览

实现 Spring Beans (2)

   一些代码   0 评论   798 浏览

前言

  本系列是对tiny-spring项目的详细解读,聚焦spring-beans的基本实现,对应着(first~sixth)-stage这六个构建过程。

  上一篇介绍了Spring对资源的抽象、读取和解析,可以说是完成了一系列准备工作,剩下的就是等着BeanFactory出场,从准备好的BeanDefinition中提取信息生成对象完成依赖注入了。

引入BeanFactory

  BeanFactory接口仅仅定义了基本的查询和获取功能,更多高级的功能都定义在其子接口中,实际的BeanFactory实现类会实现这些子接口,从而获得非常完备的功能。这样设计的好处是每个接口都专注于一部分功能,最小化了接口的职责,只有在确实需要时才转型成更合适的接口,毕竟大多是时候BeanFactory.getBean(...)就满足需求了,不是吗?上一篇也提到过,BeanFactory的实现类同时也会实现BeanDefinitionRegistry接口,进而持有beanName -> BeanDefinition的关联关系,BeanFactory接口定义的查询和获取功能便是基于此而展开的。

  Spring本身也是面向接口的,而且它的接口分得都很细,源码中很多类不仅要继承自一个抽象基类还要实现2~3个接口,这些抽象类和接口很多也有继承关系,看源码的话确实会比较累。闲话少提,下面看看BeanFactory的定义:

// 输入命令git checkout fourth-stage,切换到第四阶段,可以看到BeanFactory家族的基本框架

/**
 * BeanFactory持有从xml解析出来的
 * BeanDefinition,根据BeanDefinition
 * 提供的信息去做JavaBean的创建、管理工作。
 *
 * 这个接口只提供了最小的功能集,恰恰也是一般客
 * 户端程序用得最多的部分。更多高级功能都定义在
 * 其子接口中,比如ListableBeanFactory、ApplicationContext。
 */
public interface BeanFactory {
    /**
     * 通过beanName返回BeanFactory管理的一个对象。
     */
    @NotNull
    Object getBean(@NotNull String beanName) throws BeansException;

    /**
     * 通过beanName返回BeanFactory管理的一个对象,附加类型检查。
     */
    @NotNull
    <T> T getBean(@NotNull String beanName, @Nullable Class<T> requiredType) throws BeansException;

    /**
     * BeanFactory中是否存在名称为beanName的对象。
     * 该算法会查看BeanFactory持有的BeanDefinition来
     * 判断,因此不一定会导致对象的实例化。
     */
    boolean containsBean(@NotNull String beanName);

    /**
     * 名称为beanName的对象类型是singleton还是prototype。
     * 同样会查看持有的BeanDefinition,也因此不一定会导致
     * 对象的实例化。
     */
    boolean isSingleton(@NotNull String beanName) throws BeansException;
}

  简单介绍一下BeanFactory的几个子接口:

关于PropertyEditor

  看过上一篇我们知道不管是setter注入还是构造函数注入,xml配置文件能够表示的可被注入的类型有<list><ref><value>(<null>的话没有转换的必要就不讨论了)这三种。其中<list>转成数组或java.util.Listref转换成容器中的其他bean是非常固定的,只有<value>取出来的是字符串,而对应的bean属性可能是floatdoubleDate等其它类型,这一层类型转换Spring就是使用PropertyEditor来做的。NOTE: 类型转换在Spring中是一个比较复杂的部分,写到BeanFactory.getBean(...)的实现时我们再说。

  现在让我们来看看要如何实现一个PropertyEditor。简而言之,实现一个PropertyEditor有一个相当固定的流程,比如要实现一个StringDatePropertyEditor

  1. 创建一个类继承自PropertyEditorSupport
  2. 重写

    setAsText(String text)
    1. 将形参text转成Date类型的数据
    2. 调用setValue(...)保存上一步转好的Date类型数据
  3. (可选)重写String getAsText()将转换好的Date转回String
public class DatePropertyEditor extends PropertyEditorSupport {

    // 日期和时间格式
    private String datePattern;

    public String getDatePattern() {
        return datePattern;
    }

    public void setDatePattern(String datePattern) {
        this.datePattern = datePattern;
    }

    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        try {
            // 1. 将String转成Date
            SimpleDateFormat format = new SimpleDateFormat(getDatePattern());
            Date date = format.parse(text);
            // 2. 调用setValue进行保存
            setValue(date);
        } catch (ParseException e) {
            throw new IllegalArgumentException("转换失败", e);
        }
    }

    @Override
    public String getAsText() {
        // 1. 获取保存的Date
        Date date = (Date) getValue();
        // 2. 转回String
        return new SimpleDateFormat(getDatePattern()).format(date);
    }
}

关于AbstractBeanFactory

  规划好BeanFactory的基本框架后,就需要有具体的类去实现它。Spring提供的AbstractBeanFactory就是这样一个模板类,它实现了ConfigurableBeanFactory接口。通过上一篇我们知道,bean可以配置成singleton或是prototype两种类型,对单实例的bean,实现上Spring通过缓存来避免二次创建(这也是Spring的单例模式和GoF单例模式的区别,Spring的单例是相对于BeanFactory来说的),而ConfigurableBeanFactory有直接注册单实例bean的能力(实现上就是直接往缓存里写数据),这算是AbstractBeanFactory选择实现ConfigurableBeanFactory的一个原因吧,可以避免暴露缓存。而ListableBeanFactory接口呢?它可以操作多个BeanDefinition,而BeanDefinition是由BeanDefinitionRegistry持有和管理的,因此不适合一开始就实现这个接口,应该和BeanDefinitionRegistry放在一起实现。我们来看一下getBean(...)的实现逻辑:

@Override
public Object getBean(String beanName) throws BeansException {
    // 处理一下是FactoryBean的情况
    String resolvedBeanName = getResolvedBeanName(beanName);
    // 查一下缓存,看看是否已经创建了
    Object bean = singletonMap.get(resolvedBeanName);
    // 缓存命中
    if (bean != null) {
        // 可能是FactoryBean,根据请求的是
        // FactoryBean本身还是其生产的对象,要分别处理
        return getBeanFromSharedInstance(beanName, bean);
    }
    // 缓存未命中,此时就要通过BeanDefinition中保存的相关信息去创建bean了
    // 在我们的实现中不支持父子bean工厂,因此没有额外的BeanDefinition
    // 合并操作,实现起来要简单很多。
    BeanDefinition mbd = getBeanDefinition(resolvedBeanName);
    if (mbd != null) {
        if (mbd.isSingleton()) {
            // "先检查后执行"这类操作基本上都不是线程安全的
            // Collection.synchronizedMap是以自身作为锁,这里也用同一把锁来保护
            synchronized (singletonMap) {
                // getBean()方法并没有被整个同步住,因此这里再检查一下
                bean = singletonMap.get(resolvedBeanName);
                // 确实没有才创建
                if (bean == null) {
                    System.out.println("正在创建singleton bean[" + resolvedBeanName + "]");
                    bean = createBean(resolvedBeanName, mbd);
                    // 加入缓存
                    addSingleton(resolvedBeanName, bean);
                }
            }
            // 这个bean可能是FactoryBean
            return getBeanFromSharedInstance(beanName, bean);
        } else {
            System.out.println("正在创建prototype bean[" + beanName + "]");
            return createBean(beanName, mbd);
        }
    }
    return null;
}

  这里有必要提一下FactoryBean,这是Spring支持的一种特殊的bean——它本身是一个factory。对FactoryBean来说,它只能被配置成单例的,因为我们关心的是它生产的对象。如果一个bean是FactoryBean,那么根据beanName获取的是它生产的对象,想要获取FactoryBean本身,需要在beanName前添加&前缀。

/**
 * bean自身是一个factory.
 * 如果一个bean实现了这个接口,那么它会被当做工厂使用,而不再是普通的bean。
 */
public interface FactoryBean {
    /**
     * 此前缀用于区分是查询FactoryBean本身还是其创建的对象
     */
    String FACTORY_BEAN_PREFIX = "&";

    /**
     * 返回一个此工厂管理的对象,和BeanFactory一样,也支持singleton pattern和
     * prototype pattern。
     */
    Object getObject() throws Exception;

    /**
     * 返回此FactoryBean创建的对象的类型。
     */
    Class getObjectType();

    /**
     * 这个FactoryBean管理的bean是singleton还是prototype?
     * FactoryBean本身的scope由BeanFactory管理。
     *
     * 如果返回true,也必须保证getObject()永远返回同一对象。
     */
    boolean isSingleton();
}

  可以看到,getBean(...)逻辑非常清晰:首先查看singleton缓存,若命中,处理一下FactoryBean的情况并返回缓存的对象,若未命中,此时就要根据beanName -> BeanDefinition的关联关系找到对应的BeanDefinition,根据BeanDefinition描述的信息去创建对象了。前面提到过AbstractBeanFactory不是一个BeanDefinitionRegistry,因而真正的创建过程就需要留给子类去实现了,至此第一个模板方法createBean(...)就诞生了,这种模式也被成为模板方法模式。我们继续往下看:

@Override
public boolean containsBean(String beanName) {
    String resolvedBeanName = getResolvedBeanName(beanName);
    // 先看看缓存是否命中
    if (singletonMap.containsKey(resolvedBeanName)) {
        return true;
    }
    // 再查询一下是否有对应的BeanDefinition
//        return getBeanDefinition(resolvedBeanName) != null;
    return containsBeanDefinition(resolvedBeanName);
}

@Override
public boolean isSingleton(String beanName) throws BeansException {
    String resolvedBeanName = getResolvedBeanName(beanName);
    // 先看看缓存是否命中
    if (singletonMap.containsKey(resolvedBeanName)) {
        return true;
    }
    // 再查询一下对应的BeanDefinition
    // 这里没有去考虑是FactoryBean的情况
    BeanDefinition mbd = getBeanDefinition(resolvedBeanName);
    if (mbd != null) {
        return mbd.isSingleton();
    }
    return false;
}

  containsBean(String beanName)的逻辑相对简单:若缓存命中的话当然包含,否则只需要查询一下此beanName有没有对应的BeanDefinition即可。在我们的实现中其实判断一下getBeanDefinition(resolvedBeanName) != null也是可以的,不过再提供一个模板方法containsBeanDefinition(...)留给子类去实现也是一个不错的选择。

  isSingleton(...)呢?若缓存命中当然是了,否则就需要获取一下此beanName对应的BeanDefinition,看看定义的是不是单例。这里没有去处理FactoryBean的情况,如果是FactoryBean的话,就需要先把FactoryBean本身创建出来(通过getBean(...)),再去查询FactoryBean.isSingleton()。我们知道FactoryBean本身肯定是单例的,查询它没有太大意义,我们更关心的是它生产的对象。

  看完了bean的获取,现在让我们看一看singleton bean的销毁:

@Override
public void destroySingletons() {
    synchronized (singletonMap) {
        Set<String> keySet = singletonMap.keySet();
        for (String key : keySet) {
            destroySingleton(key, singletonMap.get(key));
        }
        singletonMap.clear();
    }
}

遍历singleton缓存,逐个销毁,逻辑上没什么好说的,只是这里又引入了一个模板方法destroySingleton(...)。我们知道,Spring对bean的生命周期是有一个统一管理的,singleton bean在被销毁时,会调用自定义的detroy-method,会执行DisposableBean回调,不过连createBean(...)都需要子类实现,destroySingleton(...)当然也只有子类知道要怎么做了。讲到这里,AbstractBeanFactory的主要内容也就讲完了。

关于AbstractAutowireCapableBeanFactory

  AbstractAutowireCapableBeanFactory继承自AbstractBeanFactory并实现了AutowireCapableBeanFactory接口,和AbstractBeanFactory一样,它也没有实现BeanDefinitionRegistry接口,因此涉及对BeanDefinition的查询也需要交给子类去实现,但它实现了来自AbstractBeanFactorycreateBean(...)模板方法,并在这个过程中加入了自动装配的功能,我们来看一下具体实现:

 // 输入命令git checkout sixth-stage,切换到第六阶段,可以看到createBean(...)的实现

@Override
public Object createBean(String beanName, BeanDefinition mbd) {
    // 确保依赖的bean先得到初始化
    String[] dependsOnBeanNames = mbd.getDependsOn();
    if (dependsOnBeanNames != null) {
        for (String dependsOnBeanName : dependsOnBeanNames) {
            getBean(dependsOnBeanName);
        }
    }

    BeanWrapper beanWrapper = null;
    // 是构造函数注入或持有构造函数的参数
    if (mbd.getResolvedAutowireMode() == BeanDefinition.AUTOWIRE_CONSTRUCTOR ||
            mbd.hasConstructorArgumentValues()) {
        beanWrapper = autowireConstructor(beanName, mbd);
    } else {
        // 不是的话就走普通的解析赋值路线
        beanWrapper = new BeanWrapper(mbd.getBeanClass());
        registerPropertyEditors(beanWrapper);
    }
    Object bean = beanWrapper.getWrappedInstance();

    // 提前缓存单实例bean
    // 只要保证都是先创建后赋值,就可以解决循环依赖
    if (mbd.isSingleton()) {
        addSingleton(beanName, bean);
    }

    // 给属性赋值
    populateBean(beanName, mbd, beanWrapper);

    // 生命周期回调
    try {
        if (bean instanceof BeanNameAware) {
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(this);
        }
        bean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
        invokeInitMethods(bean, mbd);
        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
    } catch (Exception e) {
        throw new BeansException("调用生命周期函数失败", e);
    }

    return bean;
}

  它的逻辑是这样的:首先保证依赖的bean得到初始化,方法是遍历depends-on属性指定的beanName(s)并调用getBean(...),然后才轮到自身的初始化;接下来通过BeanDefinition查看一下是否指定了装配模式为AUTOWIRE_CONSTRUCTOR或者有提供构造函数注入的相关信息,满足其一就走构造函数装配,否则的话就通过无参构造器实例化对象。以上不管哪种,都会创建一个新的对象,之后只要装配好setter注入的相关属性(通过populateBean(...)),最后统一执行生命周期回调,整个创建过程就宣告结束。

  这里有两个值得注意的地方,第一个是提前缓存singleton bean,为什么要这么做呢?答案是为了解决bean之间的循环引用。只要遵循先创建再缓存后注入的步骤就可以绕开bean之间的循环引用,这样做了就不会出现注入时因为找不到相关bean从而反复创建直至stack over flow,这也正是createBean(...)的做法。仔细想想是不是这么回事?

  第二个是引入了BeanWrapper,它的出现使得与依赖注入和类型转换相关的代码被提取了出来,进入到BeanWrapper而不是留在AbstractAutowireCapableBeanFactory中,可以看到BeanWrapper包装的正是新创建的对象,这也是它名称的由来。

关于BeanWrapper

  围绕BeanWrapper的是关于依赖注入和类型转换两大块内容,因而有必要好好聊一下BeanWrapper

  前面提到,BeanWrapper包装了新创建的对象,依赖注入最终也是要作用到被包装对象上,因而BeanWrapperget/set最终也需要反映到被包装对象的get/set,这部分抽象出来就是PropertyAccessor,真实的Spring是支持级联属性设置的,我们这里就不做支持了。

/**
 * 定义了访问JavaBean getter/setter 的方式,不支持级联属性。
 */
public interface PropertyAccessor {
    /**
     * 给bean的propertyName属性设置值propertyValue,相当于bean.setPropertyName(propertyValue)
     */
    void setPropertyValue(String propertyName, Object propertyValue) throws BeansException;

    /**
     * 获取bean的名为propertyName的属性的值,相当于bean.getPropertyName()
     */
    Object getPropertyValue(String propertyName) throws BeansException;

    /**
     * 获取特定属性的描述信息,我们关心属性对应的setter和getter是否存在,是否可访问等
     */
    PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException;

    /**
     * 获取所有属性的描述信息
     */
    PropertyDescriptor[] getPropertyDescriptors() throws BeansException;
}

  再说类型转换,它的作用是将一个对象转换成另一种类型的对象(这不是废话嘛zzz),因为我们从xml配置中获取到的类型并不一定是bean属性的最终类型。这一层的抽象是TypeConverterTypeConverter的定义也很简单:

/**
 * 将PropertyValue.value转换成实际的类型,
 * 这个接口是使用PropertyEditor进行转换。
 */
public interface TypeConverter {
    /**
     * 将value转换成requiredType类型的实例。
     */
    Object convertIfNecessary(@Nullable Object value,
                              @NotNull Class<?> requiredType) throws BeansException;

    /**
     * 将value转换成descriptor.getPropertyType()类型的实例。
     */
    @Nullable
    Object convertIfNecessary(@Nullable Object value,
                              @NotNull PropertyDescriptor descriptor) throws BeansException;
}

前面有提到过类型转换是使用PropertyEditor来进行的,而PropertyEditor的注册是由ConfigurableBeanFactory来管理的,这里就涉及一个PropertyEditor的同步问题,我们需要把ConfigurableBeanFactory管理的PropertyEditor同步给BeanWrapper一份,这一层抽象出来对应的是PropertyEditorRegistryPropertyEditorRegistrySupport是它的默认实现,非常简单的一个类,这里就不多说了。至于PropertyEditorRegistrar,讲到ApplicationContext我们再说吧。

/**
 * PropertyEditor注册器。
 */
public interface PropertyEditorRegistry {
    /**
     * 注册一个PropertyEditor,此PropertyEditor
     * 处理的类型是requiredType。
     */
    void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

    /**
     * 根据requiredType返回一个PropertyEditor。
     */
    PropertyEditor findCustomEditor(Class<?> requiredType);
}

  说到这里,本来让BeanWrapper继承PropertyEditorRegistrySupport再实现PropertyAccessor就可以了,Spring在这里又故技重施,添加了一层TypeConverterSupport,将实际的转换工作代理给了TypeConverterDelegateBeanWrapper的层次结构大体上就是这样了,我们来看看它的具体实现:

@Override
public void setPropertyValue(String propertyName, Object propertyValue) throws BeansException {
    PropertyDescriptor pd = getPropertyDescriptor(propertyName);
    if (pd == null) {
        throw new BeansException("找不到[" + propertyName + "]属性对应的PropertyDescriptor");
    }
    Method writeMethod = pd.getWriteMethod();
    if (writeMethod == null) {
        throw new BeansException("找不到[" + propertyName + "]属性对应的setter方法");
    }
    try {
        // 获取要转换成的类型
        Class<?> propertyType = pd.getPropertyType();
        // 做转换
        Object newValue = convertIfNecessary(propertyValue, pd);
        if (propertyType.isPrimitive() &&
                (newValue == null || "".equals(newValue))) {
            throw new IllegalArgumentException("属性[" + propertyName + "]的类型是基本类型[" + propertyType + "]");
        }
        // 调用setter设置进去
        writeMethod.invoke(object, newValue);
    } catch (InvocationTargetException | IllegalAccessException e) {
        throw new BeansException("无法调用此getter - " + writeMethod.getName());
    } catch (IllegalArgumentException e) {
        throw new BeansException(e);
    }
}

  setPropertyValue(...)方法首先会去获取属性对应的setter,而由PropertyDescriptor可知属性的实际类型,通过这个信息就知道需不需要做进一步的类型转换,无论转换与否,最后都会得到一个值,再调用之前获取的setter设置进去就完成了依赖注入。NOTE:和PropertyDescriptor相关的内容是通过Introspector.getBeanInfo(...)来获取的,比较简单,这里就不多说了,各位同学自行翻看一下便好。

@Override
public Object getPropertyValue(String propertyName) throws BeansException {
    PropertyDescriptor pd = getPropertyDescriptor(propertyName);
    if (pd == null) {
        throw new BeansException("找不到[" + propertyName + "]属性对应的PropertyDescriptor");
    }
    Method readMethod = pd.getReadMethod();
    if (readMethod == null) {
        throw new BeansException("找不到[" + propertyName + "]属性对应的getter方法");
    }
    try {
        // 调用getter来获取
        return readMethod.invoke(object);
    } catch (InvocationTargetException | IllegalAccessException e) {
        throw new BeansException("无法调用此getter - " + readMethod.getName());
    }
}

  getPropertyValue(...)同理,获取属性对应的getter再调用。真正的重心在convertIfNecessary(...),由于这一层被代理给了TypeConverterDelegate,所以我们去看看TypeConverterDelegate是如何处理类型转换的。

关于TypeConverterDelegate

  TypeConverterDelegate是真正利用PropertyEditor执行类型转换的地方:

/**
 * 类型转换的核心算法。
 */
private Object convertIfNecessary(String propertyName, Object value, Class<?> requiredType, PropertyDescriptor descriptor) {
    Object convertedValue = value;

    // 查找一下这个类型有没有对应的PropertyEditor
    PropertyEditor editor = propertyEditorRegistry.findCustomEditor(requiredType);

    // 若没有,尝试从PropertyDescriptor生成
    if (editor == null && descriptor != null) {
        Class<?> editorClass = descriptor.getPropertyEditorClass();
        if (editorClass != null) {
            editor = (PropertyEditor) ClassUtils.instantiateClass(editorClass);
        }
    }
    // 仍没有
    if (editor == null) {
        // 获取默认的PropertyEditor
        editor = propertyEditorRegistry.findDefaultEditor(requiredType);
        // 也没有默认的PropertyEditor
        if (editor == null) {
            // 查询标准JavaBean的PropertyEditor
            editor = PropertyEditorManager.findEditor(requiredType);
        }
    }

    // 若类型不匹配,执行转换
    if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
        convertedValue = doConvertValue(convertedValue, requiredType, editor);
    }

    if (requiredType != null) {
        if (convertedValue != null) {
            if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                // 基本类型转String
                return convertedValue.toString();
            } else if (requiredType.isArray()) {
                // 转数组
                return convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
            } else {
                // handled above
            }
        }

        if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
            throw new IllegalArgumentException("无法将name = [" + propertyName + "], value = [" + value + "]" + "的属性转换成[" + requiredType + "]类型");
        }
    }
    return convertedValue;
}

  首先根据属性的类型查询对应的PropertyEditor,这一过程是层层递进的;如果有找到,并且需要转换,就调用doConvertValue(...)进行转换,如果没找到,就尝试应用一些内置的类型转换规则。实际上Spring的实现非常复杂,这里已经做了大幅删减。

private Object doConvertValue(Object value, Class<?> requiredType, PropertyEditor editor) {
    Object convertedValue = value;
    if (editor != null) {
        if (convertedValue instanceof String) {
            System.out.println("正在使用[" + editor + "]将字符串转换成[" + requiredType + "]类型");
            String newTextValue = (String) convertedValue;
            editor.setAsText(newTextValue);
            return editor.getValue();
        } else {
            try {
                editor.setValue(convertedValue);
                Object newConvertedValue = editor.getValue();
                if (newConvertedValue != convertedValue) {
                    convertedValue = newConvertedValue;
                }
            } catch (Exception ex) {
                System.out.println("[" + editor.getClass().getName() + "]不支持setValue方法");
            }
        }
    }
    return convertedValue;
}

doConvertValue(...)的实现也很简单,就是直接使用PropertyEditor进行转换。对于value不是String的情况(没怎么见过这种情况,有老铁说一下嘛),使用的是PropertyEditor.setValuePropertyEditor.getValue,标准实现中这两方法并没有做任何实际的转换,这就需要用户注册自定义的PropertyEditor重写这两方法执行自定义的转换了。讲到这里,整个BeanWrapper及其相关的类和接口的关系就比较清晰了,是时候回去再谈AbstractAutowireCapableBeanFactory了。

再谈AbstractAutowireCapableBeanFactory

  前面提到,createBean(...)调用了populateBean(...)来执行依赖注入,兜兜转转解释完了与BeanWrapper相关的类和接口,再来看populateBean(...)的实现就比较容易了。

private void populateBean(String beanName, BeanDefinition mbd, BeanWrapper beanWrapper) {
    // 拿到所有setter注入的相关信息
    MutablePropertyValues pvs = mbd.getPropertyValues();
    // 如果有自动装配,需要提取自动装配的对象来完善MutablePropertyValues
    if (mbd.getResolvedAutowireMode() == BeanDefinition.AUTOWIRE_BY_NAME ||
            mbd.getResolvedAutowireMode() == BeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues mpvs = new MutablePropertyValues(pvs);

        if (mbd.getResolvedAutowireMode() == BeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, beanWrapper, mpvs);
        }

        if (mbd.getResolvedAutowireMode() == BeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, beanWrapper, mpvs);
        }

        pvs = mpvs;
    }
    // 执行注入
    applyPropertyValues(beanName, mbd, beanWrapper, pvs);
}

  方法首先拿到xml中已配置的setter注入的相关信息,之后再查看一下自动装配机制的设置情况。我们知道,Spring的自动装配机制是由框架本身来推导要注入的信息,对于byName类型,是注入配置文件中和属性同名的bean,对于byType类型,是注入配置文件中和属性类型兼容的bean,如果有多个的话,Spring无法替用户进行选择,就会抛出异常。注意由Spring本身来进行推导的话,此时的BeanDefinition中就会缺失这部分信息,autowireByName(...)autowireByType两个方法就是用来完善BeanDefinition缺失的信息的。

private void autowireByName(String beanName, BeanDefinition mbd,
                              BeanWrapper bw, MutablePropertyValues pvs) {
    String[] propertyNames = unsatisfiedObjectProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        if (containsBean(propertyName)) {
            Object bean = getBean(propertyName);
            pvs.addPropertyValue(new PropertyValue(propertyName, bean));
        }
    }
}

private void autowireByType(String beanName, BeanDefinition mbd,
                              BeanWrapper bw, MutablePropertyValues pvs) {
    String[] propertyNames = unsatisfiedObjectProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        Class<?> requiredType = bw.getPropertyDescriptor(propertyName).getPropertyType();
        Map<String, Object> matchingBeans = findMatchingBeans(requiredType);
        if (matchingBeans != null && matchingBeans.size() == 1) {
            pvs.addPropertyValue(new PropertyValue(propertyName, matchingBeans.values().iterator().next()));
        } else {
            if (matchingBeans != null && matchingBeans.size() > 1) {
                throw new BeansException("给[" + beanName + "]应用自动装配时找到多个符合条件的bean");
            }
        }
    }
}

  自动装配,装配的是容器中的其他bean,简单类型(基本类型、字符串等)不包含在这个过程之中,这一层的过滤就是unsatisfiedObjectProperties(...)做的事情。这里引入了一个模板方法findMatchingBeans(...),对多个BeanDefinition的操作定义在ListableBeanFactory中,所以只能留给子类去实现。

  回到populateBean(...),此时我们可以确定没有遗漏任何需要注入的信息,就可以调用applyPropertyValues(...)执行注入了。

private void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw,
                                 MutablePropertyValues pvs) throws BeansException {
    if (pvs == null) return;
    MutablePropertyValues deepCopy = new MutablePropertyValues(pvs);
    PropertyValue[] pvals = deepCopy.getPropertyValues();
    for (int i = 0; i < pvals.length; i++) {
        Object value = resolveValueIfNecessary(beanName, mbd, pvals[i].getName(), pvals[i].getValue());
        PropertyValue pv = new PropertyValue(pvals[i].getName(), value);
        deepCopy.setPropertyValueAtIndex(i, pv);
    }
    bw.setPropertyValues(deepCopy);
}

  这里的实现也分成两部分,首先我们需要确定每一个setter注入的信息是否需要解析,类型转换在这里我们并不关心,因为BeanWrapper已经处理了这一切,我们关心的是由<ref><list>代表的情况。这是因为BeanDefinition此时持有的MutablePropertyValues是从xml配置文件中解析出来的原始数据,需要进一步的解析才能使用。搞定了这一层,就可以交给BeanWrapper去完成剩下的故事了。让我们来看看resolveValueIfNecessary(...)的实现:

private Object resolveValueIfNecessary(String beanName, BeanDefinition mbd,
                                       String argName, Object value) throws BeansException {
    // value是指向另一个bean的引用
    if (value instanceof RuntimeBeanReference) {
        RuntimeBeanReference ref = (RuntimeBeanReference) value;
        return resolveReference(mbd, beanName, argName, ref);
    }
    // value是<list>标签定义的列表
    else if (value instanceof ManagedList) {
        return resolveManagedList(beanName, mbd, argName, (ManagedList) value);
    } else {
        // 内部bean/map/set等这里就不做支持了
        return value;
    }
}

  上一篇我们创建的标记类型RuntimeBeanReferenceManagedList终于派上了用场!解析RuntimeBeanReferenceManagedList的方法也很简单,对于RuntimeBeanReference,获取保存的beanName对它进行getBean(...)即可,对于ManagedList,需要逐个解析它的每一个元素,至此,setter注入的整个流程就宣告结束。

/**
 * 解析一个指向其他bean的引用。
 */
private Object resolveReference(BeanDefinition mergedBeanDefinition, String beanName,
                                  String argName, RuntimeBeanReference ref) throws BeansException {
    try {
        System.out.println("正在解析[" + beanName + "]的[" + argName + "]指向的["+ ref + "]引用");
        return getBean(ref.getBeanName());
    } catch (BeansException ex) {
        throw new BeansException("无法解析[" + beanName + "]的[" + argName + "]指向的["+ ref + "]引用");
    }
}

/**
 * 解析<list>中的每一个元素
 */
private List<Object> resolveManagedList(String beanName, BeanDefinition mbd,
                                        String argName, ManagedList<?> ml) throws BeansException {
    List<Object> resolved = new ArrayList<>();
    for (int i = 0; i < ml.size(); i++) {
        resolved.add(resolveValueIfNecessary(beanName, mbd, argName + "[" + i + "]", ml.get(i)));
    }
    return resolved;
}

  对于构造函数注入,让我们回到createBean(...),找到autowireConstructor(...),相对setter注入来说是要复杂一些的。

private BeanWrapper autowireConstructor(String beanName, BeanDefinition mbd) {
    // cargs中持有的是未解析的参数
    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
    // 保存cargs持有的参数对应的解析版本
    ConstructorArgumentValues resolvedValues = new ConstructorArgumentValues();
    int numberOfCArgs = 0;
    if (cargs != null) {
        numberOfCArgs = cargs.getNumberOfArguments();
        Set<Integer> integerSet = cargs.getIndexedArgumentValues().keySet();
        for (int index : integerSet) {
            // 支持指定下标和不指定下标的混用
            if (index > numberOfCArgs) {
                numberOfCArgs = index + 1;
            }
            // 执行解析并构造镜像版本
            ConstructorArgumentValues.ValueHolder valueHolder = cargs.getIndexedArgumentValues().get(index);
            Object resolvedValue = resolveValueIfNecessary(beanName, mbd, "ctor arg at " + index, valueHolder.getValue());
            resolvedValues.addIndexedArgumentValue(index, resolvedValue, valueHolder.getType());
        }
        // 执行解析并构造镜像版本
        for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) {
            Object resolvedValue = resolveValueIfNecessary(beanName, mbd, "ctor generic arg", valueHolder.getValue());
            resolvedValues.addGenericArgumentValue(resolvedValue, valueHolder.getType());
        }
    }

    // 获取bean class的所有构造器
    Constructor<?>[] constructors = mbd.getBeanClass().getConstructors();
    // 按参数个数从多到少排序,方便后续做选择
    Arrays.sort(constructors, (c1,c2) -> {
        int c1Len = c1.getParameterCount();
        int c2Len = c2.getParameterCount();
        return c2Len - c1Len;
    });

    BeanWrapper beanWrapper = new BeanWrapper();
    registerPropertyEditors(beanWrapper);
    Constructor<?> selectedCtor = null;
    Object[] selectedArgs = null;

    // 类型的匹配程度
    int minTypeDiffWeight = Integer.MAX_VALUE;
    // 遍历构造函数,查找第一个匹配的
    for (int i = 0; i < constructors.length; i++) {
        try {
            Constructor constructor = constructors[i];
            if (constructor.getParameterTypes().length < numberOfCArgs) {
                throw new BeansException("在给[ " + beanName + "]按构造器自动装配时找不到对应的构造函数");
            }
            Class[] argTypes = constructor.getParameterTypes();
            Object[] args = new Object[argTypes.length];
            for (int j = 0; j < argTypes.length; j++) {
                // 首先看看resolvedValues中是否有符合的
                ConstructorArgumentValues.ValueHolder valueHolder = resolvedValues.getArgumentValue(j, argTypes[j]);
                if (valueHolder != null) {
                    // 有的话解析以后可以使用
                    args[j] = beanWrapper.convertIfNecessary(valueHolder.getValue(), argTypes[i]);
                } else {
                    // 没有的话就要在整个BeanFactory中查看一下有没有类型兼容的了
                    Map<String, Object> matchingBeans = findMatchingBeans(argTypes[j]);
                    if (matchingBeans == null || matchingBeans.size() != 1) {
                        throw new IllegalStateException("使用自动装配时BeanFactory中只能有一个类型兼容的bean。");
                    }
                    args[j] = matchingBeans.values().iterator().next();
                }
            }
            int typeDiffWeight = getTypeDifferenceWeight(argTypes, args);
            // 可能有多个匹配结果,删选出最精确的那一个
            if (typeDiffWeight < minTypeDiffWeight) {
                selectedCtor = constructor;
                selectedArgs = args;
                minTypeDiffWeight = typeDiffWeight;
            }
        } catch (BeansException ex) {
            // 没有合适的
            if (i == constructors.length - 1 && selectedCtor == null) {
                throw ex;
            }
        }
    }

    if (selectedCtor == null) {
        throw new BeansException("在[" + beanName + "]中找不到合适的构造函数");
    }
    // 根据推断的构造器和参数初始化新的对象
    beanWrapper.setWrappedInstance(ClassUtils.instantiateClass(selectedCtor, selectedArgs));
    return beanWrapper;
}

  和setter注入一样,首先也需要做进一步的解析,解析完成之后我们就拥有了可用的构造函数参数——resolvedValues,接下来的任务就是根据resolvedValues从bean的所有构造函数中挑选出最匹配的那一个。何为最匹配呢?假如某个类有两个构造函数,一个接受Integer作为参数,另一个接受Number作为参数,如果我们传入的参数是Integer类型,那相对来说接受Integer作为参数的那一个就比接受Number作为参数的那一个要匹配。这一部分的逻辑是通过查询在继承体系中的远近关系来确定的,越匹配值越低。了解了这个思路之后,就可以理解for循环中的代码了。

/**
 * 用来确定arg和对应argType的远近关系,从来确定匹配结果。
 */
private int getTypeDifferenceWeight(Class<?>[] argTypes, Object[] args) {
    int result = 0;
    for (int i = 0; i < argTypes.length; i++) {
        if (!ClassUtils.isAssignableValue(argTypes[i], args[i])) {
            return Integer.MAX_VALUE;
        }
        if (args[i] != null) {
            Class<?> superClass = args[i].getClass().getSuperclass();
            while (superClass != null) {
                if (argTypes[i].isAssignableFrom(superClass)) {
                    result++;
                    superClass = superClass.getSuperclass();
                }
                else {
                    superClass = null;
                }
            }
        }
    }
    return result;
}

  看完了bean的创建流程,让我们来关心一下singleton bean的销毁过程:

@Override
public void destroySingleton(String beanName, Object singletonObject) {
    BeanDefinition mbd = getBeanDefinition(beanName);
    try {
        invokeDestroyMethod(singletonObject, mbd);
    } catch (Exception e) {
        throw new BeansException("无法调用[" + beanName + "]定义的销毁方法");
    }
}

private void invokeDestroyMethod(Object bean, BeanDefinition mbd) throws Exception {
    if (bean instanceof DisposableBean) {
        ((DisposableBean) bean).destroy();
    }
    if (mbd.getDestroyMethodName() != null) {
        bean.getClass().getMethod(mbd.getDestroyMethodName()).invoke(bean);
    }
}

统一回调了对应的生命周期方法,没有其他内容。至于AbstractAutowireCapableBeanFactory中的其他内容,都建立在上述的基础之上,各位同学自行翻看就好。

关于DefaultListableBeanFactory

  DefaultListableBeanFactory继承自AbstractAutowireCapableBeanFactory实现了ConfigurableListableBeanFactoryBeanDefinitionRegistry接口,终于完成了大一统,集齐了BeanFactory的所有功能。有了AbstractAutowireCapableBeanFactory做基石,DefaultListableBeanFactory的实现相对来讲是很简单的,私有属性Map<String, BeanDefinition>就完成了BeanDefinitionRegistryListableBeanFactory大部分的功能,值得注意的只有preInstantiateSingletons()getBeansOfType(...)两个方法了。

@Override
public void preInstantiateSingletons() {
    for (String beanName : beanDefinitionNames) {
        if (containsBeanDefinition(beanName)) {
            BeanDefinition bd = getBeanDefinition(beanName);
            if (bd.isSingleton() && !bd.isLazyInit()) {
                if (FactoryBean.class.isAssignableFrom(bd.getBeanClass())) {
                    FactoryBean factory = (FactoryBean) getBean( FactoryBean.FACTORY_BEAN_PREFIX + beanName);
                    if (factory.isSingleton()) {
                        getBean(beanName);
                    }
                } else {
                    getBean(beanName);
                }
            }
        }
    }
}

public Map<String, Object> getBeansOfType(Class<?> type, boolean includePrototypes, boolean includeFactoryBeans) throws BeansException {
    String[] beanNames = getBeanDefinitionNames(type);
    Map<String, Object> result = new HashMap<>();
    for (String beanName : beanNames) {
        if (includePrototypes || isSingleton(beanName)) {
            result.put(beanName, getBean(beanName));
        }
    }

    String[] singletonNames = getSingletonNames(type);
    for (String beanName : singletonNames) {
        result.put(beanName, getBean(beanName));
    }

    if (includeFactoryBeans) {
        String[] factoryNames = getBeanDefinitionNames(FactoryBean.class);
        for (String factoryName : factoryNames) {
            try {
                FactoryBean factory = (FactoryBean) getBean(FactoryBean.FACTORY_BEAN_PREFIX + factoryName);
                Class objectType = factory.getObjectType();
                if ((objectType == null && factory.isSingleton()) ||
                        ((factory.isSingleton() || includePrototypes) &&
                                objectType != null && type.isAssignableFrom(objectType))) {
                    Object createdObject = getBean(factoryName);
                    if (type.isInstance(createdObject)) {
                        result.put(factoryName, createdObject);
                    }
                }
            } catch (BeansException ex) {
                System.out.println("从FactoryBean中创建对象失败");
            }
        }
    }

    return result;
}

即使是这两个方法,实现也并不复杂,注意不要遗漏有FactoryBean存在的情况。

XMLBeanFactory

  到这里,我们已经拥有了DefaultXMLBeanDefinitionReaderDefaultListableBeanFactory,两者组合就是一个相对完整的IoC容器了,XMLBeanFactory则更进一步,内部进行了组合,为我们提供了便利。

public class XMLBeanFactory extends DefaultListableBeanFactory {

    /// MARK - Properties

    // xml配置文件读取器
    private final XMLBeanDefinitionReader reader = new DefaultXMLBeanDefinitionReader(this);

    /// MARK - Initializers

    public XMLBeanFactory(Resource resource) throws BeansException {
        this.reader.loadBeanDefinitions(resource);
    }
}

结语

  整个spring-beans的基本逻辑到这里就全部讲解完了,完整代码在tiny-spring

本文由 RawChen 发表, 最后编辑时间为:2021-11-13 13:21
如果你觉得我的文章不错,不妨鼓励我继续写作。

发表评论
选择表情
Top