一.spring-memcached.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:p="http://www.springframework.org/schema/p" xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">
<!-- <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" /> --><!-- 开启缓存 -->
<!-- 自定义key生成策略-->
<cache:annotation-driven key-generator="stringKeyGenerator" />
<bean id="stringKeyGenerator" class="com.wlsq.fs.cache.StringKeyGenerator" />
<bean id="cacheManager" class="com.wlsq.fs.cache.MemcachedCacheManager">
<!-- 是否事务环绕的,如果true,则如果事务回滚,缓存也回滚,默认false -->
<property name="transactionAware" value="true" />
<property name="caches">
<set>
<!--产品 -->
<bean class="com.wlsq.fs.cache.MemcachedSpringCache">
<property name="name" value="food_prod" />
<property name="memcachedClient" ref="memcachedClient" />
<property name="expiredDuration" value="1200" />
</bean>
<!-- 用户信息的缓存 -->
<bean class="com.wlsq.fs.cache.MemcachedSpringCache">
<property name="name" value="food_userInfo" />
<property name="memcachedClient" ref="memcachedClient" />
<property name="expiredDuration" value="300" />
</bean>
<!-- 用户信息的缓存 -->
</set>
</property>
</bean>
<!-- 配置缓存服务器 -->
<bean id="memcachedClient" class="net.spy.memcached.spring.MemcachedClientFactoryBean">
<property name="servers" value="${memcached.servers}" />
<property name="protocol" value="${memcached.protocol}" />
<property name="transcoder">
<bean class="net.spy.memcached.transcoders.SerializingTranscoder">
<property name="compressionThreshold" value="1024" />
</bean>
</property>
<property name="opTimeout" value="${memcached.opTimeout}" />
<property name="timeoutExceptionThreshold" value="${memcached.timeoutExceptionThreshold}" />
<property name="hashAlg">
<value type="net.spy.memcached.DefaultHashAlgorithm">KETAMA_HASH</value>
</property>
<property name="locatorType" value="${memcached.locatorType}" />
<property name="failureMode" value="${memcached.failureMode}" />
<property name="useNagleAlgorithm" value="${memcached.useNagleAlgorithm}" />
<property name="authDescriptor" ref="authDescriptor" />
</bean>
<!-- 阿里云身份校验 -->
<bean id="authDescriptor" class="net.spy.memcached.auth.AuthDescriptor">
<constructor-arg index="0" >
<list>
<value>PLAIN</value>
</list>
</constructor-arg>
<constructor-arg index="1" ref="plainCallbackHandler"/>
</bean>
<!-- 阿里云账号密码配置 -->
<bean id="plainCallbackHandler" class="net.spy.memcached.auth.PlainCallbackHandler">
<constructor-arg index="0" value="${memcached.username}"/>
<constructor-arg index="1" value="${memcached.password}"/>
</bean>
</beans>
二、自定义key生成策略StringKeyGenerator
public class StringKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object paramObject, Method paramMethod, Object[] paramArrayOfObject){
StringBuilder localStringBuilder = new StringBuilder(50);
localStringBuilder.append(paramObject.getClass().getName());
localStringBuilder.append(paramMethod.getName());
for (Object localObject : paramArrayOfObject)
{
if (localObject != null)
localStringBuilder.append(localObject.toString());
localStringBuilder.append(".");
}
return localStringBuilder.toString();
}
}
对应memecahed配置文件中的
<!-- 自定义key生成策略-->
<cache:annotation-driven key-generator="stringKeyGenerator" />
<bean id="stringKeyGenerator" class="com.wlsq.fs.cache.StringKeyGenerator" />
三、MemcachedCacheManager
继承spring的AbstractTransactionSupportingCacheManager
public class MemcachedCacheManager extends AbstractTransactionSupportingCacheManager{
public MemcachedCacheManager() {
}
private Collection<? extends Cache> caches;
@Override
protected Collection<? extends Cache> loadCaches() {
return this.caches;
}
public void setCaches(Collection<? extends Cache> caches) {
this.caches = caches;
}
}
对应memcached配置文件
<bean id="cacheManager" class="com.wlsq.fs.cache.MemcachedCacheManager">
<!-- 是否事务环绕的,如果true,则如果事务回滚,缓存也回滚,默认false -->
<property name="transactionAware" value="true" />
<property name="caches">
...
...
四、实现基于Spring Cache抽象体系的Memcached缓存(实现了Cache api接口)
public class MemcachedSpringCache implements Cache, InitializingBean{
private final static Logger logger = LoggerFactory.getLogger(MemcachedSpringCache.class);
private String name;
private MemcachedClient memcachedClient;
/**
* 默认最长缓存时间为1小时
*/
private static final int MAX_EXPIRED_DURATION = 60 * 60;
/** Null值的最长缓存时间 */
private static final int NULL_VALUE_EXPIRATION = 60 * 60 * 24 * 7;
/** 增量过期时间允许设置的最大值 */
private static final int DELTA_EXPIRATION_THRESHOLD = 60 * 60 * 24 * 30;
/**
* 缓存数据超时时间
*/
private int expiredDuration = MAX_EXPIRED_DURATION;
private static final Object NULL_HOLDER = new NullHolder();
private boolean allowNullValues = true;
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(memcachedClient, "memcachedClient must not be null!");
}
@Override
public void clear() {
try {
memcachedClient.flush();
} catch (Exception e) {
logger.error("memcached执行flush出现异常", e);
}
}
/**
* @Description: 移除缓存, 安全的evict方法,在3秒内返回结果, 否则取消操作.
*/
@Override
public void evict(Object key) {
String cacheKey = getCacheKey(key);
logger.debug("删除缓存的Key:{}", cacheKey);
Future<Boolean> future = memcachedClient.delete(cacheKey);
try {
future.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
future.cancel(false);
logger.error("memcached清除缓存出现异常, key={}, server={}", cacheKey,
memcachedClient.getNodeLocator().getPrimary(cacheKey).getSocketAddress(), e);
}
}
/**
* @Description: 根据key得到一个ValueWrapper,然后调用其get方法获取值
*/
@Override
public ValueWrapper get(Object key) {
String cacheKey = getCacheKey(key);
try {
StopWatch sw = new StopWatch();
sw.start();
Object value = memcachedClient.get(cacheKey);
sw.stop();
if (sw.getTime() > 50) {
logger.info("读取memcached用时{}, key={}", sw.getTime(), cacheKey);
}
return (value != null ? new SimpleValueWrapper(fromStoreValue(value)) : null);
} catch (Exception e) {
logger.error("读取memcached缓存发生异常, key={}, server={}", cacheKey,
memcachedClient.getNodeLocator().getPrimary(cacheKey).getSocketAddress(), e.getCause());
return null;
}
}
/**
* @Description: 根据key,和value的类型直接获取value
*/
@Override
public <T> T get(Object key, Class<T> type) {
ValueWrapper element = get(key);
Object value = (element != null ? element.get() : null);
if (value == null)
return null;
if (type != null && !type.isInstance(value)) {
throw new IllegalStateException("缓存的值类型指定错误 [" + type.getName() + "]: " + value);
}
return (T) value;
}
@Override
public String getName() {
return this.name;
}
@Override
public Object getNativeCache() {
return this.memcachedClient;
}
/**
* @Description: 往缓存放数据
* 安全的Set方法,在3秒内返回结果, 否则取消操作
*/
@Override
public void put(Object key, Object value) {
String cacheKey = getCacheKey(key);
logger.debug("放入缓存的Key:{}, Value:{}, StoreValue:{}", cacheKey, value, toStoreValue(value));
int expiration = expiredDuration;
if (value == null) {
if (allowNullValues) {
value = NULL_HOLDER; // 若允许缓存空值,则替换null为占坑对象;不允许直接缓存null,因为无法序列化
}
if (expiredDuration > NULL_VALUE_EXPIRATION) {
expiration = NULL_VALUE_EXPIRATION; // 缩短空值的过期时间,最长缓存7天
}
} else if (expiredDuration > DELTA_EXPIRATION_THRESHOLD) {
expiration += (int) (System.currentTimeMillis() / 1000); // 修改为UNIX时间戳类型的过期时间,使能够设置超过30天的过期时间
// 注意:时间戳计算这里有2038问题,
// 2038-1-19 11:14:07 (GMT +8) 后,转换成的 int 会溢出,导致出现负值
}
Future<Boolean> future = memcachedClient.set(cacheKey, expiration, value);
try {
future.get(3, TimeUnit.SECONDS);
} catch (Exception e) {
future.cancel(false);
logger.error("memcached写入缓存发生异常, key={}, server={}", cacheKey,
memcachedClient.getNodeLocator().getPrimary(cacheKey).getSocketAddress(), e);
}
}
@Override
public ValueWrapper putIfAbsent(Object arg0, Object arg1) {
// TODO Auto-generated method stub
return null;
}
private static class NullHolder implements Serializable {
private static final long serialVersionUID = -99681708140860560L;
}
/**
* 存入到缓存的key,由缓存的区域+key对象值串接而成
* @param key key对象
* @return
*/
private String getCacheKey(Object key) {
return this.name + key.toString();
}
protected Object fromStoreValue(Object storeValue) {
if (this.allowNullValues && storeValue instanceof NullHolder) {
return null;
}
return storeValue;
}
protected Object toStoreValue(Object userValue) {
if (this.allowNullValues && userValue == null) {
return NULL_HOLDER;
}
return userValue;
}
public void setName(String name) {
this.name = name;
}
public void setMemcachedClient(MemcachedClient memcachedClient) {
this.memcachedClient = memcachedClient;
}
public void setExpiredDuration(int expiredDuration) {
this.expiredDuration = expiredDuration;
}
public void setAllowNullValues(boolean allowNullValues) {
this.allowNullValues = allowNullValues;
}
}
对应memcached配置文件
<bean id="cacheManager" class="com.wlsq.fs.cache.MemcachedCacheManager">
<!-- 是否事务环绕的,如果true,则如果事务回滚,缓存也回滚,默认false -->
<property name="transactionAware" value="true" />
<property name="caches">
<set>
<!--产品 -->
<bean class="com.wlsq.fs.cache.MemcachedSpringCache">
<property name="name" value="food_prod" />
<property name="memcachedClient" ref="memcachedClient" />
<property name="expiredDuration" value="1200" />
</bean>
...
...
四、基于spring注解调用
注:此处使用spring注解配置暂不记录
@Caching(evict={@CacheEvict(value="food_prod",key = "#productId"),@CacheEvict(value="food_AppProd",key = "#productId"),@CacheEvict(value="food_AppProdDto",key = "#productId")})
public int delProductProcess(int processId,int productId) {
......
......
}
或自定义
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
@Caching(evict = {
@CacheEvict(value = "food_prod", key = "#productinfo.id"),
@CacheEvict(value = "food_AppProd", key = "#productinfo.id"),
@CacheEvict(value = "food_AppProdDto", key = "#productinfo.id")
})
public @interface ProductUpdateVo {
}
------------使用----------------
@ProductUpdateVo
public int updateByPrimaryKeySelective(Productinfo productinfo) {
......
}
此处只记录配置和使用,仅供参考