1.springboot -redis 依赖
在pom.xml中添加
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.properties 配置文件
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# Redis数据库索引(默认为0)
spring.redis.database=0
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=10
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=500
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒),如果为0 会报连接超时错误
spring.redis.timeout=3000ms
3 redis缓存配置文件
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* @see RedisConfig
* @since 1.0
*/
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
// @Value("${spring.redis.host}")
// private String host;
// @Value("${spring.redis.port}")
// private int port;
// @Value("${spring.redis.timeout}")
// private Duration timeout;
// @Value("${spring.redis.password}")
// private String password;
// @Value("${spring.redis.jedis.pool.max-active}")
// private int maxActive;
// @Value("${spring.redis.jedis.pool.max-wait}")
// private Duration maxWait;
// @Value("${spring.redis.jedis.pool.max-idle}")
// private int maxIdle;
// @Value("${spring.redis.jedis.pool.min-idle}")
// private int minIdle;
@Bean
public RedisSerializer<Object> serializer() {
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer jacksonSerial = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jacksonSerial.setObjectMapper(om);
return jacksonSerial;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(@Autowired RedisConnectionFactory factory,
@Autowired RedisSerializer serializer
) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(factory);
// 值采用json序列化
template.setValueSerializer(serializer);
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
// 设置hash key 和value序列化模式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(@Autowired RedisConnectionFactory redisConnectionFactory,
@Autowired RedisSerializer serializer
) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
config = config.entryTtl(Duration.ofMinutes(5))// 设置缓存的默认过期时间,也是使用Duration设置
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为json序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
.disableCachingNullValues(); // 不缓存空值
// 设置一个初始化的缓存空间set集合
// Set<String> cacheNames = new HashSet<>();
// cacheNames.add("timeGroup");
// cacheNames.add("user");
// // 对每个缓存空间应用不同的配置
// Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
// configMap.put("timeGroup", config);
// configMap.put("user", config.entryTtl(Duration.ofSeconds(120)));
RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory)// 使用自定义的缓存配置初始化一个cacheManager
// .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
.cacheDefaults(config)
.transactionAware()
.build();
return cacheManager;
}
}
4 spring提供的@Cacheable、@CachePut、@CacheEvict 注释
@Cacheable 作用和配置方法
@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存(缓存的是方法的返回结果)
@Cacheable 主要的参数
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | 例如: |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
注意:这里key不写则key的取值默认为参数的组合,且key必须为String类型
缺省key参数:
1.@Cacheable注解的方法参数不为String,则
@Cacheable(value = "role")
public RolePojo getRole(int id){
return roleDao.getRole(id);
}
这里key为id(int)报错,java.lang.Integer cannot be cast to java.lang.String
2.@Cacheable注解的方法参数一个且为String,则不报错
RoleService.Java
package com.fcc.service;
import com.fcc.dao.RoleDao;
import com.fcc.dao.UserDao;
import com.fcc.model.RolePojo;
import com.fcc.model.UserPojo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
/**
* @Author:FeiCongcong
* @Date:2017/6/29 0029 14:21
*/
@Service
public class RoleService {
@Autowired
private RoleDao roleDao;
@Cacheable(value = "role")
public RolePojo getRoleByStr(String str){
return roleDao.getRoleByStr(str);
}
}
RoleController.java
@RequestMapping("/getRoleByStr")
@ResponseBody
public RolePojo getRoleByStr() {
return roleService.getRoleByStr("admin");
}



