Spring与Redis集成的正确方式流程详解
作者:右耳菌
这篇文章主要为大家介绍了Spring与Redis集成的正确方式流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
1. 引入RedisTemplate
据以前的情况,我们在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,其实会简化不少。
- maven 引入 spring-data-redis
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.9.0</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.2.13.RELEASE</version> </dependency>
- 将RedisTemplate 加入Bean容器中,让Spring进行管理。
@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; }
- 如果使用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的方法
看了以上的内容,可以看到引入了RedisTemplate其实已经很简洁了,但是明显还不够,下面我们将考虑引入 “注解”
2. 引入注解
- 开启缓存 @EnableCaching
AppConfig.java
@Configuration @EnableCaching public class AppConfig { ... }
- 引入@Cacheable,表示这个方法将会访问缓存,如果无法命中缓存的话,会将方法返回的值存入redis,假设有注解为
@Cacheable(value="user", key = "#id")
,那么生成的key值为 user::{id},即如果id为1 那么生成的 key就是 user::1
@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
了
- 设置CacheManager,在AppConfig中加入以下内容
@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; }
3. 扩展 - 自行通过注解和AOP实现缓存
- 引入AOP相关的包
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.22</version> </dependency> <!-- Jackson JSON Processor --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.28</version> </dependency>
- 创建@CustomCache
package cn.lazyfennec.cache.redis.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CustomCache { /** * key的规则,可以使用springEL表达式,可以使用方法执行的一些参数 */ String key(); /** * 类似前缀 * @return */ String value(); }
- 修改AppConfig
@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; } }
- 创建 CustomCacheAspect
package cn.lazyfennec.cache.redis.annotation.aop; import cn.lazyfennec.cache.redis.annotation.CustomCache; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import java.lang.reflect.Method; /** * @Author: Neco * @Description: * @Date: create in 2022/8/24 22:18 */ @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; } }
- 新建方法 getUserNameById
@RequestMapping("/custom/name/{id}") @ResponseBody public String getUserNameById(@PathVariable Integer id) { return userService.getUserNameById(id); }
- 实际实现方法 getUserNameById,使用方式
@Override @CustomCache(value = "custom_user", key = "#id") public String getUserNameById(Integer id) { return userMapper.findUserNameById(id); }
以上就是Spring与Redis集成的正确方式详解的详细内容,更多关于Spring Redis集成方式的资料请关注脚本之家其它相关文章!