1.引入RedisTemplate
据以前的情况,我们在Java中使用Redis时一般是使用Jedis来操作的,大致的一段代码如下所示
@Overridepublic 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进行管理。
@Beanpublic RedisConnectionFactory redisConnectionFactory() {RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();redisStandaloneConfiguration.setHostName(host);redisStandaloneConfiguration.setPort(port);RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);return redisConnectionFactory;}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);//设置key值的序列化方式,默认是JDK的形式redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);return redisTemplate;}
- 如果使用RedisTemplate的替换的话,会简洁很多。
@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic 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
@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的ipredisStandaloneConfiguration.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
@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;}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);redisTemplate.setValueSerializer(StringRedisSerializer.UTF_8);return redisTemplate;}
}
- 创建 CustomCacheAspect
@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);
}