您当前的位置: 首页 >  容器

java持续实践

暂无认证

  • 1浏览

    0关注

    746博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring 源码解析之 实现容器的依赖注入 DI

java持续实践 发布时间:2021-03-08 08:20:43 ,浏览量:1

文章目录
      • 实现容器的依赖注入
        • 为什么需要依赖注入
      • IOC的实现流程
      • 测试用例

实现容器的依赖注入 为什么需要依赖注入

如果一个类中, 依赖了另外一个类, 如下MainPageController 依赖了HeadLineShopCategoryCombineService . 那么就要保证HeadLineShopCategoryCombineService 这个类是初始化好的, 否则调用headLineShopCategoryCombineService.getMainPageInfo()方法的时候, 就会报空指针.

@Controller
@Getter
public class MainPageController {
    private HeadLineShopCategoryCombineService headLineShopCategoryCombineService;

    public Result getMainPageInfo(HttpServletRequest req, HttpServletResponse resp){
        return headLineShopCategoryCombineService.getMainPageInfo();
    }
}

在上一篇文章 https://javaweixin6.blog.csdn.net/article/details/114493380中, 写的测试用例如下, 根据类, 获取其实例. 根据MainPageController类, 获取MainPageController的实例

 @DisplayName("根据类获取其实例")
    @Order(2)
    @Test
    public void getBeanTest() {
        MainPageController controller = (MainPageController) beanContainer.getBean(MainPageController.class);
        Assertions.assertEquals(true, controller instanceof MainPageController);

        HelloServlet helloServlet = (HelloServlet) beanContainer.getBean(HelloServlet.class);
        Assertions.assertEquals(null, helloServlet);
    }

运行后, 可以看到headLineShopCategoryCombineService 是null , 那么就需要依赖注入功能, 把所依赖的对象, 进行实例初始化. 需要通过标记, 来告诉容器需要将哪些实例注入到Bean的成员变量中.

IOC的实现流程

实现思路:

  1. 定义相关的注解标签
  2. 实现 创建被注解标记的成员变量实例, 并将其注入到成员变量中
  3. 依赖注入的使用 (暂时只做根据成员变量的注入 . )

整体的IOC流程如下图 实现依赖注入的类如下

package org.simpleframework.inject;

import lombok.extern.slf4j.Slf4j;
import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;
import org.simpleframework.util.ValidationUtil;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * 类名称:DependencyInjector
 * 类描述: 实现依赖注入
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2021/3/7 19:22
 * Version 1.0
 */
@Slf4j
public class DependencyInjector {
    private BeanContainer beanContainer;

    public DependencyInjector() {
        // 从构造方法中, 实例化Bean容器
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 实现依赖注入
     */
    public void doIoc() {
        // 校验容器是否为空
        if (ValidationUtil.isEmpty(beanContainer.getClasses())) {
            log.warn(" 容器中实例为空! ");
            return;
        }
        // 1. 遍历bean容器中所有的Class对象
        for (Class clazz : beanContainer.getClasses()) {
            // 2. 遍历Class 对象的所有的成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (ValidationUtil.isEmpty(fields)) {
                // 判断成员变量是否为空, 如果是空, 则遍历下一个对象
                continue;
            }
            for (Field field : fields) {
                // 3. 找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 获取注解的实例
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    // 获取Autowired注解的属性值
                    String autowiredValue = autowired.value();
                    // 4. 获取成员变量的类型
                    Class fieldClass = field.getType();
                    // 5. 获取这些成员变量的类型, 在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (fieldValue == null) {
                        // 如果从容器中获取该实例失败, 那么抛出异常
                        throw new RuntimeException(" 无法从容器中获取实例, 目标类型为:  " + fieldClass.getName() + " autowiredValue 值为: " + autowiredValue);
                    } else {
                        // 6. 通过反射将对应的成员变量实例注入到成员变量所在类的实例中
                        Object targetBean = beanContainer.getBean(clazz);
                        ClassUtil.setField(field, targetBean, fieldValue, true);

                    }

                }
            }

        }


    }

    /**
     * 根据Class 在BeanContainer里获取其实例或实现类
     * 根据接口获取其实现类
     *
     * @param fieldClass
     * @param autowiredValue
     * @return
     */
    private Object getFieldInstance(Class fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null) {
            // 不为空, 代表成员变量不是接口, 而是实现类
            return fieldValue;
        } else {
            //  获取接口的实现类
            Class implementedClass = getImplementedClass(fieldClass, autowiredValue);
            if (implementedClass != null) {
                // 从bean容器中获取其成员变量的实例并返回
                return beanContainer.getBean(implementedClass);
            } else {
                // 获取的接口实现类为空, 那么直接返回null
                return null;
            }
        }
    }

    /**
     * 获取接口的实现类
     *
     * @param fieldClass
     * @param autowiredValue
     * @return
     */
    private Class getImplementedClass(Class fieldClass, String autowiredValue) {
        // 获取接口的所有的实现类的集合
        Set classSet = beanContainer.getClassesBySuper(fieldClass);
        if (!ValidationUtil.isEmpty(classSet)) {
            if (ValidationUtil.isEmpty(autowiredValue)) {
                // autowiredValue为默认值, 即用户未指定注入哪个对象
                if (classSet.size() == 1) {
                    // 如果其实现类只有一个, 那么就直接返回就行
                    return classSet.iterator().next();
                } else {
                    // 如果实现类多于一个 , 那么抛出异常
                    throw new RuntimeException("该接口有多个实现 " + fieldClass.getName() + " 请指定需要哪个实现类");
                }
            } else {
                // autowiredValue值不为空, 那么遍历其所有的实现类, 找到和指定类名相同的进行返回
                for (Class clazz : classSet) {
                    if (autowiredValue.equals(clazz.getSimpleName())) {
                        return clazz;
                    }
                }
            }
        }
        // 如果实现类为空, 那么直接返回null
        return null;
    }
}

