据以前的情况,我们在Java中使用Redis时一般是使用Jedis来操作的,大致的一段代码如下所示
@Override public User findUserById(Integer id) { User user = null; Jedis jedis = null; try { jedis = jedisPool.getResource(); String userStr = jedis.get("user_" + id); // 尝试获取数据 if (userStr != null && !userStr.isEmpty()) { // 如果获取到有效数据,则转换后返回 user = JSONObject.parseObject(userStr, User.class); } else {// 如果没有获取到数据,则查询数据库返回 user = userMapper.findUserById(id); if (user != null) jedis.set("user_" + id, JSONObject.toJSONString(user)); // 设置到redis中 } } finally { // 记得关闭Jedis,因为这里使用的是JedisPool,所以这里的关闭并不是直接关闭连接,而是释放,以供其他的业务使用 if (jedis != null) jedis.close(); } return user; }
上边的这样的一段代码其实是有些臃肿的,但是如果我们引入RedisTemplate,其实会简化不少。
redis.clients jedis3.9.0 org.springframework.data spring-data-redis2.2.13.RELEASE
@Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); //设置key值的序列化方式,默认是JDK的形式 redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8); return redisTemplate; }
@Autowired private RedisTemplate redisTemplate; @Override public User findUserById(Integer id) { Object result = redisTemplate.opsForValue().get("user_" + id); if (result != null) return (User) result; User user = userMapper.findUserById(id); // 设置到redis中 if (user != null) redisTemplate.opsForValue().set("user_" + id, user); return user; }
看了以上的内容,可以看到引入了RedisTemplate其实已经很简洁了,但是明显还不够,下面我们将考虑引入 “注解”
@Configuration @EnableCaching public class AppConfig { ... }
@Override @Cacheable(value="user", key = "#id") // 这里返回的值会被存放到redis,key-value格式,其中生成的key值(假设id为1): user::1 public User findUserById(Integer id) { User user = userMapper.findUserById(id); return user; }
但是这样还不够,因为Spring并不清楚缓存的方式是什么,这就涉及到CacheManager 了
@Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); // 这里是redis的ip redisStandaloneConfiguration.setPort(port);// 这里是redis的端口 // 自适应集群变化 RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisCacheConfiguration redisCacheConfiguration() { RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json())); return redisCacheConfiguration; } @Bean public RedisCacheWriter redisCacheWriter(RedisConnectionFactory redisConnectionFactory) { RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory); return redisCacheWriter; } @Bean public CacheManager cacheManager(RedisCacheWriter redisCacheWriter, RedisCacheConfiguration redisCacheConfiguration) { CacheManager cacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration); ((RedisCacheManager) cacheManager).isTransactionAware(); return cacheManager; }
org.springframework spring-aop 5.3.22 org.springframework spring-aspects 5.3.22 com.fasterxml.jackson.core jackson-core 2.9.8 com.fasterxml.jackson.core jackson-databind 2.9.8 com.fasterxml.jackson.core jackson-annotations 2.9.8 com.alibaba fastjson 1.2.28
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomCache { /** * key的规则,可以使用springEL表达式,可以使用方法执行的一些参数 */ String key(); /** * 类似前缀 * @return */ String value(); }
@EnableAspectJAutoProxy // 开启AOP自动代理 public class AppConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(); redisStandaloneConfiguration.setHostName(host); redisStandaloneConfiguration.setPort(port); // 自适应集群变化 RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration); return redisConnectionFactory; } @Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory); redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8); redisTemplate.setValueSerializer(StringRedisSerializer.UTF_8); return redisTemplate; } }
@Component @Aspect public class CustomCacheAspect { @Autowired private RedisTemplate redisTemplate; @Pointcut("@annotation(cn.lazyfennec.cache.redis.annotation.CustomCache)") public void cachePointcut() { } @Around("cachePointcut()") public Object doCache(ProceedingJoinPoint joinPoint) { Object obj = null; try { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = joinPoint.getTarget().getClass().getMethod(signature.getName(), signature.getMethod().getParameterTypes()); CustomCache customCache = method.getAnnotation(CustomCache.class); String cacheKey = customCache.key(); String cacheValue = customCache.value(); // 创建解析器 ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(cacheKey); EvaluationContext context = new StandardEvaluationContext(); // 参数 // 添加参数 Object[] args = joinPoint.getArgs(); DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer(); String[] parameterNames = discover.getParameterNames(method); for (int i = 0; i < parameterNames.length; i++) { context.setVariable(parameterNames[i], args[i].toString()); } // 解析 String key = cacheValue + "::" + expression.getValue(context).toString(); // 1、 判定缓存中是否存在 obj = redisTemplate.opsForValue().get(key); if (obj != null) return obj; // 2、不存在则继续行方法 obj = joinPoint.proceed(); // 3、 同步存储value到缓存。 redisTemplate.opsForValue().set(key, obj); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Throwable throwable) { throwable.printStackTrace(); } return obj; } }
@RequestMapping("/custom/name/{id}") @ResponseBody public String getUserNameById(@PathVariable Integer id) { return userService.getUserNameById(id); }
@Override @CustomCache(value = "custom_user", key = "#id") public String getUserNameById(Integer id) { return userMapper.findUserNameById(id); }