【spring-data-redis】实现列表缓存方案---代码实现

1、配置pom.xml

		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.7.2.RELEASE</version>
		</dependency>

2、配置spring

cache:annotation-driven开启注解,其中的选项mode这里要说明一下,proxy|aspectj指定使用哪种代理方式。proxy代理使用的是基于spring-AOP技术,aspectj代理则使用的Aspectj切面技术。如果使用aspectj选项需要引入对spring-aspects.jar的依赖,并开启load-time weaving (or compile-time weaving)功能。

其中碰到的坑是注解不生效。原因是,在默认情况下,mode为proxy代理,代码在同一个具体类的方法中去调用该类的另一个方法时,被调用的方法被设置了cache注解,但是由于spring AOP不支持类中方法间的调用的代理。解决办法是将被调用的方法重构到另一个类中,cache注解生效。

	<!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
	<cache:annotation-driven cache-manager="cacheManager" key-generator="keyGenerator" />


	<bean id="jedisConnectionFactory"
		class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
		<property name="hostName" value="${redis.host}" />
		<property name="port" value="${redis.port}" />
		<property name="timeout" value="${redis.timeout}" />
		<!-- 引用配置文件 spring-context-jedis.xml 中的bean -->
		<property name="poolConfig" ref="jedisPoolConfig" />
	</bean>

	<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
		<property name="connectionFactory" ref="jedisConnectionFactory" />
		<property name="keySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="valueSerializer">
			<bean class="com.frbao.web.common.cache.RedisObjectSerializer" />
		</property>
		<property name="hashKeySerializer">
			<bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
		</property>
		<property name="hashValueSerializer">
			<bean class="com.frbao.web.common.cache.RedisObjectSerializer" />
		</property>
	</bean>

	<!-- spring自己的缓存管理器,这里定义了缓存位置名称 ,即注解中的value -->
	<bean id="cacheManager" class="com.frbao.web.common.cache.RedisCacheManager">
		<constructor-arg index="0" ref="redisTemplate" />
	</bean>
	
	<!-- 方法上使用@Cacheable时,针对方法处理的key的生成器 -->
	<bean id="keyGenerator" class="com.frbao.web.common.cache.DefaultCacheMethodKeyGenerator" />

3、数据查询方法实现

查询时,支持根据指定参数flush判断是否刷新缓存。其中flush参数不进行缓存的key生成

    @CachePut(value = MethodCacheKey.PROJECLIST, condition = "#flush")
    @Cacheable(value = MethodCacheKey.PROJECLIST, condition = "!#flush")
    public ObjectListDto fetchProjectList(final FetchProjectListParameter parameterObject,
            @KeyParam(include = false) boolean flush) {
        ObjectListDto objectList = new ObjectListDto();
        objectList = RemoteInvoker.doInvoke(new InvocationHandler<ObjectListDto>() {
            @Override
            public ObjectListDto execute() {

                if (parameterObject.orderBy == 0) {
                    return projectListService.selectProjectList(parameterObject.itype, parameterObject.fyearincomerate,
                            parameterObject.idaycount, parameterObject.pageNo, parameterObject.pageSize);
                } else {
                    return projectListService.selectProjectListOrderbyPage(parameterObject.itype,
                            parameterObject.fyearincomerate, parameterObject.idaycount, parameterObject.orderBy,
                            parameterObject.pageNo, parameterObject.pageSize);
                }

            }
        }, InterfaceNo.INVEST_0_002);

        return objectList;
    }

4、缓存key注解

在生成key时,指定include为false的参数不进行拼接

@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface KeyParam {
    boolean include() default true;
}

5、缓存key生成器

解析请求参数,如果注解KeyParam的参数指定include为false则不进行拼接。

public class DefaultCacheMethodKeyGenerator implements KeyGenerator {

    /**
     * 对参数进行拼接后MD5
     */
    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder sb = new StringBuilder();
        sb.append(target.getClass().getName());
        sb.append(".").append(method.getName());

        Annotation[][] paraAnnosArry = method.getParameterAnnotations();

        int i = 0;// 参数索引
        StringBuilder paramsSb = new StringBuilder();
        for (Annotation[] paraAnnos : paraAnnosArry) {
            KeyParam key = null;
            for (Annotation anno : paraAnnos) {
                if (KeyParam.class.isInstance(anno)) {// 如果存在指定是否生成键值注解,进行解析
                    key = (KeyParam) anno;
                    break;
                }
            }
            // 如果不指定,默认生成包含到键值中
            if ((key == null || key.include()) && params[i] != null) {
                paramsSb.append(String.valueOf(params[i]));
            }
            i++;

        }

