最近很多小伙伴都在问通过SpringCache缓存嵌套的可缓存操作和springcache缓存这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展Cache系列:spring-cach
最近很多小伙伴都在问通过SpringCache缓存嵌套的可缓存操作和spring cache缓存这两个问题,那么本篇文章就来给大家详细解答一下,同时本文还将给你拓展Cache系列:spring-cache简单三步快速应用ehcache3.x-jcache缓存(spring4.x)、Ehcache缓存初步实践实例(ehcache与spring整合方法)、EVCache缓存在 Spring Boot中的实战、JAD-CACHE缓存框架,srping集成EhCache及实现本地缓存等相关知识,下面开始了哦!
本文目录一览:- 通过SpringCache缓存嵌套的可缓存操作(spring cache缓存)
- Cache系列:spring-cache简单三步快速应用ehcache3.x-jcache缓存(spring4.x)
- Ehcache缓存初步实践实例(ehcache与spring整合方法)
- EVCache缓存在 Spring Boot中的实战
- JAD-CACHE缓存框架,srping集成EhCache及实现本地缓存
通过SpringCache缓存嵌套的可缓存操作(spring cache缓存)
我的任务是利用SpringCache作为我们的一项服务,以减少数据库查找的次数。在测试实现时,我注意到一些可缓存操作通过日志语句多次调用。调查显示,如果在可缓存的方法中调用了可缓存的操作,则嵌套操作根本不会被缓存。因此,嵌套操作的后续调用将导致进一步的查找。
下面列出了描述问题的简单单元测试:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = {SpringCacheTest.Config.class} )@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)public class SpringCacheTest { private final static String CACHE_NAME = "testCache"; private final static Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private final static AtomicInteger methodInvocations = new AtomicInteger(0); public interface ICacheableService { String methodA(int length); String methodB(String name); } @Resource private ICacheableService cache; @Test public void testNestedCaching() { String name = "test"; cache.methodB(name); assertThat(methodInvocations.get(), is(equalTo(2))); cache.methodA(name.length()); // should only be 2 as methodA for this length was already invoked before assertThat(methodInvocations.get(), is(equalTo(3))); } @Configuration public static class Config { @Bean public CacheManager getCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache(CACHE_NAME))); return cacheManager; } @Bean public ICacheableService getMockedEntityService() { return new ICacheableService() { private final Random random = new Random(); @Cacheable(value = CACHE_NAME, key = "#root.methodName.concat(''_'').concat(#p0)") public String methodA(int length) { methodInvocations.incrementAndGet(); LOG.debug("Invoking methodA"); char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); StringBuilder sb = new StringBuilder(); for (int i=0; i<length; i++) { sb.append(chars[random.nextInt(chars.length)]); } String result = sb.toString(); LOG.debug("Returning {} for length: {}", result, length); return result; } @Cacheable(value = CACHE_NAME, key = "#root.methodName.concat(''_'').concat(#p0)") public String methodB(String name) { methodInvocations.incrementAndGet(); LOG.debug("Invoking methodB"); String rand = methodA(name.length()); String result = name+"_"+rand; LOG.debug("Returning {} for name: {}", result, name); return result; } }; } }}
这两种方法的实际工作对于测试用例本身并不重要,因为仅应测试缓存。
我以某种方式理解了为什么不缓存嵌套操作的结果的原因,但是我想知道是否有可用的配置(我尚未弄清楚)来启用对嵌套可缓存操作的返回值的缓存。
我知道通过重构并提供嵌套操作的返回值作为外部操作的参数将起作用,但是因为这可能涉及更改一些操作(以及对其进行单元测试),配置或其他解决方法(如果在我们的具体情况下更可取)。
答案1
小编典典问题在于您是methodA
直接从中访问的methodB
,因此这将阻止通过处理缓存机制的Java代理。此外,您没有添加@EnableCaching
注释,因此测试中实际上根本没有缓存。
以下测试表明,如果您正确地检查了Spring创建的代理,则嵌套缓存模式将按预期工作:
import static org.junit.Assert.*;import java.util.Arrays;import java.util.Random;import java.util.concurrent.atomic.AtomicInteger;import javax.annotation.Resource;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.Cacheable;import org.springframework.cache.annotation.EnableCaching;import org.springframework.cache.concurrent.ConcurrentMapCache;import org.springframework.cache.support.SimpleCacheManager;import org.springframework.context.ApplicationContext;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.test.annotation.DirtiesContext;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(classes = { SpringCacheTest.Config.class })@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)public class SpringCacheTest { private final static String CACHE_NAME = "testCache"; private final static AtomicInteger methodInvocations = new AtomicInteger(0); public interface ICacheableService { String methodA(int length); String methodB(String name); } @Resource private ICacheableService cache; @Test public void testNestedCaching() { String name = "test"; cache.methodB(name); assertEquals(methodInvocations.get(), 2); cache.methodA(name.length()); // should only be 2 as methodA for this length was already invoked before assertEquals(methodInvocations.get(), 2); } @Configuration @EnableCaching public static class Config { @Bean public CacheManager getCacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache(CACHE_NAME))); return cacheManager; } @Bean public ICacheableService getMockedEntityService() { return new ICacheableService() { private final Random random = new Random(); @Autowired ApplicationContext context; @Override @Cacheable(value = CACHE_NAME, key = "#root.methodName.concat(''_'').concat(#p0)") public String methodA(int length) { methodInvocations.incrementAndGet(); System.out.println("Invoking methodA"); char[] chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".toCharArray(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { sb.append(chars[random.nextInt(chars.length)]); } String result = sb.toString(); System.out.println("Returning " + result + " for length: " + length); return result; } @Override @Cacheable(value = CACHE_NAME, key = "#root.methodName.concat(''_'').concat(#p0)") public String methodB(String name) { methodInvocations.incrementAndGet(); System.out.println("Invoking methodB"); ICacheableService cache = context.getBean(ICacheableService.class); String rand = cache.methodA(name.length()); String result = name + "_" + rand; System.out.println("Returning " + result + " for name: " + name); return result; } }; } }}
Cache系列:spring-cache简单三步快速应用ehcache3.x-jcache缓存(spring4.x)
前言:本项目基于spring4.x构建,使用ehcache3.5.2和JCache(jsr107规范)
一、依赖
除了ehcache和cache-api外,注意引用spring-context-support
-
-
<dependency>
-
<groupId>org.springframework</groupId>
-
<artifactId>spring-context-support</artifactId>
-
<version>4.3.16.RELEASE</version>
-
</dependency>
-
<dependency>
-
<groupId>org.ehcache</groupId>
-
<artifactId>ehcache</artifactId>
-
<version>3.5.2</version>
-
</dependency>
-
<dependency>
-
<groupId>javax.cache</groupId>
-
<artifactId>cache-api</artifactId>
-
<version>1.0.0</version>
-
</dependency>
二、配置
1、ehcache配置
-
<?xml version="1.0" encoding="UTF-8"?>
-
<ehcache:config
-
xmlns:ehcache="http://www.ehcache.org/v3"
-
xmlns:jcache="http://www.ehcache.org/v3/jsr107">
-
-
<ehcache:service>
-
<jcache:defaults>
-
<jcache:cache name="default" template="myDefaultTemplate"/>
-
</jcache:defaults>
-
</ehcache:service>
-
-
<ehcache:cache alias="allCameraCache">
-
<ehcache:key-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.String</ehcache:key-type>
-
<ehcache:value-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.String</ehcache:value-type>
-
<ehcache:expiry>
-
<ehcache:tti unit="minutes">20</ehcache:tti><!-- 数据过期时间20分钟 -->
-
</ehcache:expiry>
-
<ehcache:heap unit="entries">200</ehcache:heap><!-- 最多缓存200个对象 -->
-
</ehcache:cache>
-
-
<!-- 使用模板,可以覆盖模板的属性 -->
-
<ehcache:cache alias="cameraCache" uses-template="myDefaultTemplate">
-
<ehcache:key-type>java.lang.Object</ehcache:key-type>
-
<ehcache:value-type>java.lang.Object</ehcache:value-type>
-
<ehcache:expiry>
-
<ehcache:tti unit="minutes">30</ehcache:tti><!-- 数据过期时间30分钟,覆盖模板默认属性 -->
-
</ehcache:expiry>
-
<ehcache:heap unit="entries">500</ehcache:heap><!-- 最多缓存500个对象 -->
-
</ehcache:cache>
-
-
<!-- 默认模板 -->
-
<ehcache:cache-template name="myDefaultTemplate">
-
<ehcache:expiry>
-
<ehcache:none/><!-- 缓存永不过期 -->
-
</ehcache:expiry>
-
</ehcache:cache-template>
-
-
</ehcache:config>
2、spring配置
-
<?xml version="1.0" encoding="UTF-8"?>
-
<beans xmlns="http://www.springframework.org/schema/beans"
-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-
xmlns:context="http://www.springframework.org/schema/context"
-
xmlns:cache="http://www.springframework.org/schema/cache"
-
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
-
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
-
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
-
<!--eguid博客地址:http://bog.eguid.cc-->
-
<cache:annotation-driven cache-manager="cacheManager" /><!--扫描cache注解,如果已有可以不要-->
-
<context:component-scan base-package="cc.eguid.cache" /><!--扫描路径 -->
-
<!-- jcache缓存 -->
-
<bean id="jCacheManager" class="org.springframework.cache.jcache.JCacheManagerFactoryBean">
-
<property name="cacheManagerUri" value="classpath:config/ehcache.xml" /> <!--改成配置文件对应的路径-->
-
</bean>
-
<bean id="cacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
-
<property name="cacheManager" ref="jCacheManager" />
-
</bean>
-
</beans>
三、使缓存生效
1、注解方式使用
@Cacheable(value="cameraCache",key="#userid")
public String getCameraList(String userid,Integer otherparam) {
...
}
spring-cache的注解比较简单就不再赘述了。
Ehcache缓存初步实践实例(ehcache与spring整合方法)
Ehcache缓存初步实践实例(ehcache与spring整合方法)
- 引入jar包
在pom.xml文件引入如下依赖:
<!--ehcache缓存相关jar-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.3</version>
</dependency>
如图:
- 添加chcache. xml文件
内容如下:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir/ehcache"/>
<!-- 默认缓存 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap"/> </defaultCache>
<cache name="UniversalCache" maxElementsInMemory="0" eternal="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"/> </ehcache>
|
其中:
Cache标签表示自定义缓存区,可定义多个缓存区,他包含多个属性:
name : 缓存的名称,可以通过指定名称获取指定的某个Cache对象
maxElementsInMemory :内存中允许存储的最大的元素个数,0代表无限个
clearOnFlush:内存数量最大时是否清除。
eternal :设置缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期。根据存储数据的不同,例如一些静态不变的数据如省市区等可以设置为永不过时
timeToIdleSeconds : 设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds :缓存数据的生存时间(TTL),也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
overflowToDisk :内存不足时,是否启用磁盘缓存。
maxEntriesLocalDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
maxElementsOnDisk:硬盘最大缓存个数。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskPersistent:是否在VM重启时存储硬盘的缓存数据。默认值是false。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
- 在applicationContext.xml文件下添加如下bean:
<!-- 启用缓存注解开关 --> <cache:annotation-driven cache-manager="cacheManager"/>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"> <property name="cacheManager" ref="ehcache"/> </bean>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"> <property name="configLocation" value="classpath:ehcache.xml"/> </bean>
|
- 注解式缓存存储
项目实例:
@Cacheable(value="UniversalCache",key="#photoreviewid")//以photoreviewid为key存储缓存 public Photoreview queryPhotoreviewById(String photoreviewid) { return photoreviewDao.selectByPrimaryKey(photoreviewid); }
@Cacheable(value="UniversalCache",key="''PhotoreviewPageList''")//以''PhotoreviewPageList''为key存储缓存 public List<Map> queryPhotoreviewPageList(Map map,PageBounds pageBounds){ return photoreviewDao.selectPageList(map,pageBounds); }
@CacheEvict(value="UniversalCache",key="#photoreviewid")//删除某一key的value缓存 public int deletePhotoreviewById(String photoreviewid){ return photoreviewDao.deleteByPrimaryKey(photoreviewid); }
@CacheEvict(value="UniversalCache", allEntries=true)//删除value里面的所有缓存 public int savePhotoreview(Photoreview photoreview){ int result = 0; if(StringUtils.isEmpty(photoreview.getPhotoreviewid())){//增加用户信息 result = photoreviewDao.insert(photoreview); }else{//修改用户信息 result = photoreviewDao.updateByPrimaryKeySelective(photoreview); } return result; }
|
Spring对缓存的支持类似于对事务的支持。
首先使用注解标记方法,相当于定义了切点,然后使用Aop技术在这个方法的调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。
- @Cacheable
表明所修饰的方法是可以缓存的:当第一次调用这个方法时,它的结果会被缓存下来,在缓存的有效时间内,以后访问这个方法都直接返回缓存结果,不再执行方法中的代码段。
这个注解可以用condition属性来设置条件,如果不满足条件,就不使用缓存能力,直接执行方法。
可以使用key属性来指定key的生成规则。
@Cacheable 支持如下几个参数:
- value:缓存位置名称,不能为空,如果使用EHCache,就是ehcache.xml中声明的cache的name, 指明将值缓存到哪个Cache中
- key:缓存的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL,如果要引用参数值使用井号加参数名,如:#userId,
一般来说,我们的更新操作只需要刷新缓存中某一个值,所以定义缓存的key值的方式就很重要,最好是能够唯一,因为这样可以准确的清除掉特定的缓存,而不会影响到其它缓存值 ,
本例子中使用实体加冒号再加ID组合成键的名称,如”user:1”、”order:223123”等
- condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL
代码示例:
// 将缓存保存到名称为UserCache中,键为"user:"字符串加上userId值,如 ''user:1'' @Cacheable(value="UserCache", key="''user:'' + #userId") public User findById(String userId) { return (User) new User("1", "mengdee"); }
// 将缓存保存进UserCache中,并当参数userId的长度小于12时才保存进缓存,默认使用参数值及类型作为缓存的key // 保存缓存需要指定key,value, value的数据类型,不指定key默认和参数名一样如:"1" @Cacheable(value="UserCache", condition="#userId.length() < 12") public boolean isReserved(String userId) { System.out.println("UserCache:"+userId); return false; } |
- @CachePut
与@Cacheable不同,@CachePut不仅会缓存方法的结果,还会执行方法的代码段。它支持的属性和用法都与@Cacheable一致。
- @CacheEvict
与@Cacheable功能相反,@CacheEvict表明所修饰的方法是用来删除失效或无用的缓存数据。
@CacheEvict 支持如下几个参数:
-
- value:缓存位置名称,不能为空,同上
- key:缓存的key,默认为空,同上
- condition:触发条件,只有满足条件的情况才会清除缓存,默认为空,支持SpEL
- allEntries:true表示清除value中的全部缓存,默认为false
代码示例:
//清除掉UserCache中某个指定key的缓存 @CacheEvict(value="UserCache",key="''user:'' + #userId") public void removeUser(User user) { System.out.println("UserCache"+user.getUserId()); }
//清除掉UserCache中全部的缓存 @CacheEvict(value="UserCache", allEntries=true) public final void setReservedUsers(String[] reservedUsers) { System.out.println("UserCache deleteall"); } |
Ehcache简单测试示例(HelloWorld)
- pom引入jar包以及创建ehcache.xml文件
同上。
- HelloWorld
package com.mengdee.manage.cache;
import com.mengdee.manage.entity.Dog;
import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element;
public class CacheTest {
public static void main(String[] args) {
// 1. 创建缓存管理器 CacheManager cacheManager = CacheManager.create("./src/main/resources/ehcache.xml");
// 2. 获取缓存对象 Cache cache = cacheManager.getCache("HelloWorldCache");
// 3. 创建元素 Element element = new Element("key1", "value1");
// 4. 将元素添加到缓存 cache.put(element);
// 5. 获取缓存 Element value = cache.get("key1"); System.out.println(value); System.out.println(value.getObjectValue());
// 6. 删除元素 cache.remove("key1");
Dog dog = new Dog(1L, "taidi", (short)2); Element element2 = new Element("taidi", dog); cache.put(element2); Element value2 = cache.get("taidi"); Dog dog2 = (Dog) value2.getObjectValue(); System.out.println(dog2);
System.out.println(cache.getSize());
// 7. 刷新缓存 cache.flush();
// 8. 关闭缓存管理器 cacheManager.shutdown(); } } |
EVCache缓存在 Spring Boot中的实战
文章共 727字,阅读大约需要 2分钟 !
概 述
EVCache 是 Netflix开源的分布式缓存系统,基于 Memcached缓存和 Spymemcached客户端实现,其用在了大名鼎鼎的 AWS亚马逊云上,并且为云计算做了优化,提供高效的缓存服务。
本文利用 Memcached作为后端缓存实例服务器,并结合 Spring Boot,来实践一下 EVCache客户端的具体使用。
注: 本文首发于 My Personal Blog:CodeSheep·程序羊,欢迎光临 小站
编译 EVCache
- 第一步:Clone
git clone git@github.com:Netflix/EVCache.git
- 第二步:编译构建
./gradlew build
Downloading https://services.gradle.org/distributions/gradle-2.10-bin.zip
.................................................................................................................................
...
:evcache-client:check
:evcache-client:build
:evcache-client-sample:writeLicenseHeader
:evcache-client-sample:licenseMain
Missing header in: evcache-client-sample/src/main/java/com/netflix/evcache/sample/EVCacheClientSample.java
:evcache-client-sample:licenseTest UP-TO-DATE
:evcache-client-sample:license
:evcache-client-sample:compileTestJava UP-TO-DATE
:evcache-client-sample:processTestResources UP-TO-DATE
:evcache-client-sample:testClasses UP-TO-DATE
:evcache-client-sample:test UP-TO-DATE
:evcache-client-sample:check
:evcache-client-sample:build
BUILD SUCCESSFUL
Total time: 22.866 secs
- 第三步:得到构建生成物
同时 ~/EVCache/evcache-client/build/reports
目录下会生成相应构建报告:
接下来我们结合 Spring工程,来实战一下 EVCache Client的具体使用。
环境准备 / 工程搭建
首先准备好两台 memcached实例:
- 192.168.199.77:11211
- 192.168.199.78:11211
接下来搭建一个SpringBoot工程,过程不再赘述,需要注意的一点是 pom中需加入 EVCache的依赖支持
<dependency>
<groupId>com.netflix.evcache</groupId>
<artifactId>evcache-client</artifactId>
<version>4.137.0-SNAPSHOT</version>
</dependency>
注:我将 Spring工程设置在 8899端口启动
EVCache Client导入
- 编写 EVCache Client包装类
public class EVCacheClient {
private final EVCache evCache; // 关键角色在此
public EVCacheClient() {
String deploymentDescriptor = System.getenv("EVC_SAMPLE_DEPLOYMENT");
if ( deploymentDescriptor == null ) {
deploymentDescriptor = "SERVERGROUP1=192.168.199.77:11211;SERVERGROUP2=192.168.199.78:11211";
}
System.setProperty("EVCACHE_APP1.use.simple.node.list.provider", "true");
System.setProperty("EVCACHE_APP1-NODES", deploymentDescriptor);
evCache = new EVCache.Builder().setAppName("EVCACHE_APP1").build();
}
public void setKey(String key, String value, int timeToLive) throws Exception {
try {
Future<Boolean>[] _future = evCache.set(key, value, timeToLive);
for (Future<Boolean> f : _future) {
boolean didSucceed = f.get();
// System.out.println("per-shard set success code for key " + key + " is " + didSucceed);
// 此处可以针对 didSucceed做相应判断
}
System.out.println("finished setting key " + key);
} catch (EVCacheException e) {
e.printStackTrace();
}
}
public String getKey(String key) {
try {
String _response = evCache.<String>get(key);
return _response;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
很明显上述类主要提供了两个关键工具函数: setKey
和 getKey
- EVCache Config 配置导入
我们将 EVCacheClient 注入到Spring容器中
@Configuration
public class EVCacheConfig {
@Bean
public EVCacheClient evcacheClient() {
EVCacheClient evCacheClient = new EVCacheClient();
return evCacheClient;
}
}
编写 EVCache Service
上面几步完成之后,Service的编写自然顺理成章,仅仅是一层封装而已
@Service
public class EVCacheService {
@Autowired
private EVCacheClient evCacheClient;
public void setKey( String key, String value, int timeToLive ) {
try {
evCacheClient.setKey( key, value, timeToLive );
} catch (Exception e) {
e.printStackTrace();
}
}
public String getKey( String key ) {
return evCacheClient.getKey( key );
}
}
编写测试 Controller
我们编写一个方便用于测试的控制器,里面进行一系列对于缓存的 set
和 get
,从而便于观察实验结果
@RestController
public class EVCacheTestController {
@Autowired
private EVCacheService evCacheService;
@GetMapping("/testevcache")
public void testEvcache() {
try {
for ( int i = 0; i < 10; i++ ) {
String key = "key_" + i;
String value = "data_" + i;
int ttl = 180; // 此处将缓存设为三分钟(180s)生存期,时间一过,缓存即会失效
evCacheService.setKey(key, value, ttl);
}
for (int i = 0; i < 10; i++) {
String key = "key_" + i;
String value = evCacheService.getKey(key);
System.out.println("Get of " + key + " returned " + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实验验证
工程启动后,我们调用 Rest接口:localhost:8899/testevcache
,观察控制台中对于 key_0
到 key_9
等十个缓存 key的操作细节如下:
- 在 memcached集群中插入十条数据:
key_0
到key_9
注意此处是向每个后端 memcached缓存实例中都写入了 10条测试数据
- 从后端 memcached集群中读取刚插入的 10条数据
- 为了验证数据确实写入到后端 memcached,我们可以 telnet到后端 memcached中进行一一验证
而且这些数据的有效时间仅3分钟,3分钟后再次验证会发现数据已过期
[root@localhost ~]# telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is ''^]''.
get key_0
VALUE key_0 0 6
data_0
END
get key_1
VALUE key_1 0 6
data_1
END
get key_2
VALUE key_2 0 6
data_2
END
get key_3
VALUE key_3 0 6
data_3
END
get key_4
VALUE key_4 0 6
data_4
END
get key_5
VALUE key_5 0 6
data_5
END
get key_6
VALUE key_6 0 6
data_6
END
get key_7
VALUE key_7 0 6
data_7
END
get key_8
VALUE key_8 0 6
data_8
END
get key_9
VALUE key_9 0 6
data_9
END
本文扩展
当然本文所演示的 EVCache配合 memcached使用时,memcached被硬编码进代码,实际过程中使用,可以将其与 ZK等服务发现服务进行一个结合,实现灵活运用,这就不在本文进行赘述。
后 记
由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!
- My Personal Blog:CodeSheep 程序羊
- 我的半年技术博客之路
JAD-CACHE缓存框架,srping集成EhCache及实现本地缓存
本文介绍前文《JAD-CACHE缓存框架,架构设计篇》设计的JAD-CACHE框架的基础上进一步介绍 JAD-CACHE如果实现本地缓存以及集成EhCache。
目前项目已经在开源中国码云平台上开源,代码地址:
https://git.oschina.net/457049726/jad-cache
1、实现本地缓存
Spring自带的cache模块本身内置了一个用Map实现的本地缓存。但因为功能比较简单,JAD-CACHE对它进行了扩展,如下类图:
上图中,上面一排都是JAD-CACHE的抽象实现类,在前文已经介绍过了,本地缓存的实现,主要是下面一排的三个实现类。
图中LocalMapCache类就是Cache接口的实现类,它继承于JadAbstractCacheManager,拥有父类的可被CacheClient管理、支持缓存null值、支持差异化存活时间等特性。而它本身对于缓存数据的实现是通过它自身维护的ConcurrentMap列表来实现的。
在JAD-CACHE架构设计中,每一个Cache实例都会被一个CacheClient管理,对于LocalMapCache,管理它的CacheClient就是SimpleLocalCacheClient实例。SimpleLocalCacheClient是AbstractCacheClient的子类,它实现的父类的抽象方法registryCacheManager(),在它被初始化的过程中,注册一个SimpleLocalCacheManager实例,作为它控制的CacheManager(因为JAD-CACHE框架要求每一个CacheClient实例中都持有一个CacheManager类型的引用)。SimpleLocalCacheManager类从JAD-CACHE框架中的JadAbstractCacheManager类继承,并实现了getMissingCache(string),在这个getMissingCache()方法,实现了跟据参数决定是否需要自动创建Cache的逻辑。这使得,开发人员在需要使用某个Cache时,不需要事先配置,只需要使定它的autoCreateCache属性为true就可以自动创建Cache了。
因为SimpleLocalCacheManager类是SimpleLocalCacheClient实例在初始化的时候自动创建并注入的,所以这个实例对于开发人员来说是透明的,开发人员只需配置SimpleLocalCacheClient实例而无需配置SimpleLocalCacheManager,只要控制SimpleLocalCacheClient的启停等状态,就可以通过它自动注入的SimpleLocalCacheManager实例间接的操作缓存。
2、集成EhCache
Spring缓存模块本身是自带了与EhCache集成相关的CacheManager实现类。但应功能简单,JAD-CACHE在这里重新提供了几个新的实现,如下图:
上图中JadEhCacheCache是Cache接口的实现类,它包装了Ehcache这个Ehcache厂商实现的缓存对像。它实现了父类lookup(),get(),put()等操作缓存的抽像方法。具体实现逻辑就委托给Ehcache对像通过Ehcache自身的api去操作Ehcache缓存。
JadEhCacheCache实例对像的CacheClient实现类是EhcacheClient类,这个类从AbstractCacheClient继承,除了拥有父类的autoCreateCache等属性外,还有一个configFile属性,这个属性用于配置Ehcache配置文件和名称路径(默认为classpath跟目录下的ehcache.xml)。
JadEhCacheCache实例在初始化的时候,会自动注入一个JadEhCacheCacheManager类型的实例,这个类是JadAbstractCacheManager类的子类,它实现了父类的loadCaches()等方法,在它初始化的时候,会自动加载ehcache.xml配置文件中配置的Cache,而且具备自动创建缺省配置Cache的能力。
JadEhCacheCacheManager是EhcacheClient实例初始化的时候自动注入的,对开发人员人透明,开发人只要配置EhcacheClient实例而无需再配置JadEhCacheCacheManager就可以集成Ehcache了。
3、集成MemCahce
Spring缓存模块并没有提供对MemCache的支持,需要实现,但因MemCache是一个服务器缓存,自身比较复杂。JAD-CACHE采用阿里开源的memcache客户端代码,在它的基础上做了扩展,这里暂不讨论,稍后写一篇文章单独讨论。
更多信息,欢迎扫以下二维码关注我的微信公众号:
关于通过SpringCache缓存嵌套的可缓存操作和spring cache缓存的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于Cache系列:spring-cache简单三步快速应用ehcache3.x-jcache缓存(spring4.x)、Ehcache缓存初步实践实例(ehcache与spring整合方法)、EVCache缓存在 Spring Boot中的实战、JAD-CACHE缓存框架,srping集成EhCache及实现本地缓存等相关知识的信息别忘了在本站进行查找喔。
本文标签: