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

java持续实践

暂无认证

  • 2浏览

    0关注

    746博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring源码之 实现容器的增删改查

java持续实践 发布时间:2021-03-07 18:50:21 ,浏览量:2

文章目录
      • 容器的增删改查
      • 测试用例

容器的增删改查
  1. 容器的增加删除操作, 往容器中添加和删除bean
  2. 根据Class获取对应实例: 根据Map的key获取值
  3. 获取所有的Class和实例
  4. 通过注解获取被注解标注的Class
  5. 通过超类获取对应子类的Class (通过传入接口获取接口对应实现类的Class对象, 例如传入Service的接口, 获取Service的实现类.)
  6. 获取容器载体存储的Class的数量

上面的需求实现的代码如下

  /**
     * 添加一个class 对象和bean实例到容器中
     *
     * @param clazz Class 对象
     * @param bean  bean实例
     * @return 返回添加的值
     */
    public Object addBean(Class clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除一个IOC容器管理对象
     *
     * @param clazz
     * @return 被移除的对象, 如果不存在则返回null
     */
    public Object removeBean(Class clazz) {
        return beanMap.remove(clazz);
    }

    /**
     * 根据Class对象 获取bean 的实例
     *
     * @param clazz
     * @return
     */
    public Object getBean(Class clazz) {
        return beanMap.get(clazz);
    }

    /**
     * 获取容器管理的所有的Class对象集合 (获取所有的key)
     *
     * @return Class 集合
     */
    public Set getClasses() {
        return beanMap.keySet();
    }

    /**
     * 获取所有Bean 集合 (获取所有的value)
     *
     * @return
     */
    public Set getBeans() {
        // 获取所有的bean集合, 并通过构造函数, 传入HashSet中
        return new HashSet(beanMap.values());
    }

    /**
     * 通过指定的注解, 查找被该注解标记的Class集合 .
     *
     * @param annotation
     * @return
     */
    public Set getClassesByAnnotation(Class annotation) {
        // 1.获取beanMap的所有Class对象
        Set keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("容器为空!");
            return null;
        }

        // 2.通过注解筛选被注解标记的class对象, 并添加到ClassSet中
        Set classSet = new HashSet();
        for (Class clazz : keySet) {
            // 判断该类是否被指定的注解标记
            if (clazz.isAnnotationPresent(annotation)) {
                classSet.add(clazz);
            }
        }
        // 判断符合条件的集合是否存在,如果不存在, 则返回Null
        return classSet.size() > 0 ? classSet : null;
    }

    /**
     * 通过接口或父类获取实现类或子类的Class 集合, 不包含其本身
     *
     * @param interfaceOrClass
     * @return
     */
    public Set getClassesBySuper(Class interfaceOrClass) {
        // 1.获取beanMap的所有Class对象
        Set keySet = getClasses();
        if (ValidationUtil.isEmpty(keySet)) {
            log.warn("容器为空!");
            return null;
        }

        // 2.判断KeySet里的元素是否为传入的接口或类的子类, 如果是, 就添加到ClassSet中
        Set classSet = new HashSet();
        for (Class clazz : keySet) {
            // 判断KeySet里的元素是否为传入的接口或类的子类
            /**
             * isAssignableFrom 方法
             * A.isAssignableFrom(B)
             * A 是否为B的爸爸或爷爷或祖孙...
             *       返回true, 代表是的, 返回false. 代表B不是继承或实现了A
             * 特殊情况:  A.isAssignableFrom(A) 返回的也是 true.
             *  因此此处加上了!clazz.equals(interfaceOrClass) 来排除自身接口或类的情况
             */
            if (interfaceOrClass.isAssignableFrom(clazz) && !clazz.equals(interfaceOrClass)) {
                classSet.add(clazz);
            }
        }
        // 判断符合条件的集合是否存在,如果不存在, 则返回Null
        return classSet.size() > 0 ? classSet : null;
    }

其中 通过接口或父类获取实现类或子类的Class 集合这个方法, 有用到Class类的isAssignableFrom方法, 该方法用于判断是否是某个类的超类, 特殊情况: A.isAssignableFrom(A) 返回的也是 true.

测试用例

完整的测试用例如下 . 其中有用到TestMethodOrder注解, 标注在测试类上, 用于规定测试用例执行的顺序的. 在每个测试用例的方法中标注上@Order(), 数字越小的越先执行. 由于该测试类中的方法要先加载容器, 因此需要把加载容器的方法Order顺序传入最小.

import com.springstudy.HelloServlet;
import com.springstudy.controller.frontend.MainPageController;
import com.springstudy.service.solo.HeadLineService;
import com.springstudy.service.solo.impl.HeadLineServiceImpl;
import org.junit.jupiter.api.*;
import org.simpleframework.core.annotation.Controller;
// 单元测试的方法, 按顺序执行
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BeanContainerTest {

    private static BeanContainer beanContainer;

    /**
     * BeforeAll注解用于所有的测试用例执行之前 , 执行仅且一次初始化.
     */
    @BeforeAll
    static void init() {
        // 初始化 BeanContainer 实例
        beanContainer = BeanContainer.getInstance();
    }

    @DisplayName("加载目标类及实例到BeanContainer")
    // 第一个执行
    @Order(1)
    @Test
    public void loadBeansTest() {
        Assertions.assertEquals(false, beanContainer.isLoaded());
        beanContainer.loadBeans("com.springstudy");
        int size = beanContainer.size();
        Assertions.assertEquals(7, size);
        Assertions.assertEquals(true, beanContainer.isLoaded());
    }

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

    @DisplayName("根据注解获取对应的类的集合")
    @Order(3)
    @Test
    public void getClassesByAnnotationTest() {
        // 获取被自定义Controller注解标记的类对象
        int size = beanContainer.getClassesByAnnotation(Controller.class).size();
        Assertions.assertEquals(3, size);
    }

    @DisplayName("根据接口获取实现类")
    @Order(4)
    @Test
    public void getClassesBySuperTest() {
        // 通过传入HeadLineService接口, 获取其所有实现类, 断言判断是否有 HeadLineServiceImpl
        boolean isContains = beanContainer.getClassesBySuper(HeadLineService.class)
                .contains(HeadLineServiceImpl.class);

        Assertions.assertEquals(true, isContains);
    }

}

运行测试类, 结果如下

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

微信扫码登录

0.0419s