第六章 Spring之假如让你来写IOC容器——Scope和属性填充

Spring源码阅读目录

第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如让你来写IOC容器——加载资源篇
第三章 Spring之假如让你来写IOC容器——解析配置文件篇
第四章 Spring之假如让你来写IOC容器——XML配置文件篇
第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如让你来写IOC容器——Scope和属性填充
第七章 Spring之假如让你来写IOC容器——属性填充特别篇:SpEL表达式
第八章 Spring之假如让你来写IOC容器——拓展篇
第九章 Spring之源码阅读——环境搭建篇
第十章 Spring之源码阅读——IOC篇


文章目录

  • Spring源码阅读目录
  • 前言
  • 尝试动手写IOC容器
      • 第十版 bean对象作用域:Scope接口
          • 循环依赖
            • 循环依赖的产生
            • 循环依赖的解决
      • 第十一版 对象的属性填充
  • 总结


前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第1张图片

    所以决定花点时间研究研究Spring的源码。主要参考的书籍是:《Spring源码深度解析(第2版)》、《Spring揭秘》、《Spring技术内幕:深入解析Spring架构与设计原理(第2版)》


    书接上回,在上篇 第五章 Spring之假如让你来写IOC容器——BeanFactory和FactoryBean 已经提取出BeanFactory接口,还添加了FactoryBean作为拓展,给用户添加自己创建bean的方式。接下来看看A君又会面临什么样的挑战吧

尝试动手写IOC容器

    出场人物:A君(苦逼的开发)、老大(项目经理)

    背景:老大要求A君在一周内开发个简单的IOC容器

    前情提要:在用户的要求下,A君 新增了FactoryBean接口作为拓展,让用户自己能自由的创建对象。。。

第十版 bean对象作用域:Scope接口

    随着FactoryBean的改造完毕,A君暗自的祷告:别再提新需求,别再提新需求。A君是真的被折腾怕了,一提新需求,加班指定是跑不掉的

    屋漏偏逢连夜雨,船迟又遇打头风。还没过多久,老大又找到A君了,不过这回倒不是新需求,而是性能相关的问题。一时间,A君不知道是该高兴还是该骂娘。用户提到:对象创建过于频繁,重复创建导致内存占用大,GC频率高

    听到这个问题,A君一下子想到了很多:spring默认使用单例,单例可以提高效率,对象重用。凡此种种,不都是为了减少内存开销,提高效率吗?前人的智慧已经摆在那里了,没有比这更好的解决方案了

    有了方案后,A君心里大定,不怕没代码,就怕没思路。不过还是得考虑下拓展,碰到这么难缠的客户,鬼知道之后还会提什么奇奇怪怪的需求。基于这般考虑,A君定义了Scope接口,作为bean的作用域,其代码如下:

import com.hqd.ch03.v10.factory.ObjectFactory;

/**
 * bean对象作用域
 */
public interface Scope {
    /**
     * 获取对象
     *
     * @param name
     * @param factory
     * @return
     */
    Object get(String name, ObjectFactory<?> factory);

    /**
     * 删除对象
     *
     * @param name
     * @return
     */
    Object remove(String name);
}

这里会涉及到ObjectFactory接口,这个接口只是单纯为了bean对象延迟创建,可以通过ObjectFactory接口获取到bean对象,毕竟创建对象还是得容器来做,至于放哪里嘛?交由用户决定,ObjectFactory接口代码如下:


@FunctionalInterface
public interface ObjectFactory<T> {
    /**
     * 获取对象
     *
     * @return
     */
    T getObject();
}

    定义完Scope接口和ObjectFactory接口之后,A君又遇到一个问题:要怎么注册用户自定义的作用域?当然可以在BeanFactory接口中添加个注册作用域的方法,但是这又违反了接口单一性原则,BeanFactory接口只规定了IOC容器最基本的功能,作用域这些属于拓展的内容了,放在BeanFactory接口里显然不合适

    既然没办法直接写进BeanFactory接口中,那就新加个接口吧!于是,A君定义了ConfigurableBeanFactory接口,对BeanFactory进行了拓展,负责注册各种用户自定义的内容,代码如下:

import com.hqd.ch03.v10.config.Scope;

