【开源项目】SpringBoot整合J2Cache入门及源码解析

快速入门

引入依赖

<dependency>
    <groupId>net.oschina.j2cachegroupId>
    <artifactId>j2cache-coreartifactId>
    <version>2.8.4-releaseversion>
dependency>
<dependency>
    <groupId>net.oschina.j2cachegroupId>
    <artifactId>j2cache-spring-boot2-starterartifactId>
    <version>2.8.0-releaseversion>
dependency>
<dependency>
    <groupId>net.sf.ehcachegroupId>
    <artifactId>ehcacheartifactId>
dependency>

配置application.yml等文件

application.yml

j2cache:
  config-location: j2cache.properties

ehcache.xml


<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="https://www.ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="D:\ehcache" />

    
    
    
    
    
    
    
    
    <defaultCache
            eternal="false"
            diskPersistent="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            timeToIdleSeconds="60"
            timeToLiveSeconds="60"
            memoryStoreEvictionPolicy="LRU" />

    <cache
            name="smscode"
            eternal="false"
            diskPersistent="false"
            maxElementsInMemory="1000"
            overflowToDisk="false"
            timeToIdleSeconds="10"
            timeToLiveSeconds="10"
            memoryStoreEvictionPolicy="LRU" />

ehcache>

j2cache.properties

# 1级缓存
j2cache.L1.provider_class = ehcache
ehcache.configXml = ehcache.xml

# 2级缓存
j2cache.L2.provider_class =net.oschina.j2cache.cache.support.redis.SpringRedisProvider
j2cache.L2.config_section = redis
redis.hosts = localhost:6379

# 1级缓存中的数据如何到达2级缓存
j2cache.broadcast =net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy

使用CacheChannel

@RestController
@RequestMapping("/sms")
public class SmsCodeController {

    @Autowired
    private CacheChannel cacheChannel;

    @GetMapping("/set")
    public String getCode(String phone) {
        String code = "123456";
        cacheChannel.set("sms", phone, code);
        return code;
    }

    @GetMapping("/get")
    public String checkCode(String phone) {
        String code2 = cacheChannel.get("sms", phone).asString();
        return code2;
    }
}

源码解析

初始化

j2cache-spring-boot2-starterspring.factories中加载配置。

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
net.oschina.j2cache.autoconfigure.J2CacheAutoConfiguration,\
net.oschina.j2cache.autoconfigure.J2CacheSpringCacheAutoConfiguration,\
net.oschina.j2cache.autoconfigure.J2CacheSpringRedisAutoConfiguration

J2CacheAutoConfiguration加载j2cache.config-location指定路径下的文件。往容器中存放CacheChannelJ2CacheConfig两个实体类。

@ConditionalOnClass({J2Cache.class})
@EnableConfigurationProperties({J2CacheConfig.class})
@Configuration
@PropertySource(
    value = {"${j2cache.config-location}"},
    encoding = "UTF-8",
    ignoreResourceNotFound = true
)
public class J2CacheAutoConfiguration {
    @Autowired
    private StandardEnvironment standardEnvironment;

    public J2CacheAutoConfiguration() {
    }

    @Bean
    public net.oschina.j2cache.J2CacheConfig j2CacheConfig() throws IOException {
        net.oschina.j2cache.J2CacheConfig cacheConfig = SpringJ2CacheConfigUtil.initFromConfig(this.standardEnvironment);
        return cacheConfig;
    }

    @Bean
    @DependsOn({"springUtil", "j2CacheConfig"})
    public CacheChannel cacheChannel(net.oschina.j2cache.J2CacheConfig j2CacheConfig) throws IOException {
        J2CacheBuilder builder = J2CacheBuilder.init(j2CacheConfig);
        return builder.getChannel();
    }

    @Bean
    public SpringUtil springUtil() {
        return new SpringUtil();
    }
}