        if (paramsSb.length() > 0) {
            sb.append("_").append(MD5.encode(paramsSb.toString()));
        }
        return sb.toString();
    }

}

6、改造CacheManager,支持不同的cache实现(替换原RedisCache实现)

通过简单的继承,重写createAndAddCache方法,返回自定义的RedisCache实现。

import org.springframework.cache.Cache;
import org.springframework.data.redis.core.RedisOperations;

public class RedisCacheManager extends org.springframework.data.redis.cache.RedisCacheManager {

    /**
     * 构造函数
     * 
     * @param redisOperations
     */
    public RedisCacheManager(@SuppressWarnings("rawtypes") RedisOperations redisOperations) {
        super(redisOperations);
    }

    @Override
    protected Cache createAndAddCache(String cacheName) {
        addCache(createDiffCache(cacheName));
        return super.getCache(cacheName);
    }

    @SuppressWarnings("unchecked")
    private RedisCache createDiffCache(String cacheName) {
        long expiration = computeExpiration(cacheName);

        return new RedisCache(cacheName, (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(),
                expiration);
    }
}

7、自定义RedisCache,支持按照不同的待缓存的数据(key/value)配置缓存时间

由于原RedisCache实现的设置缓存,其中缓存时间是从cacheMetadata成员变量中获得,但是其属性却为私有和final定义,因此不能通过简单的继承进行重写。

private final RedisCacheMetadata cacheMetadata;
	@Override
	public void put(final Object key, final Object value) {

		put(new RedisCacheElement(new RedisCacheKey(key).usePrefix(cacheMetadata.getKeyPrefix()).withKeySerializer(
				redisOperations.getKeySerializer()), value).expireAfter(cacheMetadata.getDefaultExpiration()));
	}

 因此通过代理方式,在基于原Cache接口上主要是重写put和putIfAbsent方法

实现上如下代码在RedisCacheElement上调用expireAfter方法。

expireAfter(ExpireCacheStratergies.getExpireSeconds(redisCache.getName(), value))

public class RedisCache implements Cache {

    private final byte[] keyPrefix;
    private final org.springframework.data.redis.cache.RedisCache redisCache;

    /**
     * Constructs a new <code>RedisCache</code> instance.
     *
     * @param name cache name
     * @param prefix
     * @param redisOperations
     * @param expiration
     */
    public RedisCache(String name, byte[] prefix, RedisOperations<? extends Object, ? extends Object> redisOperations,
            long expiration) {

        hasText(name, "non-empty cache name is required");

        keyPrefix = prefix;
        redisCache = new org.springframework.data.redis.cache.RedisCache(name, prefix, redisOperations, expiration);
    }

    /**
     * Return the value to which this cache maps the specified key, generically specifying a type that return value will be cast
     * to.
     * 
     * @param key
     * @param type
     * @return
     * @see DATAREDIS-243
     */
    public <T> T get(Object key, Class<T> type) {
        return redisCache.get(key, type);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#get(java.lang.Object)
     */
    @Override
    public ValueWrapper get(Object key) {
        return redisCache.get(key);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#put(java.lang.Object, java.lang.Object)
     */
    @Override
    public void put(final Object key, final Object value) {
        @SuppressWarnings("rawtypes")
        RedisOperations redisOperations = (RedisOperations) redisCache.getNativeCache();

        redisCache.put(new RedisCacheElement(
                new RedisCacheKey(key).usePrefix(keyPrefix).withKeySerializer(redisOperations.getKeySerializer()), value)
                        .expireAfter(ExpireCacheStratergies.getExpireSeconds(redisCache.getName(), value)));
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#putIfAbsent(java.lang.Object, java.lang.Object)
     */
    public ValueWrapper putIfAbsent(Object key, final Object value) {
        @SuppressWarnings("rawtypes")
        RedisOperations redisOperations = (RedisOperations) redisCache.getNativeCache();

        return redisCache.putIfAbsent(new RedisCacheElement(
                new RedisCacheKey(key).usePrefix(keyPrefix).withKeySerializer(redisOperations.getKeySerializer()), value)
                        .expireAfter(ExpireCacheStratergies.getExpireSeconds(redisCache.getName(), value)));
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#evict(java.lang.Object)
     */
    public void evict(Object key) {
        redisCache.evict(key);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#clear()
     */
    public void clear() {
        redisCache.clear();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.cache.Cache#getName()
     */
    public String getName() {
        return redisCache.getName();
    }

    /**
     * {@inheritDoc} This implementation simply returns the RedisTemplate used for configuring the cache, giving access to the
     * underlying Redis store.
     */
    public Object getNativeCache() {
        return redisCache.getNativeCache();
    }

 

转载于:https://my.oschina.net/u/914290/blog/832546

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值