public interface ConfigurableBeanFactory extends BeanFactory {
    /**
     * 单例
     */
    String SCOPE_SINGLETON = "singleton";
    /**
     * 多利
     */
    String SCOPE_PROTOTYPE = "prototype";

    /**
     * 注册作用域
     *
     * @param scopeName
     * @param scope
     */
    void registerScope(String scopeName, Scope scope);
}

    解决完注册的问题后,接下来就是单例的问题了。想要实现单例,就需要把创建好的bean对象缓存起来,保证bean对象只创建一次,后边都从缓存里边取。老样子,A君定了单例缓存接口SingletonBeanRegistry,代码如下:


/**
 * 单例缓存接口
 */
public interface SingletonBeanRegistry {
    /**
     * 获取单例对象
     *
     * @param beanName
     * @return
     */
    Object getSingleton(String beanName);

    /**
     * 是否包含对象
     *
     * @param beanName
     * @return
     */
    boolean containsSingleton(String beanName);

    /**
     * 添加单例对象
     *
     * @param beanName
     * @param obj
     */
    void registerSingleton(String beanName, Object obj);
}

按照以往惯例,有了接口后,就是提供默认实现了。不过此时,A君却遇到一个面试经常遇到的问题:循环依赖

循环依赖
循环依赖的产生

    循环依赖产生的原因比较简单,无非就是A引用了B,而B又引用了A,造成了闭环,导致出现死循环。这种情况在实际开发中没办法完全杜绝,也许人家真的有这种需要呢?

循环依赖的解决

    在讨论解决循环依赖之前,先明确不能解决的情况

  • 构造器中包含循环依赖,创建对象时,你猜是先栈溢出还是堆溢出。对象都没法创建出来,更别说解决了

  • 对象不是单例对象,这点也好说,不是单例对象,意味着无法对对象进行缓存,每次都是新的对象,还是个死结

明确完不能解决的情况,能解决的情况就呼之欲出了,必须是构造器中不包含循环依赖的单例对象。如果只是单纯的解决,A君心里已经有了答案,无非就是先创建一个空对象,再把这个空对象放进缓存里就行了,这样只要一级缓存就够了。这样解决虽然方便,但是却会破坏原有的流程,原本容器是:创建对象=>填充属性=>放入缓存,如今却要因为一个特殊情况,而改变整个bean对象的创建流程,变成:创建对象=>放入缓存=>填充属性,这不是A君所愿意看到,破坏bean创建流程,意味着到时候在流程上做拓展会很麻烦,A君希望只有真正存在循环依赖的情况才进行特殊处理

    为此,A君想到了多级缓存

  • 一级缓存存放初始化完成的bean对象

  • 二级缓存存放为初始化的对象用来解决循环依赖,并且保证单例

  • 三级缓存用以存放工厂延迟加载

    理清思绪后,就能给SingletonBeanRegistry接口提供默认实现了。A君定义了DefaultSingletonBeanRegistry类,代码如下:


