springBoot2.0+redis+fastJson+自定義註解實現方法上添加過時時間

springBoot2.0集成redis實例java

1、首先引入項目依賴的maven jar包,主要包括 spring-boot-starter-data-redis包,這個再springBoot2.0以前好像是 spring-boot-starter-redis 這個,還有redis

fastjson這個包,是用來序列化的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.44</version>-----主要版本
</dependency>
2、配置redis配置文件,以yml文件爲例
redis:
database: 0
# Redis服務器地址
host: 10.18.104.13
# Redis服務器鏈接端口
port: 6379
# Redis服務器鏈接密碼(默認爲空)
password:
# 鏈接池最大鏈接數(使用負值表示沒有限制)
timeout: 1000 # 鏈接超時時間(毫秒)
pool:
maxTotal: 8
# 鏈接池最大阻塞等待時間(使用負值表示沒有限制)
maxWaitMillis: -1
# 鏈接池中的最大空閒鏈接
maxIdle: 8
# 鏈接池中的最小空閒鏈接
testOnBorrow: true
第3、編寫 RedisConfig類,
 

import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.jcache.config.JCacheConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

import static org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig;

@Configuration
@Slf4j
public class RedisConfig extends CachingConfigurerSupport {
private final RedisConnectionFactory redisConnectionFactory;
RedisConfig(RedisConnectionFactory redisConnectionFactory) {
this.redisConnectionFactory = redisConnectionFactory;
}

@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// set key serializer
StringRedisSerializer serializer = TedisCacheManager.STRING_SERIALIZER;
// 設置key序列化類,不然key前面會多了一些亂碼
template.setKeySerializer(serializer);
template.setHashKeySerializer(serializer);

// fastjson serializer
GenericFastJsonRedisSerializer fastSerializer = TedisCacheManager.FASTJSON_SERIALIZER;
template.setValueSerializer(fastSerializer);
template.setHashValueSerializer(fastSerializer);
// 若是 KeySerializer 或者 ValueSerializer 沒有配置,則對應的 KeySerializer、ValueSerializer 才使用這個 Serializer
template.setDefaultSerializer(fastSerializer);

log.info("redis: {}", redisConnectionFactory);
LettuceConnectionFactory factory = (LettuceConnectionFactory) redisConnectionFactory;
log.info("spring.redis.database: {}", factory.getDatabase());
log.info("spring.redis.host: {}", factory.getHostName());
log.info("spring.redis.port: {}", factory.getPort());
log.info("spring.redis.timeout: {}", factory.getTimeout());
log.info("spring.redis.password: {}", factory.getPassword());

// factory
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}

@Bean
@Override
public KeyGenerator keyGenerator() {
return (o, method, objects) -> {
StringBuilder sb = new StringBuilder(32);
sb.append(o.getClass().getSimpleName());
sb.append(".");
sb.append(method.getName());
if (objects.length > 0) {
sb.append("#");
}
String sp = "";
for (Object object : objects) {
sb.append(sp);
if (object == null) {
sb.append("NULL");
} else {
sb.append(object.toString());
}
sp = ".";
}
return sb.toString();
};
}

/**
* 配置 RedisCacheManager,使用 cache 註解管理 redis 緩存
*/
@Bean
@Override
public CacheManager cacheManager() {
// 初始化一個RedisCacheWriter
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);

// 設置默認過時時間:30 分鐘
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
// .disableCachingNullValues()
// 使用註解時的序列化、反序列化
.serializeKeysWith(TedisCacheManager.STRING_PAIR)
.serializeValuesWith(TedisCacheManager.FASTJSON_PAIR);

// Map<String, RedisCacheConfiguration> caches = new HashMap<>();
// // 緩存配置
// RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// .entryTtl(Duration.ofSeconds(60))
// .disableCachingNullValues()
// // .prefixKeysWith("redis.service")
// .serializeKeysWith(stringPair)
// .serializeValuesWith(fastJsonPair);
// caches.put("redis.service", config);
// return new TedisCacheManager(cacheWriter, defaultCacheConfig, caches);

