ワークノート-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に関連する依存パケットを導入する
ステップ2.redis.xmlファイルで構成されています.ここではjedisConnectionFactory接続プールの使用を推奨します.
redisTemplateのkeySerializerとvalueSerializerは実際のニーズに合わせて選択できます.この例ではSpringRedisSerializer(文字列)とJ d k SerializationRedisSerializer(オブジェクト)を使用しています.形式のキー値対キャッシュに適用されます.特別なニーズがなければ十分です.J d k SerializationRedisSerializerは効率が高いからです.それ以外にJsonのシーケンス化フォーマットもサポートされています.
redisKeyGenerator:redisキャッシュの名前を統一したkey.メソッド名、パラメータに基づいて、一意のkeyに組み合わせることができます.
errorHandler:例外を一括処理します.
redisUtil:redisツールクラスで、キャッシュを手動で操作する必要がある場所を便利にします.
ステップ3.redisCacheConfigを構成し、redisCacheConfigクラスを作成する
実はこのクラスを書かなくてもいいですが、私がこのクラスを書いたのは主にRedisCacheManagerにパラメータを渡すことで、異なる表領域の異なるキャッシュ生存時間を指定するためです.この需要がなければ、このステップを直接スキップすることができます.
私の生存時間はRedisTableに定義されています
これで3ステップ後、構成は完了します.次にキャッシュが必要な場所にコメントを追加します
valueは我々の表領域であり,@Cacheableはメソッドが返すオブジェクトをシーケンス化してキャッシュに埋め込み,再びこのメソッドを呼び出すとキャッシュデータを直接読み出すことができるほか,@CacheEveict(キャッシュ淘汰)など他の注釈もあり,異なる機能を持つ.
これで統合が完了します.
私が使っているバージョンは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(キャッシュ淘汰)など他の注釈もあり,異なる機能を持つ.
これで統合が完了します.