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

java持续实践

暂无认证

  • 1浏览

    0关注

    746博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Spring源码之 容器的载体和容器的加载

java持续实践 发布时间:2021-03-07 16:00:13 ,浏览量:1

文章目录
      • 创建单例容器
      • 实现容器
        • 实现容器的加载
      • 测试用例

创建单例容器

使用如下的枚举创建单例容器 .

@Slf4j
// 创建私有的无参构造函数
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {

    /**
     *  获取单例的bean容器实例
     * @return
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;
        ContainerHolder() {
            instance = new BeanContainer();
        }
    }

}
实现容器

容器的组成部分:

  1. 保存Class 对象以及实例的载体 .
  2. 容器的加载
  3. 对外提供容器的操作方式, 便于 客户端操作载体中的对象.

使用ConcurrentHashMap来保存目标类型的对象和实例. ConcurrentHashMap并发性强, JDK8摒弃了之前的分段锁, 采用CAS, 红黑树, 提升细节.

实现容器的加载

实现思路.

  1. 配置的管理与获取
  2. 获取指定范围内的Class对象
  3. 依据配置提取Class对象, 连同实例一并存入容器中.

代码如下

@Slf4j
// 创建私有的无参构造函数
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的map
     */
    private final Map beanMap = new ConcurrentHashMap();

    /**
     * 初始化, 容器未被加载过
     */
    private boolean loaded = false;

    /**
     * 该方法用于判断容器是否被加载过
     *
     * @return
     */
    public boolean isLoaded() {
        return loaded;
    }

    /**
     * 1. 配置的管理与获取
     * 加载bean的注册列表.
     * 把用Component, Controller, Service, Repository 注解标记的类加载到内存中去
     */
    private static final List BEAN_ANNOTATION =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

    /**
     * 返回容器中bean的数量
     * @return
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 获取单例的bean容器实例
     *
     * @return
     */
    public static BeanContainer getInstance() {
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder {
        HOLDER;
        private BeanContainer instance;

        ContainerHolder() {
            instance = new BeanContainer();
        }
    }

    /**
     * 扫描加载所有的bean
     * 3. 依据配置提取Class对象,  连同实例一并存入容器中.
     *
     * 加上synchronized  防止两个线程去加载容器
     */
    public synchronized void loadBeans(String packageName) {
        if (isLoaded()) {
            log.warn("容器已经被加载过了");
            return;
        }
        // 通过传入的包名, 把类加载到集合中
        Set classSet = ClassUtil.extractPackageClass(packageName);
        if (ValidationUtil.isEmpty(classSet)) {
            log.error("该包获取不到任何类: ", packageName);
            return;
        }

        // 遍历所有加载到的类实例
        for (Class clazz : classSet) {

            // 遍历所有的注解
            for (Class annotation : BEAN_ANNOTATION) {
                // 判断该类是否被所需要的注解修饰
                if (clazz.isAnnotationPresent(annotation)) {
                    // 如果是所需要的类
                    // 那么将目标类本身作为键, 目标类的实例作为值, 放入beanMap中
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }

}

其核心为loadBeans 方法加载bean到容器中. 有如下的注意点和步骤

  1. 方法用synchronized修饰, 避免多线程的情况下, 两个线程同时去加载bean
  2. loaded布尔值来标记是否已经加载过bean, 只需加载一次bean到容器中
  3. 通过ClassUtil.extractPackageClass(packageName)工具类根据传入的包路径,把类加入到集合中
  4. 遍历所有的类的集合, 根据 isAnnotationPresent 来判断是否为所需要的Component, Controller, Service, Repository注解 修饰的类
  5. 如果是被指定注解修饰的类, 那么将其放入beanMap中.
  6. 调用ClassUtil.newInstance(clazz, true) 方法去创建类的实例

newInstance 工具类方法如下

 /**
     * @param clazz      Class 类对象
     * @param accessible 是否支持创建出私有class对象实例
     * @param         class的类型
     * @return 实例化的类
     */
    public static  T newInstance(Class clazz, boolean accessible) {
        try {
            // 获取无参构造方法
            Constructor constructor = clazz.getDeclaredConstructor();
            // 设置是否暴力访问
            constructor.setAccessible(accessible);
            // 创建出实例, 强制转换为T对象
            return (T) constructor.newInstance();
        } catch (Exception e) {
            log.error("创建实例错误 ", e);
            throw new RuntimeException();
        }
    }
测试用例

编写BeanContainerTest 测试类 @BeforeAll 注解用于所有的测试用例执行之前 , 执行仅且一次初始化. 需要先实例化容器实例.

public class BeanContainerTest {

    private static BeanContainer beanContainer;

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

    @DisplayName("加载目标类及实例到BeanContainer")
    @Test
    public void loadBeansTest() {
    // 验证是否加载过是否为false
        Assertions.assertEquals(false, beanContainer.isLoaded());
        beanContainer.loadBeans("com.springstudy");
        int size = beanContainer.size();
        // 验证bean的数量是否为7, 目前项目中, com.springstudy 包下有7个类被指定注解修饰.
        Assertions.assertEquals(7, size);
        // 验证是否加载过是否为true
        Assertions.assertEquals(true, beanContainer.isLoaded());
    }
}

测试结果如下, 测试通过. 在这里插入图片描述

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

微信扫码登录

0.0405s