return new TedisCacheManager(cacheWriter, defaultCacheConfig);
}
第5、配置 TedisCacheManager類,實現對 RedisCacheManager管理
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cache.Cache;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ReflectionUtils;

import java.time.Duration;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;

/**
* Redis 容易出現緩存問題(超時、Redis 宕機等),當使用 spring cache 的註釋 Cacheable、Cacheput 等處理緩存問題時,
* 咱們沒法使用 try catch 處理出現的異常,因此最後致使結果是整個服務報錯沒法正常工做。
* 經過自定義 TedisCacheManager 並繼承 RedisCacheManager 來處理異常能夠解決這個問題。
*/
@Slf4j
public class TedisCacheManager extends RedisCacheManager implements ApplicationContextAware, InitializingBean {
private ApplicationContext applicationContext;

private Map<String, RedisCacheConfiguration> initialCacheConfiguration = new LinkedHashMap<>();

/**
* key serializer
*/
public static final StringRedisSerializer STRING_SERIALIZER = new StringRedisSerializer();

/**
* value serializer
* <pre>
* 使用 FastJsonRedisSerializer 會報錯:java.lang.ClassCastException
* FastJsonRedisSerializer<Object> fastSerializer = new FastJsonRedisSerializer<>(Object.class);
* </pre>
*/

public static final GenericFastJsonRedisSerializer FASTJSON_SERIALIZER = new GenericFastJsonRedisSerializer();

/**
* key serializer pair
*/
public static final RedisSerializationContext.SerializationPair<String> STRING_PAIR = RedisSerializationContext
.SerializationPair.fromSerializer(STRING_SERIALIZER);
/**
* value serializer pair
*/
public static final RedisSerializationContext.SerializationPair<Object> FASTJSON_PAIR = RedisSerializationContext
.SerializationPair.fromSerializer(FASTJSON_SERIALIZER);

public TedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
super(cacheWriter, defaultCacheConfiguration);
}

// public TedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration,
// Map<String, RedisCacheConfiguration> initialCacheConfigurations) {
// super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations);
// }

@Override
public Cache getCache(String name) {
Cache cache = super.getCache(name);
return new RedisCacheWrapper(cache);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}

@Override
public void afterPropertiesSet() {
String[] beanNames = applicationContext.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
final Class clazz = applicationContext.getType(beanName);
add(clazz);
}
super.afterPropertiesSet();
}

@Override
protected Collection<RedisCache> loadCaches() {
List<RedisCache> caches = new LinkedList<>();
for (Map.Entry<String, RedisCacheConfiguration> entry : initialCacheConfiguration.entrySet()) {
caches.add(super.createRedisCache(entry.getKey(), entry.getValue()));
}
return caches;
}

private void add(final Class clazz) {
ReflectionUtils.doWithMethods(clazz, method -> {
ReflectionUtils.makeAccessible(method);
CacheExpire cacheExpire = AnnotationUtils.findAnnotation(method, CacheExpire.class);
if (cacheExpire == null) {
return;
}
Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
if (cacheable != null) {
add(cacheable.cacheNames(), cacheExpire);
return;
}
Caching caching = AnnotationUtils.findAnnotation(method, Caching.class);
if (caching != null) {
Cacheable[] cs = caching.cacheable();
if (cs.length > 0) {
for (Cacheable c : cs) {
if (cacheExpire != null && c != null) {
add(c.cacheNames(), cacheExpire);
}
}
}
} else {
CacheConfig cacheConfig = AnnotationUtils.findAnnotation(clazz, CacheConfig.class);
if (cacheConfig != null) {
add(cacheConfig.cacheNames(), cacheExpire);
}
}
}, method -> null != AnnotationUtils.findAnnotation(method, CacheExpire.class));
}

