本文主要记录对SpringBoot集成redis缓存功能的学习总结。采用扩展Spring的缓存功能的方式。
一、Spring缓存注解
(1)@Cacheable
主要针对方法配置,能能够根据方法的请求参数对其结果进行缓存。
主要参数:
1)value:缓存的名称,必须至少指定一个。例如@Cacheable(value="cache")、@Cacheable(value={"cache1","cache2"})
2)key:缓存的key,需要按照spel表达式进行指定,当然也可以不指定,那么就按照缺省的所有参数进行组合。例如@Cacheable(value="cache",key="#userName")
3)condition:缓存的条件,可以为空,使用SPEL编写,返回布尔值,只有为true的时候才进行缓存。例如:@Cacheable(value="cache",key="#userName",condition="#userName.length>5")
(2)@CachePut
主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。与@Cacheable不同的是,它每次都会处罚真实的方法调用。
主要参数:
1)value:缓存的名称,至少应该指定一个。例如:@Cacheable(value="cache")、@Cacheable(value={"cache1","cache2"})
2)key:缓存的key,需要按照spel表达式进行指定,当然也可以不指定,那么就按照缺省的所有参数进行组合。例如@Cacheable(value="cache",key="#userName")
3)condition:缓存的条件,可以为空,使用SPEL编写,返回布尔值,只有为true的时候才进行缓存。例如:@Cacheable(value="cache",key="#userName",condition="#userName.length>5")
(3)@CacheEvict
主要针对方法配置,能够根据一定的条件对缓存进行清空。
主要参数
1)value:缓存的名称,至少应该指定一个。例如:@Cacheable(value="cache")、@Cacheable(value={"cache1","cache2"})
2)key:缓存的key,需要按照spel表达式进行指定,当然也可以不指定,那么就按照缺省的所有参数进行组合。例如@Cacheable(value="cache",key="#userName")
3)condition:缓存的条件,可以为空,使用SPEL编写,返回布尔值,只有为true的时候才进行缓存。例如:@Cacheable(value="cache",key="#userName",condition="#userName.length>5")
4)allEntries:是否清空所有缓存的内容,默认为false,如果为true,调用该方法后将清空所有的redis缓存。
5)beforeInvocation:是否在方法执行前就清空,缺省为false,如果指定为true,则在方法还没有执行前就会清空缓存。当然如果抛出异常,则不会清空缓存。
二、具体实现过程
注意:本文的示例工程是基于已经集成好了jpa的前提进行搭建的。
(1)需要添加的maven依赖
<!--引入缓存-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!--支持redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
(2)配置redis缓存配置文件
package com.liutao.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.lang.reflect.Method;
/**
* redis缓存配置,采用继承的方式来改变Spring的缓存策略。
*
* 注意:这里可以不用继承CachingConfigurerSupport,也就是一个普通的RedisCacheConfig亦可以。
* 如果要从新实现key的生成策略,只要这里修改KeyGenerator就可以,其他地方不用修改。
* 如果使用普通类,那么在使用@Cacheable的时候需要指定KeyGenarator的名字。
* @author LIUTAO
* @version 2017/5/15
* @see
*/
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {
/**
* 缓存管理器
* @param redisTemplate
* @return
*/
@Bean
public CacheManager cacheManager(RedisTemplate<?,?> redisTemplate){
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//设置默认过期时间
cacheManager.setDefaultExpiration(60);
return cacheManager;
}
/**
* redis模板操作类
* 虽然CacheManager也能获取到Cache对象,但是操作起来不灵活
*
* @param factory
* @return
*/
@Bean
public RedisTemplate<String,String> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String,String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(redisSerializer);
redisTemplate.setHashKeySerializer(redisSerializer);
return redisTemplate;
}
/**
* 自定义Key
* 此方法会根据类名+方法名+所有参数的值生成一个唯一的key,即使@Cacheable中的value一样,key也会不一样。
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
System.out.println("RedisCacheConfig.keyGenerator()");
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
for (Object obj : objects) {
sb.append(obj.toString());
}
System.out.println("keyGenerator=" + sb.toString());
return sb.toString();
}
};
}
}
(3)缓存测试方法
package com.liutao.service.impl;
import com.liutao.dao.UserDao;
import com.liutao.entity.User;
import com.liutao.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 用户服务层
*
* @author LIUTAO
* @version 2017/3/29
* @see
* @since
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
private Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
/**
* 执行缓存
* @param id
* @return
*/
@Cacheable(value="user",keyGenerator = "keyGenerator") //缓存,这里没有指定Key
@Override
public User findById(Integer id) {
logger.debug("the data coming from database is :"+id);
return userDao.findOne(id);
}
/**
* 清空缓存
* @param id
*/
@CacheEvict(value="user",allEntries = true)
@Override
public void deleteFromCache(Integer id) {
logger.debug("delete from cache");
}
}
(4)缓存测试Controller
package com.liutao.controller;
import com.liutao.entity.User;
import com.liutao.service.UserService;
import com.wordnik.swagger.annotations.Api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
/**
* 用户控制层
*
* @author LIUTAO
* @version 2017/3/29
* @see
* @since
*/
@Controller
@Api(value = "test")
@RequestMapping("/liutao/v1")
public class UserController {
private Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userServiceImpl;
@GetMapping("/user")
public @ResponseBody String test(){
User userOfload = userServiceImpl.findById(1);
logger.debug("userOfload:"+userOfload);
User userOfCached = userServiceImpl.findById(1);
logger.debug("userOfCached:"+userOfCached);
userOfload = userServiceImpl.findById(2);
logger.debug("userOfload2:"+userOfload);
return "ok";
}
@DeleteMapping("/userOfCache")
public @ResponseBody String delete(Integer id){
userServiceImpl.deleteFromCache(id);
return "ok";
}
@GetMapping("user2")
public @ResponseBody String testTwo(){
logger.debug("UserController.testTwo()");
return "ok";
}
}
(5)测试结果
首次调用如下接口:
http://localhost:8080/liutao/v1/user
运行日志:
keyGenerator=com.liutao.service.impl.UserServiceImplfindById1
keyGenerator=com.liutao.service.impl.UserServiceImplfindById1
2017-05-15 15:16:42.178 [http-nio-8080-exec-7] DEBUG com.liutao.service.impl.UserServiceImpl - the data coming from database is :1
2017-05-15 15:16:42.190 [http-nio-8080-exec-7] DEBUG com.liutao.controller.UserController - userOfload:User{name='张三丰', age=45, password='zsf123', id=1}
keyGenerator=com.liutao.service.impl.UserServiceImplfindById1
2017-05-15 15:16:42.191 [http-nio-8080-exec-7] DEBUG com.liutao.controller.UserController - userOfCached:User{name='张三丰', age=45, password='zsf123', id=1}
keyGenerator=com.liutao.service.impl.UserServiceImplfindById2
keyGenerator=com.liutao.service.impl.UserServiceImplfindById2
2017-05-15 15:16:42.192 [http-nio-8080-exec-7] DEBUG com.liutao.service.impl.UserServiceImpl - the data coming from database is :2
2017-05-15 15:16:42.195 [http-nio-8080-exec-7] DEBUG com.liutao.controller.UserController - userOfload2:User{name='guanyuqq', age=54, password='gy123', id=2}
再次调用以上接口,运行日志:
keyGenerator=com.liutao.service.impl.UserServiceImplfindById1
2017-05-15 15:18:05.072 [http-nio-8080-exec-10] DEBUG com.liutao.controller.UserController - userOfload:User{name='张三丰', age=45, password='zsf123', id=1}
keyGenerator=com.liutao.service.impl.UserServiceImplfindById1
2017-05-15 15:18:05.073 [http-nio-8080-exec-10] DEBUG com.liutao.controller.UserController - userOfCached:User{name='张三丰', age=45, password='zsf123', id=1}
keyGenerator=com.liutao.service.impl.UserServiceImplfindById2
2017-05-15 15:18:05.073 [http-nio-8080-exec-10] DEBUG com.liutao.controller.UserController - userOfload2:User{name='guanyuqq', age=54, password='gy123', id=2}
根据以上的对比,可以看见,在第二次的调用中并没有进行Mysql数据库进行查询。但是当我们超过缓存时间(60)去从新调用以上接口的时候会发现从新进入数据库进行了查询,如果我们调用以下接口,就是清除了缓存。
http://localhost:8080/liutao/v1/userOfCache?id=1
详细代码请参考gitHub地址:SpringBoot--redis缓存1