手把手教你实现spring-beans (二)

系列文章

手把手教你实现spring-beans (一)
手把手教你实现spring-beans (二)
手把手教你实现spring-context (TODO)
手把手教你实现spring-aop (TODO)

前言

  本系列是对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的几个子接口:

  • ListableBeanFactory:添加了查询和访问多个BeanDefinition的能力,BeanFactory只针对单个BeanDefinition进行操作
  • ConfigurableBeanFactory:添加了配置BeanFactory的能力,比如可以注册自定义的PropertyEditor来执行类型转换,也可以添加BeanPostProcessor从而进入bean的生命周期,还可以直接向BeanFactory中注册单实例的bean而不通过BeanDefinition(多用于框架内部注册后置处理器)
  • ConfigurableListableBeanFactory:组合了ListableBeanFactoryConfigurableBeanFactory接口的功能,并添加了提前初始化所有非懒加载的bean的功能
  • AutowireCapableBeanFactory:添加了自动装配容器中bean的能力

关于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,觉得有用的话,点个star吧~ 欢迎交流~。

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