CacheProviderHolder#init,根据配置信息加载一级缓存和二级缓存组件。

    public static CacheProviderHolder init(J2CacheConfig config, CacheExpiredListener listener) {

        CacheProviderHolder holder = new CacheProviderHolder();

        holder.listener = listener;
        holder.l1_provider = loadProviderInstance(config.getL1CacheName());
        if (!holder.l1_provider.isLevel(CacheObject.LEVEL_1))
            throw new CacheException(holder.l1_provider.getClass().getName() + " is not level_1 cache provider");
        holder.l1_provider.start(config.getL1CacheProperties());
        log.info("Using L1 CacheProvider : {}", holder.l1_provider.getClass().getName());

        holder.l2_provider = loadProviderInstance(config.getL2CacheName());
        if (!holder.l2_provider.isLevel(CacheObject.LEVEL_2))
            throw new CacheException(holder.l2_provider.getClass().getName() + " is not level_2 cache provider");
        holder.l2_provider.start(config.getL2CacheProperties());
        log.info("Using L2 CacheProvider : {}", holder.l2_provider.getClass().getName());

        return holder;
    }

CacheProviderHolder#loadProviderInstance,如果是指定的服务,加载对应的组件。如果找不到,根据类名的绝对路径加载。

    private static CacheProvider loadProviderInstance(String cacheIdent) {
        switch (cacheIdent.toLowerCase()) {
            case "ehcache":
                return new EhCacheProvider();
            case "ehcache3":
                return new EhCacheProvider3();
            case "caffeine":
                return new CaffeineProvider();
            case "redis":
                return new RedisCacheProvider();
            case "readonly-redis":
                return new ReadonlyRedisCacheProvider();
            case "memcached":
                return new XmemcachedCacheProvider();
            case "lettuce":
                return new LettuceCacheProvider();
            case "none":
                return new NullCacheProvider();
        }
        try {
            return (CacheProvider) Class.forName(cacheIdent).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new CacheException("Failed to initialize cache providers", e);
        }
    }

存放数据

CacheChannel#set()。更新一级缓存,并且更新二级缓存。判断是否需要存在空值。

	public void set(String region, String key, Object value, boolean cacheNullObject) {

		this.assertNotClose();

		if (!cacheNullObject && value == null)
			return ;

		try {
			Level1Cache level1 = holder.getLevel1Cache(region);
			level1.put(key, (value==null && cacheNullObject)?newNullObject():value);
			Level2Cache level2 = holder.getLevel2Cache(region);
			if(config.isSyncTtlToRedis())
				level2.put(key, (value==null && cacheNullObject)?newNullObject():value, level1.ttl());
			else
				level2.put(key, (value==null && cacheNullObject)?newNullObject():value);
		} finally {
			this.sendEvictCmd(region, key);//清除原有的一级缓存的内容
		}
    }

获取数据

CacheChannel#get()。一级缓存中存在,则从一级缓存中获取,否则从二级缓存中获取。

	public CacheObject get(String region, String key, boolean...cacheNullObject)  {

		this.assertNotClose();

		CacheObject obj = new CacheObject(region, key, CacheObject.LEVEL_1);
		obj.setValue(holder.getLevel1Cache(region).get(key));
		if(obj.rawValue() != null)
			return obj;

		String lock_key = key + '%' + region;
		synchronized (_g_keyLocks.computeIfAbsent(lock_key, v -> new Object())) {
			obj.setValue(holder.getLevel1Cache(region).get(key));
			if(obj.rawValue() != null)
				return obj;

			try {
				obj.setLevel(CacheObject.LEVEL_2);
				obj.setValue(holder.getLevel2Cache(region).get(key));
				if (obj.rawValue() != null) {
					holder.getLevel1Cache(region).put(key, obj.rawValue());
				}else {
					boolean cacheNull = (cacheNullObject.length > 0) ? cacheNullObject[0] : defaultCacheNullObject;
					if (cacheNull)
						set(region, key, newNullObject(), true);
				}
			} finally {
				_g_keyLocks.remove(lock_key);
			}
		}

		return obj;
	}

你可能感兴趣的:(源码解析,开源项目介绍,spring,boot,开源,java)