ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

J2Cache源码阅读(一)---从初始化开始

2021-05-28 17:59:38  阅读:251  来源: 互联网

标签:初始化 缓存 region --- 源码 J2CacheBuilder config J2Cache


J2Cache二级缓存框架源码阅读

因为最近在看Redis的设计与实现,想写一个用java实现的缓存框架,于是就找了一个开源的J2Cache框架源码来学习,这里是记录的第一篇文章。


文章索引


一.J2Cache缓存结构

因为其实我也没怎么看文档就开始看源码了,都是从源码中理解的,首先看一下J2Cache的目录结构,如图所示。
J2Cache目录结构
所有的缓存都是通过CacheProviderHolder来管理的,它是一个缓存管理器,我们知道,J2Cache是一个二级缓存框架,所以holder下会通过l1_providerl2_provider来管理Level1Cache一级缓存和Level2Cache缓存,在一级缓存和二级缓存之下会通过区分不用的region区域来划分,不同的region下保存不用的key
用图来说明就这么理解。
在这里插入图片描述

不同层级的缓存region和key名肯定是一样的。

二.J2Cache缓存对象继承关系

首先定义了一个Cache缓存接口,来看一下继承图一目了然。
在这里插入图片描述 底下实现了一级缓存二级缓存

  • 一级缓存实现了如ehcache的缓存封装,实现了缓存操作以及对缓存数据失效的侦听,实现类为EhCache等等。
  • 二级缓存实现了如Redis的缓存封装,redis是基于Jedis接口封装的,实现类为RedisHashCache等等。

不过在作为返回值是使用了CacheObject类,后面再说。

三.J2Cache初始化

1. J2Cache

J2Cache,它是缓存的入口,在测试用例中,也是使用它来调用getChannel来获取管道,调用缓存相关的方法,测试用例如下所示。

public static void main(String[] args) {
    CacheChannel cache = J2Cache.getChannel();
    
    //缓存操作
    cache.set("default", "1", "Hello J2Cache");
    System.out.println(cache.get("default", "1"));
    cache.evict("default", "1");
    System.out.println(cache.get("default", "1"));
    
    cache.close();
}

所以我们先来看J2Cache的代码,在我的理解里,J2Cache它是一个面向使用者的类。
1.首先介绍它的成员变量

  • CONFIG_FILE : 不用多说,它只不过指定了配置文件的名称,来读取指定的配置文件来使用。
  • builder : 这个类才是真正的大哥,所有的J2Cache的方法都是由它来实现的,所以J2CacheBuilder是一个面向开发者的一个类。

2.然后再来看它的静态代码块

2.1 第一步来读取配置文件并实例化config对象,里面就是一顿IO操作,不解释。
2.2 第二步使用config对象来实例化builder,这里要注意一下,后面会详细说这个类。

这里调用了J2CacheBuilder的init静态方法,其中的操作就是赋值config对象,并返回builder实例化对象,还没有进行初始化builder,所以在它的init方法注释上说,这个操作很重,请勿重复执行是不是很正确的,只有在调用了它的getChannel方法,才会初始化builder。

介绍完成员变量和静态代码块,其他方法都是通过调用builder来实现操作的,所以我们接下来就来看一下J2CacheBuilder这个类吧。

public class J2Cache {

	private final static String CONFIG_FILE = "/j2cache.properties";

	private final static J2CacheBuilder builder;

	static {
		try {
            J2CacheConfig config = J2CacheConfig.initFromConfig(CONFIG_FILE);
            builder = J2CacheBuilder.init(config);
		} catch (IOException e) {
			throw new CacheException("Failed to load j2cache configuration " + CONFIG_FILE, e);
		}
	}

	/**
	 * 返回缓存操作接口
	 * @return CacheChannel
	 */
	public static CacheChannel getChannel(){
		return builder.getChannel();
	}

    /**
     * 关闭 J2Cache
     */
	public static void close() {
	    builder.close();
    }
}

1. J2CacheBuilder

J2CacheBuilder,它才是真正进行初始化操作,获取管道的工厂类,注解上说是使用自定义配置构建 J2Cache,那我们来看一下他是如何进行初始化的吧。

介绍一下它的成员变量,解释都写在注解上了,后面会一步步初始化赋值的。

