最近在做一个项目,要加上缓存。于是上网百度实现了一个二级缓存的例子:
首先是pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>WE</groupId>
<artifactId>WE</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>WE</name>
<!--统一配置jar包版本 -->
<properties>
<springmvc-version>4.3.0.RELEASE</springmvc-version>
</properties>
<!--项目所需jar包 -->
<dependencies>
<!--单元测试依赖包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--spring mvc depend jars begin -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springmvc-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>${springmvc-version}</version>
</dependency>
<!--spring mvc depend jars end -->
<!-- ssm or ssh need spring jar -->
<!-- 添加mybatis的核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 添加mybatis与Spring整合的核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.7</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.7.3</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<!-- spring-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.6.0.RELEASE</version>
</dependency>
<!---sitemesh pom -->
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
<version>3.0.0</version>
</dependency>
<!-- pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.0.0</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
<!-- commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- com.alibaba.fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.6</version>
</dependency>
<!-- org.json -->
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
<!-- org.apache.shiro/shiro-all -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.3.2</version>
</dependency>
<!-- org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.6</version>
</dependency>
<!-- net.sf.ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.4</version>
</dependency>
<!-- netty 5.0 -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
<!-- System Log -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<!-- pagehelper 分页插件 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
</dependencies>
<!--项目构建 -->
<build>
<!--项目打包之后的名称 -->
<finalName>WE</finalName>
<!--构建需要的插件 -->
<plugins>
<!--编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>
1.7</source>
<target>1.7</target>
</configuration>
</plugin>
<!-- 本地tomcat7插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
如果只是二级缓存的话里面东西会冗余,什么单元测试,druid连接池,sitemesh,pagehelper插件,fastjson,shiro,generator工具,netty等都是可以删掉的。
好,导入了jar包,后面搭ssm框架的过程我就省略了,网上一堆教程。
搭建好之后,大概的框架如下:
包名任意取,然后分以下几种的包,
cache——放入自定义缓存类
dao——数据持久层
model——实体类
service and impl——业务逻辑
utils——工具包
web——放入controller控制
至于resource中,mapper、mybatis、properties和shiro文件跟缓存关系不大。spring中新建applicationContext-cache.xml,填写缓存相关配置。
至于其他的配置都是SSM的配置了。
先实现缓存,再实现模糊删除。这里吐槽下,之前百度模糊删除资料很少,反而是自定义一个注解,弄了半天没弄出来,后来发现根本没必要。。。然后感觉自己傻傻的。。。
好了,上代码。
这是redis的缓存类,
package com.xx.cache;//包名自定义
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Callable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Created by [email protected] on 2018-05-07 09:27:15.
*/
public class RedisCache implements Cache {
static final Logger logger = LoggerFactory.getLogger(RedisCache.class); //日志
private RedisTemplate<String, Object> redisTemplate;
private String name;
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return this.name;
}
@Override
public Object getNativeCache() {
// TODO Auto-generated method stub
return this.redisTemplate;
}
@Override
public ValueWrapper get(Object key) {
// TODO Auto-generated method stub
System.out.println("get key:"+ key.toString());
final String keyf = key.toString();
Object object = null;
object = redisTemplate.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
byte[] key = keyf.getBytes();
byte[] value = connection.get(key);
if (value == null) {
return null;
}
return toObject(value);
}
});
return (object != null ? new SimpleValueWrapper(object) : null);
}
@Override
public void put(Object key, Object value) {
// TODO Auto-generated method stub
final String keyf = key.toString();
final Object valuef = value;
final long liveTime = 86400;
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
byte[] keyb = keyf.getBytes();
byte[] valueb = toByteArray(valuef);
connection.set(keyb, valueb);
if (liveTime > 0) {
connection.expire(keyb, liveTime);
}
return 1L;
}
});
}
private byte[] toByteArray(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}
private Object toObject(byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
@Override
public void evict(Object key) {
// TODO Auto-generated method stub
System.out.println("del key:"+key.toString());//注意这个就会发现我为啥觉得自己傻傻的
final String keyf = key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyf.getBytes());
}
});
}
@Override
public void clear() {
// TODO Auto-generated method stub
System.out.println("clear key");
redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection connection) throws DataAccessException {
connection.flushDb();
return "ok";
}
});
}
@Override
public <T> T get(Object key, Class<T> type) {
// TODO Auto-generated method stub
return null;
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T get(Object o, Callable<T> callable) {
return null;
}
}
这个没啥说的,就是代码,然后是二级(Ehcache+Redis)的缓存类:
package com.xx.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import net.sf.ehcache.Element;
/**
* 两级缓存,一级:ehcache,二级为redisCache
*
*/
public class EhRedisCache implements Cache{
private static final Logger logger = Logger.getLogger(EhRedisCache.class);
private String name;
private net.sf.ehcache.Cache ehCache;
private RedisTemplate<String, Object> redisTemplate;
private long liveTime = 1*60*60; //默认1h=1*60*60
@Override
public String getName() {
return this.name;
}
@Override
public Object getNativeCache() {
return this;
}
@Override
public ValueWrapper get(Object key) {
Element value = ehCache.get(key);
logger.info("Cache L1 (ehcache) :{"+key+"}={"+value+"}");
if (value!=null) {
return (value != null ? new SimpleValueWrapper(value.getObjectValue()) : null);
}
final String keyStr = key.toString();
Object objectValue = redisTemplate.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] key = keyStr.getBytes();
byte[] value = connection.get(key);
if (value == null) {
return null;
}
//每次获得,重置缓存过期时间
if (liveTime > 0) {
connection.expire(key, liveTime);
}
return toObject(value);
}
},true);
ehCache.put(new Element(key, objectValue));//取出来之后缓存到本地
logger.info("Cache L2 (redis) :{"+key+"}={"+objectValue+"}");
return (objectValue != null ? new SimpleValueWrapper(objectValue) : null);
}
@Override
public void put(Object key, Object value) {
ehCache.put(new Element(key, value));
final String keyStr = key.toString();
final Object valueStr = value;
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] keyb = keyStr.getBytes();
byte[] valueb = toByteArray(valueStr);
connection.set(keyb, valueb);
if (liveTime > 0) {
connection.expire(keyb, liveTime);
}
return 1L;
}
},true);
}
@Override
public void evict(Object key) {
System.out.println("del levels key:"+key.toString());
ehCache.remove(key);
final String keyStr = key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
return connection.del(keyStr.getBytes());
}
},true);
}
@Override
public void clear() {
ehCache.removeAll();
redisTemplate.execute(new RedisCallback<String>() {
public String doInRedis(RedisConnection connection)
throws DataAccessException {
connection.flushDb();
return "clear done.";
}
},true);
}
public net.sf.ehcache.Cache getEhCache() {
return ehCache;
}
public void setEhCache(net.sf.ehcache.Cache ehCache) {
this.ehCache = ehCache;
}
public RedisTemplate<String, Object> getRedisTemplate() {
return redisTemplate;
}
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public long getLiveTime() {
return liveTime;
}
public void setLiveTime(long liveTime) {
this.liveTime = liveTime;
}
public void setName(String name) {
this.name = name;
}
/**
* 描述 : Object转byte[]. <br>
* @param obj
* @return
*/
private byte[] toByteArray(Object obj) {
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
oos.close();
bos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
return bytes;
}
/**
* 描述 : byte[]转Object . <br>
* @param bytes
* @return
*/
private Object toObject(byte[] bytes) {
Object obj = null;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
obj = ois.readObject();
ois.close();
bis.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
return obj;
}
@Override
public <T> T get(Object arg0, Class<T> arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T get(Object arg0, Callable<T> arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
// TODO Auto-generated method stub
return null;
}
}
缓存类搞定了,然后就是配置了。
这是applicationContext-cache.xml的全部配置
<?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/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder
ignore-resource-not-found="true" location="classpath:properties/config.properties" />
<!-- cache-manager指向缓存管理器的bean id名 -->
<!-- springCacheManager/ehRedisCacheManager proxy-target-class="true" -->
<cache:annotation-driven cache-manager="springCacheManager" proxy-target-class="true" />
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空闲数 -->
<property name="maxIdle" value="${redis.maxIdle}" />
<!-- 最大空连接数 -->
<property name="maxTotal" value="${redis.maxTotal}" />
<!-- 最大等待时间 -->
<property name="maxWaitMillis" value="${redis.maxWaitMillis}" />
<!-- 连接超时时是否阻塞,false时报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="${redis.blockWhenExhausted}" />
<!-- 返回连接时,检测连接是否成功 -->
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<!-- Spring-redis连接池管理工厂 -->
<bean id="jedisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!-- IP地址 -->
<property name="hostName" value="${redis.host}" />
<!-- password -->
<property name="password" value="${redis.password}" />
<!-- 端口号 -->
<property name="port" value="${redis.port}" />
<!-- 超时时间 默认2000 -->
<property name="timeout" value="${redis.timeout}" />
<!-- 连接池配置引用 -->
<property name="poolConfig" ref="poolConfig" />
<!-- usePool:是否使用连接池 -->
<property name="usePool" value="${redis.usePool}" />
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
<!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->
<bean id="springCacheManager" class="org.springframework.cache.support.SimpleCacheManager">
<property name="caches">
<set>
<!-- 这里可以配置多个redis -->
<bean class="com.xx.cache.RedisCache">
<property name="redisTemplate" ref="redisTemplate" />
<property name="name" value="layerCache" />
<!-- myCache名称要在类或方法的注解中使用 -->
</bean>
<!-- 自定义ehcache+redis-->
<bean id="ehRedisCache" class="com.xx.cache.EhRedisCache">
<property name="redisTemplate" ref="redisTemplate" />
<property name="ehCache" ref="ehCache"/>
<property name="name" value="layersCache"/>
<!-- redis缓存保留时间,单位秒 -->
<property name="liveTime" value="1000"/>
</bean>
</set>
</property>
</bean>
<!-- ehCache 操作对象 -->
<!-- ehCacheManager 位于spring-shiro.xml -->
<bean id="ehCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="ehCacheManager"/>
</bean>
</beans>
其实也就几个配置:连接池管理、连接池配置、缓存管理器。
其中ehCacheManager:
<bean id="ehCacheManager" class ="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:shiro/ehcache-shiro.xml" />
<property name="shared" value="true"></property>
</bean>
然后ehcache-shiro.xml:
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false" dynamicConfig="false" monitoring="autodetect">
<diskStore path="java.io.tmpdir" />
<!--
diskStore path:用来配置磁盘缓存使用的物理路径
name: 缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)
eternal="false" 元素是否永恒,如果是就永不过期(必须设置)
maxElementsOnDisk====磁盘缓存中最多可以存放的元素数量,0表示无穷大
maxElementsInMemory="1000" 内存缓存中最多可以存放的元素数量(必须设置)
timeToIdleSeconds="0" 导致元素过期的访问间隔(秒为单位). 0表示可以永远空闲,默认为0
timeToLiveSeconds="600" 元素在缓存里存在的时间(秒为单位). 0 表示永远存在不过期
overflowToDisk="false" 当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设置)
diskPersistent="false" 磁盘缓存在VM重新启动时是否保持(默认为false)
diskExpiryThreadIntervalSeconds="100" 磁盘失效线程运行时间间隔,默认是120秒
memoryStoreEvictionPolicy="LFU" 内存存储与释放策略.当达到maxElementsInMemory时
共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)默认使用"最近使用"策略
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
然后看springCacheManager中的set,我们可用的value就有两个了,一个是layerCache,直接存redis,一级的,还有一个是layersCache,二级的。
写一个测试:
public interface CacheService {
@Cacheable(value = "layerCache",key = "#key")
String addCache(String key);
@CacheEvict(value = "layerCache",key = "#key")
String delCache(String key);
@Cacheable(value = "layersCache",key = "#key")
String addCache2(String key);
@CacheEvict(value = "layersCache",key = "#key")
String delCache2(String key);
}
实现:
public class CacheServiceImpl implements CacheService{
@Override
public String addCache(String key) {
System.out.println("第一次调用会会打印此语句---一级缓存----");
return "cache test success !!!";
}
@Override
public String delCache(String key) {
System.out.println("移除缓存的值---一级缓存----");
return "cache remove success !!!";
}
@Override
public String addCache2(String key) {
System.out.println("第一次调用会会打印此语句---二级缓存----");
return "cache test success !!!";
}
@Override
public String delCache2(String key) {
System.out.println("移除缓存的值---二级缓存----");
return "cache remove success !!!";
}
}
controller:
/**
* 测试cache配置是否成功
*
* @return
*/
@RequestMapping("/cache")
public String testCache() {
String value = cacheService.addCache("cacheTest");
return "/cacheTest.jsp";
}
@RequestMapping("/delcache")
public String delcache() {
String value = cacheService.delCache("cacheTest");
return "/cacheTest.jsp";
}
@RequestMapping("/cache2")
public String testCache2() {
String value = cacheService.addCache2("cacheTest2");
return "/cacheTest.jsp";
}
@RequestMapping("/delcache2")
public String delcache2() {
String value = cacheService.delCache2("cacheTest2");
return "/cacheTest.jsp";
}
运行:
分别点击测试一级,测试二级一次,看控制台输出:
,然后再分别点击一次:
这时可以看到已经进入缓存了,没有再进入impl的方法内了。然后我们移除缓存再试一次:
发现移除缓存后再测试又没有进入缓存了,好了至此缓存已成功!
至于模糊删除缓存,我一开始没啥头绪,上网搜点资料,然后看见有人用spring 的AOP来实现,我因此尝试了一波,我并未成功,忙活了半天。。。。
好了,看代码中有一行,
System.out.println("del key:"+key.toString());
这不是已经取到key 了啊?傻傻的每次都能从控制台看到,一直没反应过来。
这不就是重写@CacheEvict方法吗?那还不就简单了,利用正则循环key,模糊匹配的就删掉。
EhRedisCache的改如下:
@Override
public void evict(Object key) {
System.out.println("del levels key:"+key.toString());
if (key.toString().startsWith("regex:")) {
String pattern = key.toString().replace("regex:", "");
//String pattern = res.replace("*", ".*");
System.out.println("pattern:"+pattern);
Set<String> keys_redis = redisTemplate.keys(pattern);
for (Iterator<String> iterator = keys_redis.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println("remove redis keys:"+string);
}
redisTemplate.delete(keys_redis);
List<?> cacheKeys_encache = ehCache.getKeys();
pattern = pattern.replace("*", ".*");
for (Object cacheKey: cacheKeys_encache) {
if(Pattern.matches(pattern, cacheKey.toString())){
System.out.println("remove encache keys:"+cacheKey.toString());
ehCache.remove(cacheKey.toString());
}
}
return;
}
ehCache.remove(key);
final String keyStr = key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
return connection.del(keyStr.getBytes());
}
},true);
}
RedisCache的改如下:
@Override
public void evict(Object key) {
// TODO Auto-generated method stub
System.out.println("del key:"+key.toString());
if (key.toString().startsWith("regex:")) {
String pattern = key.toString().replace("regex:", "");
//String pattern = res.replace("*", ".*");
System.out.println("pattern:"+pattern);
Set<String> keys_redis = redisTemplate.keys(pattern);
for (Iterator iterator = keys_redis.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println("remove redis keys:"+string);
}
redisTemplate.delete(keys_redis);
return;
}
final String keyf = key.toString();
redisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection connection) throws DataAccessException {
return connection.del(keyf.getBytes());
}
});
}
这种场景,是在增删改查的时候,我查询的时候从缓存,然后增加、删除、修改的时候移除相关缓存。
大概是这样:
@Cacheable(value = "layersCache",key = "#root.targetClass + '_' + 'count'")
long countByExample(CampusExample example);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int deleteByExample(CampusExample example);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int deleteByPrimaryKey(Integer id);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int insert(Campus record);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int insertSelective(Campus record);
@Cacheable(value = "layersCache",key = "#root.targetClass + '_demo_' + #example")
List<Campus> selectByExample(CampusExample example);
@Cacheable(value = "layersCache",key = "#root.targetClass + '_demo_' + #id")
Campus selectByPrimaryKey(Integer id);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int updateByExampleSelective(@Param("record") Campus record, @Param("example") CampusExample example);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int updateByExample(@Param("record") Campus record, @Param("example") CampusExample example);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int updateByPrimaryKeySelective(Campus record);
@CacheEvict(value = "layersCache",key = "'regex:'+#root.targetClass + '_*' ")
int updateByPrimaryKey(Campus record);
这是测试:
可以看到pattern:CampusServiceImpl_*,然后就移除一堆了。至此模糊删除也已成功。