文章目录
前言
本文使用 编程方式 实现 Spring+ehcache
Spring 版本
本文使用 Spring framework 版本为 4.3.0.RELEASE
Spring 缓存注解的介绍
测试方案和基础代码
方案
首先,我们先提供一个简单的 Service 以及几个简单的实现,然后添加 ehcache 相关配置,之后通过测试方法展示Spring 和 ehcache 结合的效果
实体类
package com.bestcxx.test;
public class User {
private Long id;
private String userName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Override
public String toString() {
return "User [id=" + id + ", userName=" + userName + "]";
}
}
Service 接口
public interface AInterface {
String aMethod(User user);
void bMethod(User user);
String cMethod(User user);
String dMethod(User user);
String eMethod(Long id);
String fMethod(Long id);
}
接口具体实现
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Component;
@Component
public class AImpl implements AInterface {
/**
* @Cacheable 适用场景:单纯查询方法的缓存
* value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
* key 是缓存的名字,可以用 Spel 表达式直接从方法入参获取
* condition 是条件判断,满足条件的才会被缓存
* sync 是否同步,true 为支持,Spring 4.3 后开始支持
*/
//@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100",sync=true)
//@Cacheable(value = {"ehcache_test"},key = "#user.id",condition = "#user.id>100")
@Cacheable(value = {"ehcache_test"},key = "#p0.id",condition = "#p0.id>100")
@Override
public String aMethod(User user) {
System.out.println("invoke aMethod");
return Math.random()+"";
}
/**
* @CacheEvict 适用场景:该注解修饰的方法,在调用时会清除指定的缓存,可以一次指定多个
* value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
* key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
* condition 是条件判断,满足条件的才会起作用
* allEntries 默认为只有 key 值匹配的才会被清楚缓存,设置为true则所有的缓存都失效,不可与key同时指定
* beforeInvocation 是否在方法调用前清楚缓存,默认为false 即只有方法完全调用成功才可以
*/
@CacheEvict(value = { "ehcache_test" },key = "#user.id",condition = "#user.id>100")
@Override
public void bMethod(User user) {
System.out.println("invoke bMethod()");
}
/**
* @CachePut 适用场景
* value 是缓存规则的名字,可以指定多个,指定多个就会有多个缓存
* key 缓存的名字,可以用 Spel 表达式直接从方法入参获取
* condition 是条件判断,满足条件的才会起作用
*/
@CachePut(value = { "ehcache_test" },key = "#user.id",condition="#user.id>100")
@Override
public String cMethod(User user) {
System.out.println("invoke cMethod");
return Math.random()+"";
}
/**
* 这个方法看看就行,一般不会这么使用
* @Caching 可以同时配置多个@Cacheable、@CacheEvict、@CachePut
* 内部规则就是所有分规则的集合
* 需要注意的是
*/
@Caching(cacheable = {@Cacheable(value={"ehcache_test"},key="#p0.id"),@Cacheable(value={"ehcache_test"},key="#p0.id")},
evict = {@CacheEvict(value={"ehcache_test","demo"},key= "#p0.id")},
put = {@CachePut(value={"ehcache_test"},key="#p0.id")})
@Override
public String dMethod(User user) {
System.out.println("invoke dMethod");
return Math.random()+"";
}
/**
* #root.method.name 可以省略 #root.
*/
//@Cacheable(value="ehcache_test",key="#root.method.name")
@Cacheable(value="ehcache_test",key="method.name")
@Override
public String eMethod(Long id) {
System.out.println("invoke eMethod");
return id+"";
}
/**
* #root.caches[index] 可以省略 #root
* #root.args[index] 可以省略 #root
*/
//@Cacheable(value="ehcache_test",key="caches[0]")
@Cacheable(value="ehcache_test",key="args[0]")
@Override
public String fMethod(Long id) {
System.out.println("invoke fMethod");
return id+"";
}
}
Spring xml 配置
<context:component-scan base-package="com.bestcxx.test"/>
增加缓存配置——这里结合ehcache
- ehcache.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"
updateCheck="false">
<!-- 默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。 -->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<!-- 有命名的 cache,可以定义多个 -->
<cache name="ehcache_test"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="10"
timeToLiveSeconds="20"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
<!-- name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
maxBytesLocalHeap="50m" 最大存储为50m
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
-->
ehcache 配置类
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
@Configuration
@EnableCaching
public class ConfigCache {
@Bean
public EhCacheCacheManager ehCacheManager(net.sf.ehcache.CacheManager cm) {
return new EhCacheCacheManager(cm);
}
@Bean
public EhCacheManagerFactoryBean ehCache() {
EhCacheManagerFactoryBean ehCacheFactoryBean=new EhCacheManagerFactoryBean();
ehCacheFactoryBean.setConfigLocation(new ClassPathResource("com/bestcxx/test/ehcache.xml"));
return ehCacheFactoryBean;
}
}
测试方法
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestClass {
@SuppressWarnings("resource")
public static void main(String[] args) {
//加载配置文件,获取 applicationContext 对象
ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:applicationContext2.xml");
AInterface aInterface=applicationContext.getBean(AInterface.class);
User user=new User();
user.setId(99l);
user.setUserName("Jecket");
//条件测试->100 才会触发缓存机制
System.out.println("--缓存条件触发测试--");
System.out.println(aInterface.aMethod(user));
System.out.println(aInterface.aMethod(user));
//第一次设置缓存,第二次直接读取缓存
System.out.println("--缓存初次二次查询测试--");
user.setId(199l);
System.out.println(aInterface.aMethod(user));
System.out.println(aInterface.aMethod(user));
//清除缓存测试,再次查询会发现缓存失效
System.out.println("--清除缓存测试--");
aInterface.bMethod(user);
System.out.println(aInterface.aMethod(user));
System.out.println(aInterface.aMethod(user));
//强制更新缓存测试-会直接更新缓存
System.out.println("--强制更新缓存测试--");
System.out.println(aInterface.cMethod(user));
System.out.println(aInterface.aMethod(user));
System.out.println(aInterface.aMethod(user));
//尝试从 root 获取缓存配置信息
System.out.println("--尝试从 root 获取缓存配置信息--");
aInterface.bMethod(user);
System.out.println(aInterface.eMethod(user.getId()));
System.out.println(aInterface.eMethod(user.getId()));
System.out.println("--尝试从 root 获取缓存配置信息--");
aInterface.bMethod(user);
System.out.println(aInterface.fMethod(user.getId()));
System.out.println(aInterface.fMethod(user.getId()));
}
}
测试输出结果
--缓存条件触发测试--
invoke aMethod
0.5251943952019077
invoke aMethod
0.7259120572321393
--缓存初次二次查询测试--
invoke aMethod
0.501591345010235
0.501591345010235
--清除缓存测试--
invoke bMethod()
invoke aMethod
0.7954256410911823
0.7954256410911823
--强制更新缓存测试--
invoke cMethod
0.20779400279129234
0.20779400279129234
0.20779400279129234
--尝试从 root 获取缓存配置信息--
invoke bMethod()
invoke eMethod
199
199
--尝试从 root 获取缓存配置信息--
invoke bMethod()
invoke fMethod
199
199