private final static Logger log = LoggerFactory.getLogger(J2CacheBuilder.class); // 日志对象

    private CacheChannel channel; // 缓存操作的管道
    private CacheProviderHolder holder; // 缓存管理
    private ClusterPolicy policy; //不同的广播策略
    private AtomicBoolean opened = new AtomicBoolean(false); // 管道是否打开
    private J2CacheConfig config; // 传入的配置文件对象

首先从上文说道,在静态代码块中,调用了J2CacheBuilder的init静态方法,这个方法是这个样子的,它返回了一个builder实例化对象。

public final static J2CacheBuilder init(J2CacheConfig config) {
        return new J2CacheBuilder(config);
    }

构造方法也只是进行了赋值,并没有进行初始化。

private J2CacheBuilder(J2CacheConfig config) {
        this.config = config;
    }

所以,我们再来看getChannel方法,这个是用来获取管道的方法,以后都是通过管道来进行缓存操作的。

我们可以看到,在双重校验之后(保证线程安全),会调用initFromConfig方法进行初始化,该私有方法主要是对holder缓存管理对象policy不同的广播策略(用于集群)进行赋值。

  • 初始化holder对象时,会传入config配置文件,和CacheExpiredListener监听接口, 在holder的init方法中,listener进行直接赋值,config用于读取一级缓存的名称,比如配置文件中默认为“caffeine”,二级缓存的名称,比如配置文件中默认为“redis”,以此来通过不同的策略来实例化不同的CacheProvider,即l1_provider和l2_provider
  • 初始化policy对象同理,通过工厂模式匹配不同的配置参数来实例化对应的ClusterPolicy。【这块我没有细看】

这样我们的initFromConfig方法就执行完毕,初始化好了这两个对象,然后会实例化单例的CacheChannel对象,实现三个未实现的抽象方法,然后将opened设为true,即为管道打开。

	/**
     * 返回缓存操作接口
     *
     * @return CacheChannel
     */
    public CacheChannel getChannel() {
        /**
         * 做了双层校验,为了单例生成CacheChannel缓存操作接口
         * tip : CacheChannel 虽然是一个管道,但是包括了region等方法,总体来说就是一个缓存操作的接口
         */
        if (this.channel == null || !this.opened.get()) {
            synchronized (J2CacheBuilder.class) {
                if (this.channel == null || !this.opened.get()) {
                    this.initFromConfig(config); // 这里进行加载配置初始化
                    /* 初始化缓存接口 */
                    this.channel = new CacheChannel(config, holder) {
                        @Override
                        public void sendClearCmd(String region) {
                            policy.sendClearCmd(region);
                        }

                        @Override
                        public void sendEvictCmd(String region, String... keys) {
                            policy.sendEvictCmd(region, keys);
                        }

                        @Override
                        public void close() {
                            super.close();
                            policy.disconnect();
                            holder.shutdown();
                            opened.set(false);
                        }
                    };
                    this.opened.set(true);
                }
            }
        }
        return this.channel;
    }


	/**
     * 加载配置
     *
     * @return
     * @throws IOException
     */
    private void initFromConfig(J2CacheConfig config) {
        // 配置中默认为“json”
        SerializationUtils.init(config.getSerialization(), config.getSubProperties(config.getSerialization()));
        //初始化两级的缓存管理 (配置, 监听)
        this.holder = CacheProviderHolder.init(config, (region, key) -> {
            //当一级缓存中的对象失效时,自动清除二级缓存中的数据
            Level2Cache level2 = this.holder.getLevel2Cache(region);
            level2.evict(key);
            if (!level2.supportTTL()) {
                //再一次清除一级缓存是为了避免缓存失效时再次从 L2 获取到值
                this.holder.getLevel1Cache(region).evict(key);
            }
            log.debug("Level 1 cache object expired, evict level 2 cache object [{},{}]", region, key);
            if (policy != null)
                policy.sendEvictCmd(region, key);
        });

        policy = ClusterPolicyFactory.init(holder, config.getBroadcast(), config.getBroadcastProperties());
        log.info("Using cluster policy : {}", policy.getClass().getName());
    }

四.总结

通过传入的config对象实例化了序列化器、holder缓存管理器、policy广播策略、channel管道,之后就是通过调用channel实现具体的操作缓存方法。

标签:初始化,缓存,region,---,源码,J2CacheBuilder,config,J2Cache
来源: https://blog.csdn.net/weixin_44414492/article/details/117367766

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有