redis中会存value~keys 这种格式的key,该key下存着以方法入参“admin”为名的数据,“admin”的具体value即为方法返回对象
3.@Cacheable注解的方法参数为多个String
@Cacheable(value = "role")
public RolePojo getRoleByStr(String str,String str1,String str2){
return roleDao.getRoleByStr(str);
}
此时报错java.lang.ClassCastException: org.springframework.cache.interceptor.SimpleKey cannot be cast to java.lang.String
故最佳实践:@Cacheable 的key参数不采用缺省
1.在RedisConfig.java中定义缓存数据 key 生成策略的bean,key即采用此bean
package com.fcc.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import java.lang.reflect.Method;
/**
* @Author:FeiCongcong
* @Date:2017/7/3 0003 14:48
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
/*定义缓存数据 key 生成策略的bean
包名+类名+方法名+所有参数
*/
@Bean
public KeyGenerator wiselyKeyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
/*要启用spring缓存支持,需创建一个 CacheManager的 bean,CacheManager 接口有很多实现,这里Redis 的集成,用 RedisCacheManager这个实现类
Redis 不是应用的共享内存,它只是一个内存服务器,就像 MySql 似的,
我们需要将应用连接到它并使用某种“语言”进行交互,因此我们还需要一个连接工厂以及一个 Spring 和 Redis 对话要用的 RedisTemplate,
这些都是 Redis 缓存所必需的配置,把它们都放在自定义的 CachingConfigurerSupport 中
*/
@Bean
public CacheManager cacheManager(
@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// cacheManager.setDefaultExpiration(60);//设置缓存保留时间(seconds)
return cacheManager;
}
//1.项目启动时此方法先被注册成bean被spring管理
@Bean
public RedisTemplate<String, String> redisTemplate(
RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
Service层用法:
@Cacheable(value = "role",keyGenerator="wiselyKeyGenerator")
public RolePojo getRoleByStrs(String str,String str1,String str2){
return roleDao.getRoleByStr(str);
}

2.自己组装key(value值+方法参数值)
@Cacheable(value = "role",key="'role'.concat(#id)")
public RolePojo getRole(int id){
return roleDao.getRole(id);
}
注:如果key采用单纯的用参数值的组合,
@Cacheable(value = "user",key = "#id.toString()")
public UserPojo getUser(int id){
return userDao.getUser(id);
}
@Cacheable(value = "role",key="#id.toString()")
public RolePojo getRole(int id){
return roleDao.getRole(id);
}
当调用这两个方法时,若入参相同,就会报错com.fcc.model.RolePojo cannot be cast to com.fcc.model.UserPojo
@CachePut 作用和配置方法
@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 主要的参数
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | 例如: @Cacheable(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
@CacheEvict 作用和配置方法
@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 主要的参数
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | 例如: @CachEvict(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 | 例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”) |
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 | 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | 例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
@Cacheable 相当于insert()操作
@CachePut 相当于update()操作
@CacheEvict 相当于delete()操作
注意事项:
1.要缓存的 Java 对象必须实现 Serializable 接口,因为 Spring 会将对象先序列化再存入 Redis,如果不实现 Serializable 的话将会遇到类似这种错误:nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [......]]
2.CacheManager 设置缓存过期时间,否则缓存对象将永不过期,这样做可以避免一些野数据“永久保存”。此外,设置缓存过期时间也有助于资源利用最大化,因为缓存里保留的永远是热点数据。
(1).在cacheManager里面配置,对所有@Cache注解起作用
代码如下:
@Bean
public CacheManager cacheManager(
@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setDefaultExpiration(60);//设置缓存保留时间(seconds)
return cacheManager;
}
源码跟踪分析:
跟踪@Cacheable调用时发现@Cacheable 的实现是通过cglib代理来实现:
1.CglibAopProxy.intercept的方法,该方法中通过
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
获取一个List拦截链,然后通过
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
方法执行对应的拦截链进行执行处理。
最后通过,
processReturnType(proxy, target, method, retVal);
处理返回值,并返回。 其中具体在AbstractCacheInvoker.doPut()方法执行时,将数据插入到redis服务器
参考文档:http://blog.youkuaiyun.com/mergades/article/details/45244453
http://www.cnblogs.com/softidea/p/5801499.html
http://blog.youkuaiyun.com/sanjay_f/article/details/47372967