memcached与spring提供的cache接口整合demo

一.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) {
   ......
}

此处只记录配置和使用,仅供参考

原理请参考:https://my.oschina.net/004/blog/368664

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值