springboot定制redis缓存

本文详细介绍了Spring Cache注解在与Redis集成时的高级用法,包括自定义缓存Key生成器、缓存管理和缓存超时配置。通过示例代码展示了如何实现更灵活的缓存策略。
  1. key生成核心逻辑
    package com.kiiik.config.redis.cache;
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import org.apache.commons.lang.ArrayUtils;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.util.CollectionUtils;
    /**
     *作者 : iechenyb<br>
     *类描述: 说点啥<br>
     *创建时间: 2018年11月20日
     * "userInfor#1800#90";
     */
    public class BaseCacheKey {
    	public final static String CACHE_SPLIT = "#";
    	Log log = LogFactory.getLog(BaseCacheKey.class);
    	private final Object[] params;
    	private final int hashCode;
    	private final String className;
    	private final String methodName;
    	private final String simpleName;
    	private  String baseKey=null;//kiiik.zuul.baseKey.***
     
    	public BaseCacheKey(Object target, Method method, Object[] elements) throws Exception{
    		this.className=target.getClass().getName();
    		this.simpleName=target.getClass().getSimpleName();
    		this.methodName=getMethodName(method);
    		this.params = new Object[elements.length];
    		System.arraycopy(elements, 0, this.params, 0, elements.length);
    		this.hashCode=generatorHashCode();
    		initKey(method);
    	}
    	
    	public BaseCacheKey(Object target, Method method) throws Exception{
    		this.className=target.getClass().getName();
    		this.simpleName=target.getClass().getSimpleName();
    		this.methodName=getMethodName(method);
    	    this.params = null;
    	    StringBuilder sb = new StringBuilder();
            sb.append(target.getClass().getName());
            sb.append(method.getName());
         	this.hashCode= sb.toString().hashCode();
         	initKey(method);
    	}
    	//key#timeout#refresh
    	public String initKey(Method method) throws Exception{
    		List<String> keys = new ArrayList<>();
    		if(method.isAnnotationPresent(Cacheable.class)){
    			Cacheable a = method.getAnnotation(Cacheable.class);
    			if(!ArrayUtils.isEmpty(a.cacheNames())){
    				keys = Arrays.asList(a.cacheNames());
    			}else{
    				keys = Arrays.asList(a.value());
    			}
    		}else if(method.isAnnotationPresent(CachePut.class)){
    			CachePut a = method.getAnnotation(CachePut.class);
    			if(!ArrayUtils.isEmpty(a.cacheNames())){
    				keys = Arrays.asList(a.cacheNames());
    			}else{
    				keys = Arrays.asList(a.value());
    			}
    		}else if(method.isAnnotationPresent(CacheEvict.class)){
    			CacheEvict a = method.getAnnotation(CacheEvict.class);
    			if(!ArrayUtils.isEmpty(a.cacheNames())){
    				keys = Arrays.asList(a.cacheNames());
    			}else{
    				keys = Arrays.asList(a.value());
    			}
    		}else{
    			throw new Exception("缓存key的value或者names格式不正确。格式=唯一名称#过期时间#刷新时间");
    		}
    		if(!CollectionUtils.isEmpty(keys)){
    			baseKey = keys.get(0).split(CACHE_SPLIT)[0];
    		}else{
    			baseKey=null;
    		}
    		return baseKey;
    	}
    	
    	private String getMethodName(Method method){
    		StringBuilder builder = new StringBuilder(method.getName());
    		Class<?>[] types = method.getParameterTypes();
    		if(types.length!=0){
    			builder.append("(");
    			for(Class<?> type:types){
    				String name = type.getName();
    				builder.append(name+",");
    			}
    			builder.append(")");
    		}
    		return builder.toString();
    	}
     
    	@Override
    	public boolean equals(Object obj){
    		if(this==obj)
    			return true;
    		if (obj == null)
    			return false;
    		if (getClass() != obj.getClass())
    			return false;
    		BaseCacheKey o=(BaseCacheKey) obj;
    		if(this.hashCode!=o.hashCode())
    			return false;
    		
    		if (!Arrays.equals(params, o.params))
    			return false;
    		
    		return true;
    	}
    	
    	@Override
    	public final int hashCode() {
    		return hashCode;
    	}
    	/**
    	 * 
    	 *作者 : iechenyb<br>
    	 *方法描述: 参数为空时,每个对象都一样<br>
    	 *创建时间: 2018年11月22日
    	 *@return
    	 */
    	private int generatorHashCode() {
    		final int prime = 31;
    		int result = 1;
    		result = prime * result + hashCode;
    		result = prime * result + ((className == null) ? 0 : className.hashCode());
    		result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
    		if(params.length>0){
    			result = prime * result + Arrays.deepHashCode(params);//没有考虑对象的情况,需要对每个对象参数进行重写hashcode方法
    		}
    		return result;
    	}
        /**
         * 类名+方法名+name|value+hashcode
         */
    	@Override
    	public String toString() {
    		/*return "BaseCacheKey [params=" + Arrays.deepToString(params) + ", className=" + className + ", methodName="
    				+ methodName + "]";*/
    	    return RedisUtils.keyBuilder(simpleName, baseKey, String.valueOf(hashCode));
    	}
    }
    

     

  2. 定制redis缓存
    package com.kiiik.config.redis.cache;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.data.redis.cache.RedisCache;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.core.RedisOperations;
    import org.springframework.util.Assert;
    
    /**
     * 作者 : iechenyb<br>
     * 类描述: 说点啥<br>
     * 创建时间: 2018年11月21日
     */
    public class CustomizeRedisCacheManager extends RedisCacheManager {
    	Log log = LogFactory.getLog(CustomizeRedisCacheManager.class);
    
    	CustomizeRedisCacheManager(RedisOperations<?, ?> redisOperations) {
    		super(redisOperations);
    	}
    
    	@SuppressWarnings("unchecked")
    	@Override
    	protected RedisCache createCache(String cacheName) {
    		Assert.hasText(cacheName, "CacheName must not be null or empty!");
    
    		String[] values = cacheName.split("#");
    
    		long expiration = computeExpiration(values);
    		return new RedisCache(values[0], (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null),
    				getRedisOperations(), expiration, false);
    	}
    
    	private long computeExpiration(String[] values) {
    		if (values.length > 1) {
    			return Long.parseLong(values[1]);
    		}
    		// 如果说想使用默认的过期时间而不指定特殊时间,则可以直接@Cacheable(cacheNames="name"),不需要加'#过期时间'了
    		return super.computeExpiration(values[0]);
    	}
    }
    

     

  3. 定制key生成器
    package com.kiiik.config.redis.cache;
    
    import java.lang.reflect.Method;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.cache.CacheManager;
    import org.springframework.cache.annotation.EnableCaching;
    import org.springframework.cache.interceptor.KeyGenerator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.RedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import org.springframework.stereotype.Component;
    
    /**
     * 作者 : iechenyb<br>
     * 类描述: 说点啥<br>
     * 创建时间: 2018年11月20日
     */
    @Component("cacheKeyGenerator")
    // 开启缓存
    @EnableCaching
    public class BaseCacheKeyGenerator implements KeyGenerator {
    	Log log = LogFactory.getLog(BaseCacheKeyGenerator.class);
    
    	// private Duration timeToLive = Duration.ZERO;
    	@Override
    	public Object generate(Object target, Method method, Object... params) {
    		try {
    			return new BaseCacheKey(target, method, params).toString();
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    
    	/*
    	 * 默认的处理方式
    	 * 
    	 * @Bean public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate)
    	 * {
    	 * 
    	 * redisTemplate.setKeySerializer(keySerializer());
    	 * redisTemplate.setValueSerializer(valueSerializer());
    	 * redisTemplate.setHashKeySerializer(keySerializer());
    	 * redisTemplate.setHashValueSerializer(valueSerializer());
    	 * 
    	 * RedisCacheManager redisCacheManager= new
    	 * RedisCacheManager(redisTemplate);
    	 * redisCacheManager.setDefaultExpiration(3600); Map<String,Long>
    	 * expiresMap=new HashMap<>(); expiresMap.put("Product",5L);
    	 * redisCacheManager.setExpires(expiresMap); return redisCacheManager; }
    	 */
    	// 定制的处理方式
    	@Bean
    	public CacheManager cacheManager(RedisTemplate<?, ?> redisTemplate) {
    		redisTemplate.setKeySerializer(stringKeySerializer());
    		redisTemplate.setValueSerializer(valueSerializer());
    		redisTemplate.setHashKeySerializer(stringKeySerializer());
    		redisTemplate.setHashValueSerializer(valueSerializer());
    
    		CustomizeRedisCacheManager redisCacheManager = new CustomizeRedisCacheManager(redisTemplate);
    		redisCacheManager.setDefaultExpiration(3600);
    		Map<String, Long> expiresMap = new HashMap<>();
    		expiresMap.put("Product", 5L);
    		redisCacheManager.setExpires(expiresMap);
    		return redisCacheManager;
    	}
    
    	@Bean(name = "redisTemplate")
    	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    		redisTemplate.setConnectionFactory(redisConnectionFactory);
    
    		redisTemplate.setKeySerializer(stringKeySerializer());
    		redisTemplate.setHashKeySerializer(stringKeySerializer());
    		redisTemplate.setValueSerializer(valueSerializer());
    		redisTemplate.setHashValueSerializer(valueSerializer());
    
    		log.debug("自定义RedisTemplate加载完成");
    		return redisTemplate;
    	}
    
    	
    	public JdkSerializationRedisSerializer jdkKeySerializer() {
    		return new JdkSerializationRedisSerializer();
    	}
    	
    	public StringRedisSerializer stringKeySerializer() {
    		return new StringRedisSerializer();
    	}
    
    	private RedisSerializer<Object> valueSerializer() {
    		return new GenericJackson2JsonRedisSerializer();
    	}
    }
    

     

  4. 辅助工具类
    package com.kiiik.config.redis.cache;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.util.StringUtils;
    
    import com.cyb.freemarker.mvc.base.BaseController;
    /**
     *作者 : iechenyb<br>
     *类描述: 说点啥<br>
     *创建时间: 2018年11月22日
     */
    
    public class RedisUtils {
    	Log log = LogFactory.getLog(RedisUtils.class);
    
    	 /**
         * 主数据系统标识 
         */
        public static final String KEY_PREFIX = "system:example";
        /**
         * 分割字符,默认[:],使用:可用于rdm分组查看
         */
        private static final String KEY_SPLIT_CHAR = ":";
    
        /**
         * redis的key键规则定义
         * @param module 模块名称
         * @param func 方法名称
         * @param args 参数..
         * @return key
         */
        public static String keyBuilder(String module, String func, String... args) {
            return keyBuilder(null, module, func, args);
        }
        
        private static String keyName(String value) throws Exception{
        	if(StringUtils.isEmpty(value)){
        		throw new Exception("缓存没有定义对应的key名称");
        	}
        	if(value.split("#").length > 0){
        		return value.split("#")[0];
        	}
    		return value;
        }
        public static String keyBuilder(Class<?> clazz, String name) throws Exception {
            return new StringBuffer(KEY_PREFIX)
            		.append(KEY_SPLIT_CHAR)
            		.append(clazz.getSimpleName())
            		.append(KEY_SPLIT_CHAR)
            		.append(keyName(name))
            		.toString();
        }
    
        /**
         * redis的key键规则定义
         * @param module 模块名称
         * @param func 方法名称
         * @param objStr 对象.toString()
         * @return key
         */
        public static String keyBuilder(String module, String func, String objStr) {
            return keyBuilder(null, module, func, new String[]{objStr});
        }
    
        /**
         * redis的key键规则定义
         * @param prefix 项目前缀
         * @param module 模块名称
         * @param func 方法名称
         * @param objStr 对象.toString()
         * @return key
         */
        public static String keyBuilder(String prefix, String module, String func, String objStr) {
            return keyBuilder(prefix, module, func, new String[]{objStr});
        }
    
        /**
         * redis的key键规则定义
         * @param prefix 项目前缀
         * @param module 模块名称
         * @param func 方法名称
         * @param args 参数..
         * @return key
         */
        public static String keyBuilder(String prefix, String module, String func, String... args) {
            // 项目前缀
            if (prefix == null) {
                prefix = KEY_PREFIX;
            }
            StringBuilder key = new StringBuilder(prefix);
            // KEY_SPLIT_CHAR 为分割字符
            key.append(KEY_SPLIT_CHAR).append(module).append(KEY_SPLIT_CHAR).append(func);
            for (String arg : args) {
                key.append(KEY_SPLIT_CHAR).append(arg);
            }
            return key.toString();
        }
    
        /**
         * redis的key键规则定义
         * @param redisEnum 枚举对象
         * @param objStr 对象.toString()
         * @return key
         */
        public static String keyBuilder(RedisEnum redisEnum, String objStr) {
            return keyBuilder(redisEnum.getKeyPrefix(), redisEnum.getModule(), redisEnum.getFunc(), objStr);
        }
        
        public static void main(String[] args) throws Exception {
    		System.out.println(keyBuilder("com.cyb.test", "method", String.valueOf(111)));
    		System.out.println(keyBuilder("com.cyb.test", "method:value", String.valueOf(111)));
    	    System.out.println(keyBuilder(BaseController.class, "aaa"));
        }
    
    }
    
    
    package com.kiiik.config.redis.cache;
    
    /**
     *作者 : iechenyb<br>
     *类描述: 说点啥<br>
     *创建时间: 2018年11月22日
     */
    public enum RedisEnum {
    	/**
         * 数据字典Service - 根据字典类型查询字典数据
         */
    	RedisEnum(
                RedisUtils.KEY_PREFIX, "", "", "");
    
        /**
         * 系统标识
         */
        private String keyPrefix;
        /**
         * 模块名称
         */
        private String module;
        /**
         * 方法名称
         */
        private String func;
        /**
         * 描述
         */
        private String remark;
    
        RedisEnum(String keyPrefix, String module, String func, String remark) {
            this.keyPrefix = keyPrefix;
            this.module = module;
            this.func = func;
            this.remark = remark;
        }
    
    	public String getKeyPrefix() {
    		return keyPrefix;
    	}
    
    	public void setKeyPrefix(String keyPrefix) {
    		this.keyPrefix = keyPrefix;
    	}
    
    	public String getModule() {
    		return module;
    	}
    
    	public void setModule(String module) {
    		this.module = module;
    	}
    
    	public String getFunc() {
    		return func;
    	}
    
    	public void setFunc(String func) {
    		this.func = func;
    	}
    
    	public String getRemark() {
    		return remark;
    	}
    
    	public void setRemark(String remark) {
    		this.remark = remark;
    	}
        
    }
    

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值