ClassUtil 的工具类, 实现设置类的属性值

   /**
     * 设置类的属性值
     *
     * @param field      成员变量
     * @param target     类实例
     * @param value      成员变量的值
     * @param accessible 是否允许设置私有属性
     */
    public static void setField(Field field, Object target, Object value, boolean accessible) {
        field.setAccessible(accessible);
        try {
            field.set(target, value);
        } catch (IllegalAccessException e) {
            log.error("设置属性值异常 ", e);
            throw new RuntimeException(e);
        }
    }
测试用例

测试用例如下,

package org.simpleframework.inject;

import com.springstudy.controller.frontend.MainPageController;
import com.springstudy.service.combine.impl.HeadLineShopCategoryCombineServiceImpl;
import com.springstudy.service.combine.impl.HeadLineShopCategoryCombineServiceImpl2;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.simpleframework.core.BeanContainer;

/**
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2021/3/7 22:22
 * Version 1.0
 */
public class DependencyInjectorTest {

    @Test
    public void doIocTest() {
        // 获取容器实例
        BeanContainer beanContainer = BeanContainer.getInstance();
        // 指定包名, 加载类到容器中
        beanContainer.loadBeans("com.springstudy");
        // 判断容器是否加载完毕
        Assertions.assertEquals(true, beanContainer.isLoaded());
        // 从容器中获取指定的bean
        MainPageController mainPageController = (MainPageController) beanContainer.getBean(MainPageController.class);
        // 判断获取的对象是否为MainPageController
        Assertions.assertEquals(true, mainPageController instanceof MainPageController);

        // 在没有进行调用IOC之前, 即没有依赖注入之前, Controller中的Service对象 为null
        Assertions.assertNull(mainPageController.getHeadLineShopCategoryCombineService());

        // 进行依赖注入
        new DependencyInjector().doIoc();
        // 在没有进行调用IOC之后, 即依赖注入之后, Controller中的Service对象 不为null
        Assertions.assertNotNull(mainPageController.getHeadLineShopCategoryCombineService());

        // 验证注入的是HeadLineShopCategoryCombineServiceImpl
        Assertions.assertTrue(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
        // 验证注入的不是 HeadLineShopCategoryCombineServiceImpl2
        Assertions.assertFalse(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);
    }
}

在项目中使用自定义的@Autowired注解进行注入 . 并且也有测试同一个接口, 两个实现类. 如下 一个Service接口, 两个实现类. 在这里插入图片描述 在Controller中注入时, 需指定其注入的类名称. 在这里插入图片描述 在测试用例中, 验证的代码如下

  // 验证注入的是HeadLineShopCategoryCombineServiceImpl
        Assertions.assertTrue(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl);
        // 验证注入的不是 HeadLineShopCategoryCombineServiceImpl2
        Assertions.assertFalse(mainPageController.getHeadLineShopCategoryCombineService() instanceof HeadLineShopCategoryCombineServiceImpl2);

测试结果如下 在这里插入图片描述

关注
打赏
1658054974
查看更多评论
立即登录/注册

微信扫码登录

0.0412s