import com.hqd.ch03.v10.factory.ObjectFactory;
import com.hqd.ch03.v10.registry.SingletonBeanRegistry;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {
    /**
     * 三级缓存,只有Bean存在循环依赖时触发,做特殊处理,其他Bean按照正常初始化流程
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();
    /**
     * 二级缓存,保证三级缓存使用ObjectFactory创建出来的对象唯一
     */
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    /**
     * 一级缓存,存储初始化完的单例对象
     */
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>();


    @Override
    public Object getSingleton(String beanName) {
        Object single = singletonObjects.get(beanName);
        if (single == null) {
            single = earlySingletonObjects.get(beanName);
            if (single == null) {
                ObjectFactory<?> objectFactory = this.singletonFactories.get(beanName);
                if (objectFactory != null) {
                    single = objectFactory.getObject();
                    this.earlySingletonObjects.put(beanName, single);
                    this.singletonFactories.remove(beanName);
                }
            }
        }
        return single;
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        /**
         * 只有bean未初始化完成才能加入三级缓存
         */
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    protected void addSingleton(String beanName, Object obj) {
        this.singletonObjects.put(beanName, obj);
        this.earlySingletonObjects.remove(beanName);
        this.singletonFactories.remove(beanName);
    }

    @Override
    public boolean containsSingleton(String beanName) {
        return singletonObjects.containsKey(beanName);
    }

    @Override
    public void registerSingleton(String beanName, Object obj) {
        singletonObjects.put(beanName, obj);
    }
}

    有了单例缓存的实现类,接着需要对SpringImitation进行一番改造。SpringImitation需要继承DefaultSingletonBeanRegistry类,并且实现ConfigurableBeanFactory接口,改造也不多,就是提供注册作用域Scope的实现,根据不同的作用域存放对象,比如:单例存放在缓存中,多例不缓存。另外的改造点就是获取对象时,先去缓存获取即可,改造如下:

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第2张图片

注册Scope作用域,如下:

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第3张图片

完整代码:


import com.hqd.ch03.v10.config.BeanDefinition;
import com.hqd.ch03.v10.config.MutablePropertyValues;
import com.hqd.ch03.v10.config.Scope;
import com.hqd.ch03.v10.factory.BeanFactory;
import com.hqd.ch03.v10.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v10.factory.FactoryBean;
import com.hqd.ch03.v10.io.ResourceLoader;
import com.hqd.ch03.v10.io.support.DefaultResourceLoader;
import com.hqd.ch03.v10.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v10.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v10.registry.support.SimpleBeanDefinitionRegistry;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

public abstract class SpringImitationV10 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
    protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
    /**
     * 单例缓存
     */
    protected Map<String, Scope> scopeCache = new HashMap<>();
    /**
     * 资源加载器
     */
    protected ResourceLoader resourceLoader = new DefaultResourceLoader();

    public static boolean isFactoryDereference(String name) {
        return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    }


    /**
     * 处理传入的name,去除&开头
     *
     * @param name
     * @return
     */
    protected String transformedBeanName(String name) {
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        String beanName = name;
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    }

    /**
     * 区分正常bean还是FactoryBean
     *
     * @param obj
     * @param name
     * @param beanName
     * @param bd
     * @return
     */
    protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
        if (isFactoryDereference(name)) {
            if (!(obj instanceof FactoryBean)) {
                throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
            }
            return obj;
        }
        if (obj instanceof FactoryBean) {
            return ((FactoryBean) obj).getObject();
        }
        return obj;
    }

    private void initBean(Object bean, BeanDefinition bd) {
        try {
            MutablePropertyValues properties = bd.getProperties();
            properties.getProperties().forEach(propertyValue -> {
                boolean isRef = propertyValue.isRef();
                Object value = propertyValue.getValue();
                if (isRef) {
                    value = getBean((String) value);
                }
                try {
                    BeanUtils.setProperty(bean, propertyValue.getName(), value);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private Object createBean(BeanDefinition bd, String beanName) {
        try {
            Object instance = null;
            if (bd.isSingleton()) {//单例
                instance = getSingleton(beanName);
                if (instance == null) {
                    instance = doCreate(bd, beanName);
                }
                addSingleton(beanName, instance);
            } else if (bd.isPrototype()) {//多例
                instance = doCreate(bd, beanName);
            } else {//自定义作用域
                String scopeName = bd.getScope();
                Scope scope = scopeCache.get(scopeName);
                if (scope == null) {
                    throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
                }
                instance = scope.get(beanName, () -> doCreate(bd, beanName));
            }
            return instance;

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected Object doCreate(BeanDefinition bd, String beanName) {
        String beanClass = bd.getBeanClass();
        try {
            Object instance = Class.forName(beanClass).getConstructor().newInstance();
            addSingletonFactory(beanName, () -> instance);
            //doSomething
            initBean(instance, bd);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void registerScope(String scopeName, Scope scope) {
        if (StringUtils.isNotBlank(scopeName) && scope != null) {
            scopeCache.put(scopeName, scope);
        }
    }

    @Override
    public <T> T getBean(String name, Class<T> clazz) {
        return (T) doGetBean(name);
    }

    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }

    protected Object doGetBean(String name) {
        String beanName = transformedBeanName(name);
        BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
        Object bean = createBean(bd, beanName);
        bean = getObjectForBeanInstance(bean, name, beanName, bd);
        return bean;
    }


    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

    都做好后,又到了愉快地测试环节,这次测试需要测试两部分内容,一个是自定义作用域,另外一个是循环依赖。先从循环依赖开始吧,先添加一个IdCard类,如下:


import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class IdCard {
    private String id;
    private User user;
}

接着修改下User类,形成循环依赖,如下:

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {
    private String name;
    private int age;
    private IdCard idCard;


    public User(String name, IdCard idCard) {
        this.name = name;
        this.idCard = idCard;
    }

}

    然后是自定义作用域ThreadScope类,bean和线程绑定,相同的线程才能获取到相同的bean对象。代码如下:


import com.hqd.ch03.v10.config.Scope;
import com.hqd.ch03.v10.factory.ObjectFactory;

import java.util.HashMap;
import java.util.Map;

/**
 * 线程作用域
 */
public class ThreadScope implements Scope {
    private final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            return new HashMap<>();
        }
    };

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Map scope = threadLocal.get();
        Object obj = scope.get(name);
        if (obj == null) {
            obj = objectFactory.getObject();
            scope.put(name, obj);
        }
        return obj;
    }

    @Override
    public Object remove(String name) {
        return threadLocal.get().remove(name);
    }

}

接下再修改下配置文件,如下:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="idCard" class="com.hqd.ch03.bean.IdCard">
        <property name="id" value="123456"/>
        <property name="user" ref="user0"/>
    bean>
    <bean id="user0" class="com.hqd.ch03.bean.User" scope="singleton">
        <property name="idCard" ref="idCard"/>
        <property name="name" value="法外狂徒-张三"/>
        <property name="age" value="14"/>
    bean>
    <bean id="user1" class="com.hqd.ch03.bean.User" scope="prototype">
        <property name="name" value="法外狂徒-张三"/>
        <property name="age" value="14"/>
    bean>
    <bean id="user2" class="com.hqd.ch03.bean.User" scope="thread">
        <property name="name" value="法外狂徒-张三"/>
        <property name="age" value="14"/>
    bean>

    <bean id="user3" class="com.hqd.ch03.bean.User" scope="singleton">
        <constructor-arg index="0" value="ikun"/>
        <constructor-arg index="1" value="15"/>
        <constructor-arg index="2" ref="idCard"/>
    bean>
    <bean id="user4" class="com.hqd.ch03.bean.User" scope="singleton">
        <constructor-arg index="0" value="ikun"/>
        <constructor-arg index="1" ref="idCard"/>
        <property name="age" value="15"/>
    bean>
beans>

    都准备好后,编写测试方法,如下:


    @Test
    public void v10() {
        System.out.println("############# 第十版:scope作用域 #############");
        SpringImitationV10 beanFactory = new SpringImitationV10Xml("classpath:v10/bean.xml");
        Scope threadScope = new ThreadScope();
        //注册自定义作用域
        beanFactory.registerScope("thread", threadScope);
        for (int i = 0; i < 5; i++) {
            String beanName = "user";
            for (int j = 0; j < 2; j++) {
                User bean = beanFactory.getBean(beanName + j, User.class);
                System.out.println(beanName + j + ":" + bean);

            }
        }
        //使用容器获取bean
        try {
            for (int i = 0; i < 2; i++) { //@2
                new Thread(() -> {
                    System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
                    System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
                }).start();

                TimeUnit.SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

打印结果如下:

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第4张图片

可以看到,IdCard和User都有值了,并且分别引用了对方

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第5张图片

根据打印结果可以看出,user0是单例模式,所以每次获取到的对象hashCode都一样。user1是多例,每次获取的对象都不一样。user2是自定义线程作用域,所以同一个线程内获取到的对象一样

    “好嘞,总算搞定了”。虽然被折腾的很难受,A君还是有所收获的,从之前的懵懵懂懂,到现在越来越清晰起来了。或许,以后可以开始看spring的源码了

第十一版 对象的属性填充

    “他来了,他又来了”,A君看到老大过来都要崩溃了。老大当然不是来给A君升职加薪,而是带着用户的新需求来的。

    “这次又是啥需求”,A君都绝望了,屁股还没坐热,新需求就过来了

    “用户希望可以自定义类属性解析,现在的类属性解析不符合他们的要求,又没法拓展”,老大留下几句话后就飘然离去。只剩下A君在原地凌乱了:之前图省事,直接用 BeanUtils 实现了,哪知道现在又玩这出

    不过,A君这次到不怎么慌乱,毕竟不能白被虐这么多次。说是类属性解析,其实就是再设置类属性时候,回调用户自定义的实现,相当于类型转换:把String类型转成各个不同的类型。基于之前的经验,无非就是定义一个接口给用户做定制化类型转换,再定义个注册类型转换的接口就完事了

    这次转换接口不需要A君自己定义,jdk有现成的 PropertyEditor 接口,还有做好封装的 PropertyEditorSupport 类,A君只需要定义了接口 PropertyEditorRegistry 来注册自定义实现即可,代码如下:


import java.beans.PropertyEditor;

/**
 * 属性转换器注册接口
 */
public interface PropertyEditorRegistry {
    /**
     * 注册转换器
     *
     * @param requiredType
     * @param propertyEditor
     */
    void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);

    /**
     * 查找类型转化器
     *
     * @param requiredType
     * @return
     */
    PropertyEditor findCustomEditor(Class<?> requiredType);
}

有了注册接口后,老样子,提供个默认实现,代码如下:


import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;
import com.hqd.ch03.v11.beans.propertyeditors.CustomBooleanEditor;
import com.hqd.ch03.v11.beans.propertyeditors.CustomNumberEditor;

import java.beans.PropertyEditor;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 类型转化器注册类
 */
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
    /**
     * 用户注册编辑器
     */
    private Map<Class<?>, PropertyEditor> customEditors = new LinkedHashMap<>();
    /**
     * 默认编辑器
     */
    private Map<Class<?>, PropertyEditor> defaultEditors = new LinkedHashMap<>();

    public PropertyEditorRegistrySupport() {
        this.createDefaultEditors();
    }

    public PropertyEditor getDefaultEditor(Class<?> requiredType) {
        return this.defaultEditors.get(requiredType);
    }

    public PropertyEditor findCustomEditor(Class<?> requiredType) {
        return this.customEditors.get(requiredType);
    }

    /**
     * 注册系统默认转换器
     */
    private void createDefaultEditors() {
        this.defaultEditors.put(boolean.class, new CustomBooleanEditor());
        this.defaultEditors.put(Boolean.class, new CustomBooleanEditor());
        this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class));
        this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class));
        this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class));
        this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class));
    }

    @Override
    public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
        this.customEditors.put(requiredType, propertyEditor);
    }
}

    有了注册接口默认实现后,还有个问题:怎么寻找到对应的类型转换器(PropertyEditor),匹配规则,优先级要怎么定义?

    原本A君打算直接在 PropertyEditorRegistry 接口中定义方法就完事了。“接口单一,接口单一”,A君默认了几遍,硬生生的忍了下来。不然回头又要被老大叼了。基于这般想法,A君定义了 TypeConverter 接口,负责从注册器中(PropertyEditorRegistry)中寻找到对应的类型转换器(PropertyEditor),代码如下:

