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
java 复制代码
  
@Configuration  
@EnableCaching  
public class AppConfig {
...
} 
  • 引入@Cacheable,表示这个方法将会访问缓存,如果无法命中缓存的话,会将方法返回的值存入redis,假设有注解为 @Cacheable(value="user", key = "#id"),那么生成的key值为 user::{id},即如果id为1 那么生成的 key就是 user::1
java 复制代码
@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中加入以下内容
java 复制代码
@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相关的包
xml 复制代码
<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>
java 复制代码
@Target(ElementType.METHOD)  
@Retention(RetentionPolicy.RUNTIME)
public @interface  CustomCache {  
    /**
   * key的规则,可以使用springEL表达式,可以使用方法执行的一些参数
   */
    String key();

    /**
   *  类似前缀
   * @return
   */
    String value();
}
  • 修改AppConfig
java 复制代码
@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
java 复制代码
@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
java 复制代码
@RequestMapping("/custom/name/{id}")
@ResponseBody
public String getUserNameById(@PathVariable Integer id) {
    return userService.getUserNameById(id);
}
  • 实际实现方法 getUserNameById,使用方式
java 复制代码
@Override
@CustomCache(value = "custom_user", key = "#id")
public String getUserNameById(Integer id) {
return userMapper.findUserNameById(id);
}

相关推荐
Flittly1 小时前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
咖啡八杯18 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
用户3074596982072 天前
Redis 延时队列详解
redis
烤代码的吐司君2 天前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
Flittly2 天前
【AgentScope Java新手村系列】(14)人机交互
java·spring boot·spring
leeyi4 天前
Checkpoint 机制:Agent 怎么在断电后接着跑
redis·aigc·agent
云技纵横5 天前
一个 @Async 让循环依赖暴雷:Spring 代理的暗坑
redis
犯困蛋挞yy6 天前
用Claude快速解决Redis代码报错反复无解的问题
redis
唐青枫6 天前
Java Spring WebFlux 实战指南:用 Mono、Flux 和 WebClient 写响应式接口
java·spring
咖啡八杯8 天前
GoF设计模式——策略模式
java·后端·spring·设计模式