private void add(String[] cacheNames, CacheExpire cacheExpire) {
for (String cacheName : cacheNames) {
if (cacheName == null || "".equals(cacheName.trim())) {
continue;
}
long expire = cacheExpire.expire();
log.info("cacheName: {}, expire: {}", cacheName, expire);
if (expire >= 0) {
// 緩存配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(expire))
.disableCachingNullValues()
// .prefixKeysWith(cacheName)
.serializeKeysWith(STRING_PAIR)
.serializeValuesWith(FASTJSON_PAIR);
initialCacheConfiguration.put(cacheName, config);
} else {
log.warn("{} use default expiration.", cacheName);
}
}
}

protected static class RedisCacheWrapper implements Cache {
private final Cache cache;

RedisCacheWrapper(Cache cache) {
this.cache = cache;
}

@Override
public String getName() {
// log.info("name: {}", cache.getName());
try {
return cache.getName();
} catch (Exception e) {
log.error("getName ---> errmsg: {}", e.getMessage(), e);
return null;
}
}

@Override
public Object getNativeCache() {
// log.info("nativeCache: {}", cache.getNativeCache());
try {
return cache.getNativeCache();
} catch (Exception e) {
log.error("getNativeCache ---> errmsg: {}", e.getMessage(), e);
return null;
}
}

@Override
public ValueWrapper get(Object o) {
// log.info("get ---> o: {}", o);
try {
return cache.get(o);
} catch (Exception e) {
log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
return null;
}
}

@Override
public <T> T get(Object o, Class<T> aClass) {
// log.info("get ---> o: {}, clazz: {}", o, aClass);
try {
return cache.get(o, aClass);
} catch (Exception e) {
log.error("get ---> o: {}, clazz: {}, errmsg: {}", o, aClass, e.getMessage(), e);
return null;
}
}

@Override
public <T> T get(Object o, Callable<T> callable) {
// log.info("get ---> o: {}", o);
try {
return cache.get(o, callable);
} catch (Exception e) {
log.error("get ---> o: {}, errmsg: {}", o, e.getMessage(), e);
return null;
}
}

@Override
public void put(Object o, Object o1) {
// log.info("put ---> o: {}, o1: {}", o, o1);
try {
cache.put(o, o1);
} catch (Exception e) {
log.error("put ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
}
}

@Override
public ValueWrapper putIfAbsent(Object o, Object o1) {
// log.info("putIfAbsent ---> o: {}, o1: {}", o, o1);
try {
return cache.putIfAbsent(o, o1);
} catch (Exception e) {
log.error("putIfAbsent ---> o: {}, o1: {}, errmsg: {}", o, o1, e.getMessage(), e);
return null;
}
}

@Override
public void evict(Object o) {
// log.info("evict ---> o: {}", o);
try {
cache.evict(o);
} catch (Exception e) {
log.error("evict ---> o: {}, errmsg: {}", o, e.getMessage(), e);
}
}

@Override
public void clear() {
// log.info("clear");
try {
cache.clear();
} catch (Exception e) {
log.error("clear ---> errmsg: {}", e.getMessage(), e);
}
}
}
6、自定義註解,能夠在緩存方法上添加過時時間。

import org.springframework.core.annotation.AliasFor;

import java.lang.annotation.*;

/**
* @author wjw
* 2018/7/12
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface CacheExpire {
/**
* expire time, default 60s
*/
@AliasFor("expire")
long value() default 60L;

/**
* expire time, default 60s
*/
@AliasFor("value")
long expire() default 60L;

}
7、案例使用:

/**
* 利用jdbcTemplate進行查詢
* @param userID
* @return
*/

@Cacheable(value = "user",key = "'user_'.concat(#root.args[0])",sync=true)
@CacheExpire(expire = 60)
public List<Map<String,Object>> getUser(Long userID){
StringBuilder sql = new StringBuilder("");
System.out.print(122);
sql.append("select * from tbl_sys_user t where t.user_id = ? ");
return jdbcTemplate.queryForList(sql.toString(),userID);
}
八,最後在啓動入口類上添加註解
@EnableCaching
相關文章
相關標籤/搜索