一起来写个SpringBoot[6] — — 拓展IOC的实现

项目地址:https://github.com/xiaogou446/jsonboot
使用branch:feature/expandIOC
命令行:git checkout feature/expandIOC

拓展IOC

在上一节中,我们简单的实现了IOC容器。我们通过遍历扫描到类文件,生成未完成的bean,后遍历未完成的bean进行依赖注入。这种做法也有许多的问题。比如在将所有的class类文件都实例化后,再进行依赖注入,如果一个依赖注入的过程出现了问题,导致后续的依赖注入无法进行,那么最终使用的则都是未完成的bean,在真正的使用中会出问题。其次我们从始至终使用的容器都是BEANS一个容器,不管是未完成的bean还是真正初始化完成bean,都是存储在这个容器中。如果某个bean在初始化期间出现了异常,使未初始化完成的bean存在了最终的容器中,如果在运行过程中出现异常,那排查的难度可想而知。也正是使用一个BEANS容器,使得不同阶段的bean没有区分性,无法确定bean执行的状态和运行阶段。针对以上问题,本节对IOC容器进行拓展。

我们定义三级缓存来存储beans,三级缓存,也就是最基础的,仅仅是对类对象进行实例化,还没有进行依赖注入以及进行aop校验的未完成对象。二级缓存,用来进行对bean是否需要进行aop代理进行判断校验,如果需要进行aop,则将bean进行aop代理,生成代理对象存入二级缓存中,也是用于如果发生循环依赖,且循环依赖的对象需要aop代理时,那注入的对象需要是一个代理对象,会在注入时进行aop的代理,存入二级缓存。一级缓存,最终bean所储备的地方,是真正能够直接使用的对象。我们额外定义一个列表CURRENT_IN_CREATION,用来表示当前正在实例化的对象。

    /**
     * 设置存放Bean的map 一级缓存
     */
    public static final Map<String, Object> BEANS = new ConcurrentHashMap<>(128);

    /**
     * 存放暂时的aop对象 二级缓存
     */
    public static final Map<String, Object> EARLY_BEANS = new HashMap<>(16);

    /**
     * 存放实例化并未初始化的基础对象 三级缓存
     */
    public static final Map<String, Object> BASIC_OBJECTS = new HashMap<>(16);

    /**
     * 记录当前正在实例化的对象名
     */
    public static final List<String> CURRENT_IN_CREATION = new LinkedList<>();

初始开始还是遍历已经收录到CLASSES的类,进行实例化,beanName代表该类定义的名称,如果有 @Component注解,就使用注解上的名称,如果没有注解就使用默认的类名称。

    public static void loadBeans(){
        Map<Class<? extends Annotation>, Set<Class<?>>> classes = ApplicationContext.CLASSES;
        classes.forEach((key, value) -> {
                for (Class<?> aClass : value){
                    String beanName = aClass.getName();
                    if (key == Component.class){
                        Component component = aClass.getAnnotation(Component.class);
                        beanName = StringUtils.isBlank(component.value()) ? beanName : component.value();
                    }
                    buildBeans(beanName, aClass);
                }
        });
    }

具体就在于buildBeans(beanName, aClass); 这个方法这是实例化开始的第一步。代码上也标记了比较详细的注释。我们仅对实现类进行实例化,如果是接口,就报出异常。优先从一级缓存中取出完备的bean,如果能够取到,代表这个类在别的时刻已经被实例化并初始化完成了,那么直接取出来使用。如果取不到,那么代表这个bean还没有实例化或还没有初始化完成。接着是从二级缓存和三级缓存中取,优先是从二级缓存中取,如果在二级缓存或三级缓存中取到(区别在于是否已经进行过aop的生成校验-具体下一节完成),并且当前对象正在实例化的流程当中(CURRENT_IN_CREATION),那么将这个未完成的bean返回,解决循环依赖(如果有aop还需要判断aop,下一节aop中会重点提到)。如果在二级缓存和三级缓存中对没有找到对象,代表该对象还没有进行实例化过,那么通过反射对该类进行实例化,并把实例化后的未完成对象加入三级缓存,将beanName加入创建列表CURRENT_IN_CREATION。生成未完成对象后进行属性填充,也就是依赖注入。注入完成后再进行aop代理的判断,看看二级缓存中是否有代理对象了,如果有就返回代理对象。之后再进行初始化步骤,完成后得到的就是一个完整可用的bean了,存入一级缓存,并删除二级缓存和三级缓存中的未完成bean,一级将该beanName移除CURRENT_IN_CREATION。

    public static Object buildBeans(String beanName, Class<?> aClass){
        if (aClass.isInterface()){
            throw new NotFountTargetBeanException("该依赖没有找到目标类:" + beanName);
        }
        //先从一级缓存中获取bean
        Object bean = BEANS.get(beanName);
        if (bean != null){
            return bean;
        }
        //再从二三级缓存中获取bean  解决循环依赖
        if ( (bean = EARLY_BEANS.get(beanName)) == null && (bean = BASIC_OBJECTS.get(beanName)) == null){
            bean = ReflectionUtil.newInstance(aClass);
            //
            BASIC_OBJECTS.put(beanName, bean);
            //将bean的名称存在正在创建的列表中
            CURRENT_IN_CREATION.add(beanName);
        }else if (CURRENT_IN_CREATION.contains(beanName)){
            //判断二三级缓存中的对象是否已经是代理对象

            //判断是否可以生成代理对象

            //如果是代理生成代理 返回未完成的bean 解决循环依赖
            return bean;
        }
        //如果在二三级缓存中存在 却没有在加载流程中,则继续完成加载。

        //进行依赖注入
        DependencyInjection.injectionDependency(bean);
        //进行aop 判断是否需要aop 判断二级缓存中是否已经有代理对象

        //存入一级缓存,删除二三级缓存
        BEANS.put(beanName, bean);
        EARLY_BEANS.remove(beanName);
        BASIC_OBJECTS.remove(beanName);
        CURRENT_IN_CREATION.remove(beanName);
        return bean;
    }