import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;

/**
 * 类型转换接口,通常与{@link PropertyEditorRegistry}连用
 * {@link PropertyEditorRegistry}负责具体转换器的注册,删除
 * TypeConverter负责寻找具体的转换器进行转换
 */
public interface TypeConverter {
    <T> Object convertIfNecessary(Object value, Class<T> requiredType);
}

定义好接口后,A君思考了下,觉得:用户毕竟是大哥,用户说了算。匹配规则就先匹配用户自定义的类型转换器(PropertyEditor),找不到,在找系统默认的转换器。就有了 TypeConverterSupport 实现,代码如下:

import com.hqd.ch03.v11.beans.TypeConverter;
import com.hqd.ch03.v11.beans.property.support.PropertyEditorRegistrySupport;

import java.beans.PropertyEditor;

public class TypeConverterSupport extends PropertyEditorRegistrySupport implements TypeConverter {
    @Override
    public <T> T convertIfNecessary(Object value, Class<T> requiredType) {
        PropertyEditor editor = findCustomEditor(requiredType);
        if (editor == null) {
            editor = getDefaultEditor(requiredType);
        }
        if (editor == null) {
            return (T) value;
        }
        if (value instanceof String) {
            editor.setAsText((String) value);
        } else {
            editor.setValue(value);
        }
        return (T) editor.getValue();
    }
}

    做好这些后,接下来就是想办法把 TypeConverterSupport 和bean关联起来。为此,A君先定义了个接口:PropertyAccessor,用来操作bean属性信息,代码如下:

