ワークノート-spring-data-redis注釈ベースのredisキャッシュを実現

13499 ワード

Spring-data-redisは注釈でredisキャッシュを統合することをサポートし、2.xバージョン以降はカスタム例外処理をサポートし、Spring 5を要求する.0.7以上、jedis 2.9以上jdk 1.8.
私が使っているバージョンはSpringバージョン5.0.8、jedisバージョン2.9.0、spring-data-redisバージョン2.0.9です.
第一歩.まずPOMに関連する依存パケットを導入する

    redis.clients
    jedis
    2.9.0


    org.springframework.data
	spring-data-redis
	2.0.9.RELEASE


	org.springframework.data
	spring-data-keyvalue
	2.0.9.RELEASE

ステップ2.redis.xmlファイルで構成されています.ここではjedisConnectionFactory接続プールの使用を推奨します.


	
	
		
		  
               
          
          
               
          
	
		
	
         
        
        
	

	
		
		
		
		
		
		
		
		
		
		
		
		
		
		
	
	

     
    
     

	
	
	

redisTemplateのkeySerializerとvalueSerializerは実際のニーズに合わせて選択できます.この例ではSpringRedisSerializer(文字列)とJ d k SerializationRedisSerializer(オブジェクト)を使用しています.形式のキー値対キャッシュに適用されます.特別なニーズがなければ十分です.J d k SerializationRedisSerializerは効率が高いからです.それ以外にJsonのシーケンス化フォーマットもサポートされています.
redisKeyGenerator:redisキャッシュの名前を統一したkey.メソッド名、パラメータに基づいて、一意のkeyに組み合わせることができます.
public class RedisKeyGenerator implements KeyGenerator {
    private static final Logger logger = LoggerFactory.getLogger(RedisKeyGenerator.class);


	private static String NO_PARAM_KEY = "no_params";
	@Override
	public Object generate(Object target, Method method, Object... params) {

		String sp = ":";
		StringBuilder strBuilder = new StringBuilder(30);
        //   
/*      strBuilder.append(target.getClass().getSimpleName());
        strBuilder.append(sp);*/
        //    
        strBuilder.append(method.getName());
        strBuilder.append(sp);
        if (params.length > 0) {
            int i =1;
            //    
            for (Object object : params) {
                if (isSimpleValueType(object.getClass())) {
                    strBuilder.append(object);
                    if(i++ < params.length) {
                        strBuilder.append(sp);
                    }
                } else {
                    strBuilder.append(JSONObject.fromObject(object).toString());
                    if(i++ < params.length) {
                        strBuilder.append(sp);
                    }
                }
            }
        } else {
            strBuilder.append(NO_PARAM_KEY);
        }
        logger.info("=============newKey:{}",strBuilder.toString());
        return strBuilder.toString();
     
    }
	
	public static boolean isSimpleValueType(Class> clazz) {
		return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
				CharSequence.class.isAssignableFrom(clazz) ||
				Number.class.isAssignableFrom(clazz) ||
				Date.class.isAssignableFrom(clazz) ||
				URI.class == clazz || URL.class == clazz ||
				Locale.class == clazz || Class.class == clazz);
	}

}

errorHandler:例外を一括処理します.
public class RedisCacheErrorHandler extends SimpleCacheErrorHandler {
    private static final Logger logger = LoggerFactory.getLogger(RedisCacheErrorHandler.class);

    private static final Logger fatallogger = LoggerFactory.getLogger("fatal");

    private static final int NUM = 3;

//	private final Cache delegate;

/*	public RedisCacheErrorHandler(Cache redisCache) {
		this.delegate = redisCache;
	}*/
	
	@Override
	public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
//		logger.info("get  ");
		logger.error("cache get error, cacheName:{}, key:{}, msg:", cache.getName(), key, exception);
//		throw exception;
	}

	@Override
	public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
//		logger.info("put  ");
		logger.error("cache put error, cacheName:{}, key:{}, msg:", cache.getName(), key, exception);
//		throw exception;
	}

	//    redis     ,      ,      ,    NUM ,    fatal  
	@Override
	public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
		boolean checkEvit = false;
		for(int i=1; i <= NUM; i++) {
			try{
			//	delegate.evict(key);
				cache.evict(key);
				checkEvit = true;
			}catch (RuntimeException e) {
				fatallogger.error("cache evict error:{}, cacheName:{}, key:{}, msg:", i, cache.getName(), key, exception);
			}
			if(checkEvit) {
				break;
			}
		}
	}

	@Override
	public void handleCacheClearError(RuntimeException exception, Cache cache) {
		logger.error("cache clear error, cacheName:{}, msg:", cache.getName(), exception);
	}
}

redisUtil:redisツールクラスで、キャッシュを手動で操作する必要がある場所を便利にします.
public final class RedisUtil {
    @Resource
    private RedisTemplate redisTemplate;  
  
    /** 
     *        value 
     *  
     * @param keys 
     */  
    public void remove(final String... keys) {  
        for (String key : keys) {  
            remove(key);  
        }  
    }  
  