以上介绍了对象实例化的流程,其中也涉及到了依赖注入这个关键的步骤,现在来看看依赖注入是如何执行的。DependencyInjection.injectionDependency(bean);

依赖注入和之前定义的流程相似,本次通过fieldTypeClass(类型)和beanName(名称)贯穿其中,也是遍历该对象中注入的每个依赖,如果是接口就找到实现类,并通过beanName找到对应的fieldTypeClass,最后调用BeanFactory.buildBeans(beanName, fieldTypeClass)对需要的类进行实例化,也就是返回上面的流程。

    /**
     * 对目标bean进行依赖注入
     *
     * @param bean 进行依赖注入的bean
     */
    public static void injectionDependency(Object bean){
        if (bean == null){
            return;
        }
        //遍历该类中每个注入的依赖
        for (Field field : bean.getClass().getDeclaredFields()){
            //判断参数上是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                Class<?> fieldTypeClass = field.getType();
                String beanName = fieldTypeClass.getName();
                beanName  = ReflectionUtil.getComponentValue(fieldTypeClass, Component.class, beanName);
                //判断是否是接口 如果是接口那存着的就是接口信息,需要顺延找到实现类
                if (fieldTypeClass.isInterface()){
                    Set<Class<?>> subClass = ReflectionUtil.getSubClass(ApplicationContext.getInstance().packageName, fieldTypeClass);
                    if (subClass.size() == 0){
                        throw new InterfaceNotExistsImplementException("接口的实现类不存在");
                    }else if (subClass.size() == 1){
                        Class<?> aClass = subClass.iterator().next();
                        fieldTypeClass = aClass;
                        //实现类的名字肯定在ioc容器中,目前只考虑单个实现累的情况,如果是多个需要用@Qulifer
                        beanName  = ReflectionUtil.getComponentValue(aClass, Component.class, aClass.getName());
                    }else{
                        //有两个实现类以上,通过qualifier进行判断指定目标类
                        Qualifier qualifier = field.getDeclaredAnnotation(Qualifier.class);
                        if (qualifier == null || StringUtils.isBlank(qualifier.value())){
                            throw new NotFountTargetBeanException("该依赖没有找到目标类:" + field.toString());
                        }

                        for (Class<?> aClass : subClass){
                            beanName  = ReflectionUtil.getComponentValue(aClass, Component.class, aClass.getName());
                            if (beanName.equals(qualifier.value())){
                                fieldTypeClass = aClass;
                                beanName = qualifier.value();
                                break;
                            }
                        }
                    }
                }
                //根据类型的类名获取
                Object targetBean = BeanFactory.buildBeans(beanName, fieldTypeClass);
                if (targetBean == null){
                    throw new NotFountTargetBeanException("该依赖没有找到目标类:" + field.toString());
                }
                ReflectionUtil.setReflectionField(bean, field, targetBean);
            }
        }
    }

通过以上对IOC容器的拓展,让一个bean从最初的类到生成一个完整的bean的各个阶段都有进行记录,解决循环依赖-aop等阶段的问题。

上述的代码写的可能还是比较绕,我特意去他人的博客找了(抄)了一张流程图来…可是说是非常清晰明了了。
在这里插入图片描述

测试

不测了…改变了一下实现方式,具体的调用没有什么区别。下一节实现aop并整合到实例化这个流程中。

下一节:一起来写个SpringBoot[7] — — 实现AOP


版权声明:本文为qq_41762594原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。