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);
}

相关推荐
编程、小哥哥4 小时前
netty之Netty与SpringBoot整合
java·spring boot·spring
BergerLee8 小时前
对不经常变动的数据集合添加Redis缓存
数据库·redis·缓存
huapiaoy8 小时前
Redis中数据类型的使用(hash和list)
redis·算法·哈希算法
【D'accumulation】9 小时前
令牌主动失效机制范例(利用redis)注释分析
java·spring boot·redis·后端
Cikiss9 小时前
微服务实战——SpringCache 整合 Redis
java·redis·后端·微服务
wxin_VXbishe9 小时前
springboot合肥师范学院实习实训管理系统-计算机毕业设计源码31290
java·spring boot·python·spring·servlet·django·php
OEC小胖胖10 小时前
Spring Boot + MyBatis 项目中常用注解详解(万字长篇解读)
java·spring boot·后端·spring·mybatis·web
一休哥助手10 小时前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
落落落sss10 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
盒马盒马11 小时前
Redis:zset类型
数据库·redis