import com.hqd.ch03.v11.config.PropertyValue;

/**
 * bean操作属性接口
 */
public interface PropertyAccessor {
    /**
     * 根据属性名获取属性类型
     *
     * @param propertyName 属性名
     * @return
     */
    Class<?> getPropertyType(String propertyName);

    /**
     * 根据属性名获取属性值
     *
     * @param propertyName 属性名
     * @return
     */
    Object getPropertyValue(String propertyName);

    /**
     * 设置属性值
     *
     * @param propertyName 属性名
     * @param value        属性值
     * @param isRef        是否引用类型
     */
    void setPropertyValue(String propertyName, Object value, boolean isRef);

    /**
     * 通过{@link PropertyValue}设置属性值
     *
     * @param pv
     */
    void setPropertyValue(PropertyValue pv);
}

再包装个 AbstractPropertyAccessor 抽象类,抽取公共实现,如下:


import com.hqd.ch03.v11.beans.property.PropertyAccessor;
import com.hqd.ch03.v11.beans.support.TypeConverterSupport;
import com.hqd.ch03.v11.config.PropertyValue;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements PropertyAccessor {
    /**
     * bean对象
     */
    protected Object bean;
    protected Class<?> beanClass;

    public AbstractPropertyAccessor(Object bean) {
        this.bean = bean;
        this.beanClass = bean.getClass();
    }

    @Override
    public void setPropertyValue(PropertyValue pv) {
        setPropertyValue(pv.getName(), pv.getValue(), pv.isRef());
    }

    protected boolean checkType(String propertyName, Object value) {
        if (value == null) {
            return true;
        }
        Class<?> targetClass = value.getClass();
        Class<?> propertyType = getPropertyType(propertyName);
        return propertyType.isAssignableFrom(targetClass);

    }

    @Override
    public void setPropertyValue(String propertyName, Object value, boolean isRef) {
        Object newVal = value;
        Class<?> propertyType = getPropertyType(propertyName);
        if (isRef) {
            if (!checkType(propertyName, value)) {
                throw new RuntimeException(String.format("%s is not  subclass of %s", value, propertyName));
            }
        } else {
            newVal = convertIfNecessary(value, propertyType);
        }
        try {
            PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
            Method methodName = pd.getWriteMethod();
            methodName.invoke(bean, newVal);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public Class<?> getPropertyType(String propertyName) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
            return pd.getPropertyType();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getPropertyValue(String propertyName) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(propertyName, beanClass);
            return pd.getReadMethod().invoke(bean);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

    还需要个接口 BeanWrapper 用来操作bean,并且包含类型转换。代码如下:

import com.hqd.ch03.v11.beans.property.PropertyAccessor;
import com.hqd.ch03.v11.beans.property.PropertyEditorRegistry;

public interface BeanWrapper extends PropertyEditorRegistry, PropertyAccessor, TypeConverter {
    /**
     * 获取bean的实例对象
     *
     * @return
     */
    Object getWrappedInstance();

    Class<?> getWrappedClass();
}

默认实现 BeanWrapperImpl ,代码如下:


import com.hqd.ch03.v11.beans.property.support.AbstractPropertyAccessor;

public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {


    public BeanWrapperImpl(Object bean) {
        super(bean);
    }

    @Override
    public Object getWrappedInstance() {
        return bean;
    }

    @Override
    public Class<?> getWrappedClass() {
        return beanClass;
    }

}

    现在什么都准备好了,提供个方法给用户注册自定义类型转换器即可。为此,A君 修改了 ConfigurableBeanFactory 接口,如下:

import com.hqd.ch03.v11.config.Scope;

import java.beans.PropertyEditor;

public interface ConfigurableBeanFactory extends BeanFactory {
    /**
     * 单例
     */
    String SCOPE_SINGLETON = "singleton";
    /**
     * 多利
     */
    String SCOPE_PROTOTYPE = "prototype";

    void registerScope(String scopeName, Scope scope);

    void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);
}

接着就是 ConfigurableBeanFactory 的实现 SpringImitationSpringImitation改动并不多,只需在初始化bean对象时,调用 BeanWrapper 的方法即可,如下:

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第6张图片

initBeanWrapper方法如下:
第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第7张图片

完整代码


import com.hqd.ch03.v11.beans.BeanWrapper;
import com.hqd.ch03.v11.beans.BeanWrapperImpl;
import com.hqd.ch03.v11.config.BeanDefinition;
import com.hqd.ch03.v11.config.MutablePropertyValues;
import com.hqd.ch03.v11.config.Scope;
import com.hqd.ch03.v11.factory.BeanFactory;
import com.hqd.ch03.v11.factory.ConfigurableBeanFactory;
import com.hqd.ch03.v11.factory.FactoryBean;
import com.hqd.ch03.v11.io.ResourceLoader;
import com.hqd.ch03.v11.io.support.DefaultResourceLoader;
import com.hqd.ch03.v11.registry.BeanDefinitionRegistry;
import com.hqd.ch03.v11.registry.support.DefaultSingletonBeanRegistry;
import com.hqd.ch03.v11.registry.support.SimpleBeanDefinitionRegistry;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.PropertyEditor;
import java.util.HashMap;
import java.util.Map;

public abstract class SpringImitationV11 extends DefaultSingletonBeanRegistry implements ConfigurableBeanFactory {
    private final Map<Class<?>, Class<? extends PropertyEditor>> customEditors = new HashMap<>();
    protected BeanDefinitionRegistry beanDefinitionRegistry = new SimpleBeanDefinitionRegistry();
    /**
     * 单例缓存
     */
    protected Map<String, Scope> scopeCache = new HashMap<>();
    /**
     * 资源加载器
     */
    protected ResourceLoader resourceLoader = new DefaultResourceLoader();

    public static boolean isFactoryDereference(String name) {
        return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
    }

    @Override
    public void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass) {
        customEditors.put(requiredType, propertyEditorClass);
    }

    /**
     * 处理传入的name,去除&开头
     *
     * @param name
     * @return
     */
    protected String transformedBeanName(String name) {
        if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
            return name;
        }
        String beanName = name;
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    }

    /**
     * 区分正常bean还是FactoryBean
     *
     * @param obj
     * @param name
     * @param beanName
     * @param bd
     * @return
     */
    protected Object getObjectForBeanInstance(Object obj, String name, String beanName, BeanDefinition bd) {
        if (isFactoryDereference(name)) {
            if (!(obj instanceof FactoryBean)) {
                throw new RuntimeException(String.format("%s 未实现FactoryBean接口", beanName));
            }
            return obj;
        }
        if (obj instanceof FactoryBean) {
            return ((FactoryBean) obj).getObject();
        }
        return obj;
    }

    private void initBean(BeanWrapper beanWrapper, BeanDefinition bd) {
        try {
            MutablePropertyValues properties = bd.getProperties();
            properties.getProperties().forEach(propertyValue -> {
                boolean isRef = propertyValue.isRef();
                Object value = propertyValue.getValue();
                if (isRef) {
                    value = getBean((String) value);
                }
                try {
                    beanWrapper.setPropertyValue(propertyValue.getName(), value, isRef);
                    //BeanUtils.setProperty(bean, propertyValue.getName(), value);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    private Object createBean(BeanDefinition bd, String beanName) {
        try {
            Object instance = null;
            if (bd.isSingleton()) {//单例
                instance = getSingleton(beanName);
                if (instance == null) {
                    instance = doCreate(bd, beanName);
                }
                addSingleton(beanName, instance);
            } else if (bd.isPrototype()) {//多例
                instance = doCreate(bd, beanName);
            } else {//自定义作用域
                String scopeName = bd.getScope();
                Scope scope = scopeCache.get(scopeName);
                if (scope == null) {
                    throw new RuntimeException(String.format("[%s]未注册scope", scopeName));
                }
                instance = scope.get(beanName, () -> doCreate(bd, beanName));
            }
            return instance;

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected Object doCreate(BeanDefinition bd, String beanName) {
        String beanClass = bd.getBeanClass();
        try {
            Object instance = Class.forName(beanClass).getConstructor().newInstance();
            BeanWrapper beanWrapper = new BeanWrapperImpl(instance);
            initBeanWrapper(beanWrapper);
            addSingletonFactory(beanName, () -> beanWrapper.getWrappedInstance());
            //doSomething
            initBean(beanWrapper, bd);
            return instance;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    protected void initBeanWrapper(BeanWrapper bw) {
        /**
         * 注册自定义类型转换器
         *
         */
        registerCustomEditors(bw);
    }

    /**
     * 是否应该区分全局转换器和局部转换器
     * 全局为Factory级别,局部为Bean级别
     *
     * @param bw
     */
    protected void registerCustomEditors(BeanWrapper bw) {
        if (MapUtils.isNotEmpty(customEditors)) {
            customEditors.forEach((key, val) -> {
                try {
                    bw.registerCustomEditor(key, val.getConstructor().newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }

    @Override
    public void registerScope(String scopeName, Scope scope) {
        if (StringUtils.isNotBlank(scopeName) && scope != null) {
            scopeCache.put(scopeName, scope);
        }
    }

    @Override
    public <T> T getBean(String name, Class<T> clazz) {
        return (T) doGetBean(name);
    }

    @Override
    public Object getBean(String name) {
        return doGetBean(name);
    }

    protected Object doGetBean(String name) {
        String beanName = transformedBeanName(name);
        BeanDefinition bd = beanDefinitionRegistry.getBeanDefinition(beanName);
        Object bean = createBean(bd, beanName);
        bean = getObjectForBeanInstance(bean, name, beanName, bd);
        return bean;
    }


    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
}

    改造完毕后,又是测试的时间了。先准备个IKun对象,如下:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class IKun extends User {
    private boolean handsome;
    private double deposit;
    private String[] hobbies;
}

xml配置如下:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="idCard" class="com.hqd.ch03.bean.IdCard">
        <property name="id" value="123456"/>
        <property name="user" ref="ikun"/>
    bean>
    <bean id="ikun" class="com.hqd.ch03.bean.IKun" scope="singleton">
        <property name="idCard" ref="idCard"/>
        <property name="name" value="法外狂徒-张三"/>
        <property name="age" value="14"/>
        <property name="handsome" value="true"/>
        <property name="deposit" value="6.66"/>
        <property name="hobbies" value="唱,跳,rap"/>
    bean>
beans>

IKun主要爱好是:唱,跳,rap,以逗号作为分割符,设置属性时候,按逗号进行切割。所以自定义类型转换器 CustomArrayEditor,代码如下:

import java.beans.PropertyEditorSupport;

public class CustomArrayEditor extends PropertyEditorSupport {
    @Override
    public void setAsText(String text) throws IllegalArgumentException {
        setValue(text.split(","));
    }
}

测试类需要注册一下 CustomArrayEditor 即可,代码如下:

 @Test
    public void v11() {
        System.out.println("############# 第十一版:属性填充 #############");
        SpringImitationV11 beanFactory = new SpringImitationV11Xml("classpath:v11/bean.xml");
        beanFactory.registerCustomEditor(String[].class, CustomArrayEditor.class);
        IKun iKun = beanFactory.getBean("ikun", IKun.class);
        System.out.println("加载标签对象:" + iKun);
    }

打印结果,如下:

第六章 Spring之假如让你来写IOC容器——Scope和属性填充_第8张图片

Ikun的兴趣爱好已经成功的设置进去了

    “这是在帮我了解spring吗?”,A君 不禁感慨。客户虽然难缠额一批,也是因为他们难缠,让 A君 对spring的理解比以往深入了不少

    随着A君 代码上板,这次的自己实现IOC容器已经进入了收尾阶段。按照老大的话说:还需要留些拓展功能,就能结束了


总结

    正所谓树欲静而风不止,欲知后事如何,请看下回分解(✪ω✪)

你可能感兴趣的:(Spring,spring,java,面试)