spring cache一般会与ehcache或者memcached来做缓存,但是一般做的是代码缓存,因此本次用c语言实现的redis来与spring cache来做数据缓存,减少内存的消耗。还可以用spring cache的注解@Cacheable把缓存数据存到redis中。本次的代码都已分享,下载链接在文章底部.
在spring的源码中有个cache包如下图:
在这个包中有两个接口,第一个接口Cache是缓存操作接口,里面声明了对缓存数据的put,get等操作。
另外一个CacheManager接口
这个接口声明了获取缓存实例的方法。
因此我们要用redis来替代spring cache做缓存我们就必须要实现cache接口,并且继承cacheManager接口来做实现,接下来我们看下cacheManager中有哪些已经实现的的类可以供我们参考。如下图:
我们主要看第一个AbstractCacheManager,我们可以大致看下这个类,主要是使用的ConcurrentMap来做put和get操作的,这个是java并发包里面的,因为AbstractCacheManager是一个抽象类,我们可以猜想应该还有类会继承它,如下图:
我们可以看到我们稍微熟悉的两个类,AbstractTransactionSupportingCacheManager和SimpleCacheManager类,这两个类是一般在以前做缓存时配置在xml文件中的一个bean,SimpleCacheManager是简单缓存类,AbstractTransactionSupportingCacheManager是带事物支持的缓存类。因此在本次集成中我们需要改写这个类,为了减少代码入侵,因此最好的方法是我们另外写个类继承这个AbstractTransactionSupportingCacheManager这个类并实现DisposableBean这个接口,然后重写afterPropertiesSet,destroy,loadCaches,getCache这几个方法,afterPropertiesSet是在配置文件加载完之后,bean加载之前执行的。
我们参考下AbstractCacheManager这个类中的loadCaches和getCache方法。所以我们需要重写loadCaches和getCache方法是为了加载缓存个获取缓存的实例。
分析完spring cache的源码之后我们大致知道了它的运作流程,所以现在来用redis来做spring cache的缓存。
1.首先写个类来实现spring中的cache接口。
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import org.springframework.cache.Cache;
import org.springframework.cache.support.SimpleValueWrapper;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisConnectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JedisCache implements Cache {
private static final Logger logger = LoggerFactory.getLogger(JedisCache.class);
private String name;
private List<JedisPool> jedisPoolList;
private CacheStoreRouter<JedisPool> cacheStoreRouter;
private Serializer serializer;
private int expires;
public JedisCache(String name, List<JedisPool> jedisList, CacheStoreRouter<JedisPool> cacheStoreRouter,
Serializer serializer, int expires) {
this.name = name;
this.jedisPoolList = jedisList;
this.cacheStoreRouter = cacheStoreRouter;
this.serializer = serializer;
this.expires = expires;
}
@Override
public Object getNativeCache() {
return this.jedisPoolList;
}
@Override
public Cache.ValueWrapper get(Object key) {
if (key != null) {
JedisPool jedisPool = (JedisPool) this.cacheStoreRouter.pickUp(this.jedisPoolList, this.name, key);
if (jedisPool != null) {
Jedis jedis = null;
boolean broken = false;
try {
jedis = jedisPool.getResource();
String uniqueKey = uniqueKey(key);
String valueSerial = jedis.get(uniqueKey);
if ((this.name.equals("sessionCache")) && (this.expires > 0)) {
long flag = -2L;
flag = jedis.expire(uniqueKey, this.expires).longValue();
if (flag == -2L) {
logger.warn("Cache {} key {} miss.", this.name, key);
return null;
}
}
Object value = null;
try {
value = this.serializer.toObject(valueSerial);
} catch (ClassNotFoundException e) {
logger.error("", e);
}
logger.debug("uniqueKey={}, valueSerial={}", new Object[] { uniqueKey, valueSerial });
if (value != null) {
logger.info("Cache {} key {} hit.", this.name, key);
return new SimpleValueWrapper(value);
}
logger.warn("Cache {} key {} miss.", this.name, key);
} catch (JedisConnectionException e) {
logger.error("key={}", key, e);
broken = true;
} finally {
if (jedis != null) {
if (broken) {
jedisPool.returnBrokenResource(jedis);
} else {
jedisPool.returnResource(jedis);
}
}
}
} else {
logger.error("Cache store route error.");
}
} else {
logger.warn("Key is null.");
}
return null;
}
@Override
public <T> T get(Object o, Class<T> aClass) {
Cache.ValueWrapper vw = get(o);
return (T) vw.get();
}
@Override
public void put(Object key, Object value) {
if ((key != null) && (value != null)) {
JedisPool jedisPool = (JedisPool) this.cacheStoreRouter.pickUp(this.jedisPoolList, this.name, key);
if (jedisPool != null) {
Jedis jedis = null;
boolean broken = false;
try {
jedis = jedisPool.getResource();
String uniqueKey = uniqueKey(key);
String valueSerial = this.serializer.toString(value);
String result = jedis.setex(uniqueKey, this.expires, valueSerial);
logger.debug("uniqueKey={}, expires={}, valueSerial={}, result={}",
new Object[] { uniqueKey, String.valueOf(this.expires), valueSerial, result });
} catch (JedisConnectionException e) {
logger.error("key={}", key, e);
broken = true;
} finally {
if (jedis != null) {
if (broken) {
jedisPool.returnBrokenResource(jedis);
} else {
jedisPool.returnResource(jedis);
}
}
}
} else {
logger.error("Cache store route error.");
}
} else {
logger.warn("Key or value is null. Key={}, value={}", key, value);
}
}
@Override
public Cache.ValueWrapper putIfAbsent(Object o, Object o1) {
return null;
}
@Override
public void evict(Object key) {
if (key != null) {
JedisPool jedisPool = (JedisPool) this.cacheStoreRouter.pickUp(this.jedisPoolList, this.name, key);
if (jedisPool != null) {
Jedis jedis = null;
boolean broken = false;
String[] keys = ((String) key).split(",");
for (String cacheKey : keys) {
try {
jedis = jedisPool.getResource();
String uniqueKey = uniqueKey(cacheKey);
long removeCount = jedis.del(uniqueKey).longValue();
logger.debug("uniqueKey={}, removeCount={}",
new Object[] { uniqueKey, String.valueOf(removeCount) });
} catch (JedisConnectionException e) {
logger.error("key={}", key, e);
broken = true;
} finally {
if (jedis != null) {
if (broken) {
jedisPool.returnBrokenResource(jedis);
} else {
jedisPool.returnResource(jedis);
}
}
}
}
} else {
logger.error("Cache store route error.");
}
} else {
logger.warn("Key is null.");
}
}
@Override
public void clear() {
long deleteCount = 0L;
for (JedisPool jedisPool : this.jedisPoolList) {
if (jedisPool != null) {
Jedis jedis = null;
boolean broken = false;
try {
jedis = jedisPool.getResource();
Set<String> keySet = jedis.keys(uniqueKey("*"));
String[] keyArray = (String[]) keySet.toArray(new String[keySet.size()]);
if (keyArray.length > 0) {
deleteCount += jedis.del(keyArray).longValue();
}
} catch (JedisConnectionException e) {
logger.error("", e);
broken = true;
} finally {
if (jedis != null) {
if (broken) {
jedisPool.returnBrokenResource(jedis);
} else {
jedisPool.returnResource(jedis);
}
}
}
}
}
logger.debug("count={}", Long.valueOf(deleteCount));
}
private String uniqueKey(Object key) {
return this.name + "#" + String.valueOf(key);
}
@Override
public String getName() {
return this.name;
}
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
return null;
}
}
2.另外写个类继承AbstractTransactionSupportingCacheManager并实现DisposableBean
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cache.Cache;
import org.springframework.cache.transaction.AbstractTransactionSupportingCacheManager;
import redis.clients.jedis.JedisPool;
public class JedisCacheManager extends AbstractTransactionSupportingCacheManager implements DisposableBean {
private static final Logger logger = LoggerFactory.getLogger(JedisCacheManager.class);
public static final String sessionCacheName = "sessionCache";
private ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<String, Cache>();
private Map namedClients;
private CacheStoreJedisHashRouter cacheStoreJedisHashRouter;
private Serializer serializer;
private int timeout;
private int expires;
private List<JedisPool> jedisPoolList;
private GenericObjectPoolConfig config;
private boolean testOnBorrow;
@Override
public void afterPropertiesSet() {
config.setTestOnBorrow(testOnBorrow);
String server = namedClients.get("default").toString();
String[] split = server.split(":");
JedisPool jedisPool = new JedisPool(config, split[0], Integer.valueOf(split[1]), timeout);
jedisPoolList = new ArrayList<>();
jedisPoolList.add(jedisPool);
}
@Override
public void destroy() throws Exception {
}
@Override
protected Collection<? extends Cache> loadCaches() {
Collection<Cache> values = cacheMap.values();
return values;
}
@Override
public Cache getCache(String name) {
Cache cache = cacheMap.get(name);
if (cache == null) {
cache = new JedisCache(name, jedisPoolList, cacheStoreJedisHashRouter, serializer, expires);
cacheMap.put(name, cache);
}
return cache;
}
public Map getNamedClients() {
return namedClients;
}
public void setNamedClients(Map namedClients) {
this.namedClients = namedClients;
}
public CacheStoreJedisHashRouter getCacheStoreJedisHashRouter() {
return cacheStoreJedisHashRouter;
}
public void setCacheStoreJedisHashRouter(CacheStoreJedisHashRouter cacheStoreJedisHashRouter) {
this.cacheStoreJedisHashRouter = cacheStoreJedisHashRouter;
}
public Serializer getSerializer() {
return serializer;
}
public void setSerializer(Serializer serializer) {
this.serializer = serializer;
}
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getExpires() {
return expires;
}
public void setExpires(int expires) {
this.expires = expires;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public GenericObjectPoolConfig getConfig() {
return config;
}
public void setConfig(GenericObjectPoolConfig config) {
this.config = config;
}
}
在JedisCacheManager这个类中注入类一些redis的配置,可以对应下面这个配置文件。
<?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" >
<cache:annotation-driven cache-manager="springcacheManager"
error-handler="cacheError" />
<!--单机配置 -->
<bean id="springcacheManager" class="com.zlb.ecp.cache.JedisCacheManager">
<property name="namedClients">
<map>
<entry key="default" value="${redis.servers}" />
</map>
</property>
<property name="cacheStoreJedisHashRouter">
<bean class="com.zlb.ecp.cache.CacheStoreJedisHashRouter" />
</property>
<property name="serializer">
<bean class="com.zlb.ecp.cache.JsonSerializer" />
</property>
<property name="timeout" value="${redis.pool.timeout}" />
<property name="expires" value="${redis.pool.expires}" />
<property name="config">
<bean class="org.apache.commons.pool2.impl.GenericObjectPoolConfig" />
</property>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
</bean>
</beans>
对应的properties文件
#多个servers用逗号(",")隔开,不要配置redis从机的IP,只需要redis主机IP
redis.servers=127.0.0.1:6379
#连接超时时间(单位:秒)
redis.pool.timeout=3000
#缓存时间(单位:秒)
redis.pool.expires=86400
#在获取一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.pool.testOnBorrow=true
以上两个类是主要的类,另外还有接口以及这个两个类中需要用的帮助类方法的代码就不贴出来,我分享到csdn上(注:需要的jar包最好是spring4.0以上和java支持redis的jedisjar包,jar包自行下载,本次文章只描叙如何实现,如果找不到可以看代码中的import需要哪些包自行百度),
下载链接:http://download.csdn.net/download/lh2420124680/10035602