    /** 
     *     key 
     *  
     * @param pattern 
     */  
    public void removePattern(final String pattern) {  
        Set keys = redisTemplate.keys(pattern);  
        if (keys.size() > 0)  
            redisTemplate.delete(keys);  
    }  
  
    /** 
     *      value 
     *  
     * @param key 
     */  
    public void remove(final String key) {  
        if (exists(key)) {  
            redisTemplate.delete(key);  
        }  
    }  
  
    /** 
     *            value 
     *  
     * @param key 
     * @return 
     */  
    public boolean exists(final String key) {  
        return redisTemplate.hasKey(key);  
    }  
  
    /** 
     *      
     *  
     * @param key 
     * @return 
     */  
    public Object get(final String key) { 
    	log.info("redis    ...key【{}】",key);
        Object result = null;  
        ValueOperations operations = redisTemplate  
                .opsForValue();  
        result = operations.get(key);  
        redisTemplate.expire(key, 3600, TimeUnit.SECONDS);//      
        return result;  
    }  
  
    /** 
     *      
     *  
     * @param key 
     * @param value 
     * @return 
     */  
    public boolean set(final String key, Object value) {  
        boolean result = false;  
        try {  
        	log.info("redis    ...key【{}】value【{}】expireTime【{}】",key,value);
            ValueOperations operations = redisTemplate  
                    .opsForValue();  
            operations.set(key, value);  
            result = true;  
        } catch (Exception e) {  
            log.error("redis      ",e);
        }  
        return result;  
    }  
  
    /** 
     *      
     *  
     * @param key 
     * @param value 
     * @return 
     */  
    public boolean set(final String key, Object value, Long expireTime) {  
        boolean result = false;  
        try { 
        	log.info("redis    ...key【{}】value【{}】expireTime【{}】",key,value,String.valueOf(expireTime));
            ValueOperations operations = redisTemplate  
                    .opsForValue();  
            operations.set(key, value);  
            redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);  
            result = true;  
        } catch (Exception e) {  
            log.error("redis      ",e);
        }  
        return result;  
    }  
  
    public void setRedisTemplate(  
            RedisTemplate redisTemplate) {  
        this.redisTemplate = redisTemplate;  
    }  
}  

ステップ3.redisCacheConfigを構成し、redisCacheConfigクラスを作成する
@Configuration
public class RedisCacheConfig {

	@Bean
    public KeyGenerator simpleKeyGenerator() {
        return new RedisKeyGenerator();
    }

    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
    	
    	Map initializeConfigs = initConfig();
        return new RedisCacheManager(
        		RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory),        	
        		RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(300)),
            initializeConfigs
        );
    }
    
    private static Map initConfig(){
    	Map configurations = new HashMap();
    	for(RedisTable rt : RedisTable.values()){
    		RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(rt.getExpireTime()));
    		configurations.put(rt.getOrdinal(), config);
    	}
    	return configurations;
    }
    

実はこのクラスを書かなくてもいいですが、私がこのクラスを書いたのは主にRedisCacheManagerにパラメータを渡すことで、異なる表領域の異なるキャッシュ生存時間を指定するためです.この需要がなければ、このステップを直接スキップすることができます.
私の生存時間はRedisTableに定義されています
public enum RedisTable {
	T_DEVICE("device","   ",(long)3000),
	T_TRMNL("trmnl","   ",(long)3000),
	T_MERCHANT("merchant","   ",(long)3000);

	/**    **/
	private String ordinal;
	/**    **/
	private String chineseName;
	/**      (s)**/
	private Long expireTime;
	private RedisTable(String idx,String chineseName,Long expireTime) {
		this.ordinal = idx;
		this.chineseName = chineseName;
		this.expireTime = expireTime;
	}
	public String getOrdinal() {
		return ordinal;
	}
	@Override
	public String toString() {
		return super.toString().toLowerCase();
	}
	public String getChineseName() {
		return chineseName;
	}
	public Long getExpireTime() {
		return expireTime;
	}
	private static final Map cacheTableMapping = new HashMap();
	static{
		for(RedisTable it :RedisTable.values()){
			cacheTableMapping.put(it.ordinal,it);
		}
	}
	public static final RedisTable fromCacheTableName(String name){
		return cacheTableMapping.get(name);
	}
	
	public static Map getMap(){
		return cacheTableMapping;
	}
}

これで3ステップ後、構成は完了します.次にキャッシュが必要な場所にコメントを追加します
	@Cacheable(value="device")
	public Device findDeviceById(String id) {
		return this.getDeviceDao().findById(id);
	}

valueは我々の表領域であり,@Cacheableはメソッドが返すオブジェクトをシーケンス化してキャッシュに埋め込み,再びこのメソッドを呼び出すとキャッシュデータを直接読み出すことができるほか,@CacheEveict(キャッシュ淘汰)など他の注釈もあり,異なる機能を持つ.
これで統合が完了します.