你每次用Redis做缓存的时候是不是都要写这么一大堆重复的代码?
String result = jedis.get(key);
if(StringUtils.isEmpty(result)){
// 查询数据库,并将结果存入Redis
}else {
return result;
}
接下来看我怎么解决这个问题,去除重复的代码,以后每次用缓存的时候只需要添加一个注解就ok了
redis:
# host: 127.0.0.1
# port: 6379
# timeout: 60000
jedis:
pool.max-active: 8
cluster:
nodes: 192.168.174.128:7000,192.168.174.128:7001,192.168.174.128:7002,192.168.174.128:7003,192.168.174.128:7004,192.168.174.128:7005
@Configuration
public class RedisConfig {
@Value("${spring.redis.cluster.nodes}")
private String nodes;
/*单机模式*/
@Bean
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
// key序列化
RedisSerializer<?> stringSerializer = new StringRedisSerializer();
// value序列化
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置redisTemplate
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
redisTemplate.setKeySerializer(stringSerializer);// key序列化
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/*集群模式*/
@Bean
public JedisCluster jedisCluster() {
Set<HostAndPort> setNodes = new HashSet<>();
String[] arrayNode = nodes.split(",");
//node{IP:PORT}
for (String node : arrayNode) {
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
setNodes.add(new HostAndPort(host, port));
}
return new JedisCluster(setNodes);
}
}
/**
* 该注解主要实现查询操作.
* 有缓存查询缓存,没缓存查询数据库
* @author zhushanglin
*
* 操作规范:
* key:
* 1.用户没有赋值
* 如果key为"",表示用户使用自动生成的key
* key:包名.类名.方法名.拼接第一个参数
* 2.如果用户赋值
* key:使用用户的数据
* seconds:
* 如果时间不为0,表示用户需要设定超时时间
*/
@Target({ElementType.METHOD}) //对方法生效
@Retention(RetentionPolicy.RUNTIME)
public @interface Cache_Find {
String key() default "";
int seconds() default 0;
}
@Aspect //标识切面
@Component //交给spring容器管理
public class CacheAspect {
//required = false 当用户使用时才注入
@Autowired(required = false)
private JedisCluster jedis; //注入redis集群对象
/**
* 利用AOP规则:动态获取注解对象
* 步骤:
* 1.根据key查询redis.
* 2.没有数据,需要让目标方法执行.查询的结果保存redis
* 3.将json串转化为返回值对象 返回.
* @param joinPoint
* @param cacheFind
* @return
*/
@SuppressWarnings("unchecked")
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,
Cache_Find cacheFind) {
Object data = null;
String key = getKey(joinPoint,cacheFind);
//1.从redis中获取数据
String result = jedis.get(key);
//2.判断缓存中是否有数据
try {
if(StringUtils.isEmpty(result)) {
//2.1缓存中没有数据
data = joinPoint.proceed();//目标方法执行
//2.2将返回值结果,转化为JSON
String json = ObjectMapperUtil.toJSON(data);
if(StringUtils.isEmpty(json)){
// 把空的数据也缓存起来,并设置过期时间,预防缓存穿透!
jedis.setex(key,60, json);
}else {
//2.3判断用户是否编辑时间
//如果有时间,必须设定超时时间.
if(cacheFind.seconds()>0) {
int seconds = cacheFind.seconds();
jedis.setex(key,seconds, json);
}else {
jedis.set(key,json);
}
}
System.out.println("AOP查询数据库!!!!!");
}else {
//表示缓存数据不为空,将缓存数据转化为对象
Class returnClass = getReturnClass(joinPoint);
data = ObjectMapperUtil.toObject(result,returnClass);
System.out.println("AOP查询缓存!!!!");
}
} catch (Throwable e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return data;
}
/**
* 获取目标方法的返回值类型
* @param joinPoint
* @return
*/
private Class getReturnClass(ProceedingJoinPoint joinPoint) {
MethodSignature signature =
(MethodSignature) joinPoint.getSignature();
return signature.getReturnType();
}
/**
* 动态获取key
* @param joinPoint
* @param cacheFind
* @return
*/
private String getKey(ProceedingJoinPoint joinPoint, Cache_Find cacheFind) {
String key = cacheFind.key();
if(StringUtils.isEmpty(key)) {
//key自动生成
String className =
joinPoint.getSignature().getDeclaringTypeName();
String methodName =
joinPoint.getSignature().getName();
if(joinPoint.getArgs().length>0)
//拼接第一个参数
key = className+"."+methodName+"::"
+ joinPoint.getArgs()[0];
else
key = className+"."+methodName;
}
return key;
}
}
上面使用到的ObjectMapperUtil工具类是:
import java.io.IOException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 作用: 实现对象与JSON串之间的转化
* @author zhushanglin
*
*/
public class ObjectMapperUtil {
//常量对象 可以调用对象的方法 线程安全 不安全???
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将检查异常转化为运行时异常.
* @param data
* @return
*/
public static String toJSON(Object data) {
String json = null;
try {
json = MAPPER.writeValueAsString(data);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return json;
}
/**
* 根据JSON转化为对象
* 参数:json数据,Class
* 返回值:由用户决定.
*/
@SuppressWarnings("unchecked")
public static <T> T toObject(String json,Class<T> target) {
T obj = null;
try {
obj = MAPPER.readValue(json, target);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return obj;
}
}
在需要使用Redis缓存的方法上 使用@Cache_Find注解即可,这样再也不用每次都写文章最开始的那个if else 了,是不是很实用,有用的话帮忙点个赞哦~
@Override
@Cache_Find//实现缓存处理
public ItemDesc findItemDescById(Long id) {
String url = "http://manage.jt.com/web/item/findItemDescById/"+id;
String itemDescJSON = httpClient.doGet(url);
return ObjectMapperUtil.toObject(itemDescJSON, ItemDesc.class);
}
分布式存取
@Autowired
private JedisCluster jedisCluster;
jedisCluster.setex("cookie", 20*60, cookie);
String cookie = jedisCluster.get("cookie");
// 其他对象类型,参考CacheAspect 中的用法