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

Spring源码阅读目录

第一部分——IOC篇

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

第二部分——AOP篇

第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的内容——概念篇
第十三章 Spring之假如让你来写AOP——AOP联盟篇
第十四章 Spring之假如让你来写AOP——雏形篇
第十五章 Spring之假如让你来写AOP——Joinpoint(连接点)篇
第十六章 Spring之假如让你来写AOP——Pointcut(切点)篇
第十七章 Spring之假如让你来写AOP——Advice(通知)上篇
第十八章 Spring之假如让你来写AOP——Advice(通知)下篇
第十九章 Spring之假如让你来写AOP——番外篇:Spring早期设计
第二十章 Spring之假如让你来写AOP——Aspect(切面)篇
第二十一章 Spring之假如让你来写AOP——Weaver(织入器)篇
第二十二章 Spring之假如让你来写AOP——Target Object(目标对象)篇
第二十三章 Spring之假如让你来写AOP——融入IOC容器篇
第二十四章 Spring之源码阅读——AOP篇

第三部分——事务篇

第二十五章 Spring之曾经的老朋友——事务
第二十六章 Spring之假如让你来写事务——初稿篇
第二十七章 Spring之假如让你来写事务——铁三角篇
第二十八章 Spring之假如让你来写事务——属性篇
第二十九章 Spring之假如让你来写事务——状态篇
第三十章 Spring之假如让你来写事务——管理篇
第三十一章 Spring之假如让你来写事务——融入IOC容器篇
第三十二章 Spring之源码阅读——事务篇

第四部分——MVC篇

第三十三章 Spring之梦开始的地方——MVC
第三十四章 Spring之假如让你来写MVC——草图篇
第三十五章 Spring之假如让你来写MVC——映射器篇
第三十六章 Spring之假如让你来写MVC——拦截器篇
第三十七章 Spring之假如让你来写MVC——控制器篇
第三十八章 Spring之假如让你来写MVC——适配器篇
第三十九章 Spring之假如让你来写MVC——番外篇:类型转换
第四十章 Spring之假如让你来写MVC——ModelAndView篇
第四十一章 Spring之假如让你来写MVC——番外篇:数据绑定
第四十二章 Spring之假如让你来写MVC——视图篇
第四十三章 Spring之假如让你来写MVC——上传文件篇
第四十四章 Spring之假如让你来写MVC——异常处理器篇
第四十五章 Spring之假如让你来写MVC——国际化篇
第四十六章 Spring之假如让你来写MVC——主题解析器篇
第四十七章 Spring之假如让你来写MVC——闪存管理器篇
第四十八章 Spring之假如让你来写MVC——请求映射视图篇
第四十九章 Spring之假如让你来写MVC——番外篇:属性操作
第五十章 Spring之假如让你来写MVC——融入IOC容器篇
第五十一章 Spring之源码阅读——MVC篇

第五部分——Boot篇

第五十二章 Spring之再进一步——Boot
第五十三章 Spring之假如让你来写Boot——环境篇
第五十四章 Spring之假如让你来写Boot——注解篇(上)
第五十五章 Spring之假如让你来写Boot——注解篇(下)
第五十六章 Spring之假如让你来写Boot——SPI篇
第五十七章 Spring之假如让你来写Boot——配置文件篇(上)
第五十八章 Spring之假如让你来写Boot——配置文件篇(下)
第五十九章 Spring之假如让你来写Boot——番外篇:再谈Bean定义
第六十章 Spring之假如让你来写Boot——自动装配篇
第六十一章 Spring之假如让你来写Boot——番外篇:杂谈Starter
第六十二章 Spring之假如让你来写Boot——番外篇:重构BeanFactory
第六十三章 Spring之假如让你来写Boot——番外篇:再谈ApplicationContext
第六十四章 Spring之假如让你来写Boot——内嵌Web容器篇
第六十五章 Spring之假如让你来写Boot——Main方法启动篇
第六十六章 Spring之最终章——结语篇



前言

    对于Spring一直都是既熟悉又陌生,说对它熟悉吧,平时用用没啥问题,但面试的时候被问的一脸懵逼,就很尴尬,都不好意思在简历上写着熟悉Spring了
在这里插入图片描述

    所以决定花点时间研究研究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的实现,根据不同的作用域存放对象,比如:单例存放在缓存中,多例不缓存。另外的改造点就是获取对象时,先去缓存获取即可,改造如下:

在这里插入图片描述

注册Scope作用域,如下:

在这里插入图片描述

完整代码:


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();
        }
    }

打印结果如下:

在这里插入图片描述

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

在这里插入图片描述

根据打印结果可以看出,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 的方法即可,如下:

在这里插入图片描述

initBeanWrapper方法如下:
在这里插入图片描述

完整代码


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);
    }

打印结果,如下:

在这里插入图片描述

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

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

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


总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

穷儒公羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值