添加jar包依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> |
Redis配置文件
spring.redis.database=0 spring.redis.port=6379 spring.redis.host=172.21.13.24 spring.redis.timeout=5000 #spring.redis.password=123 spring.redis.pool.max-idle=8 spring.redis.pool.min-idle=1 spring.redis.pool.max-active=8 spring.redis.pool.max-wait=6000 |
创建配置类
package net.thinktrader.fundta.cache; import org.springframework.boot.context.properties.ConfigurationProperties; 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.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import redis.clients.jedis.JedisPoolConfig; @Configuration public class RedisConfig { /** * redis连接池配置 * @return */ @Bean @ConfigurationProperties(prefix="spring.redis.pool") public JedisPoolConfig getRedisConfig(){ JedisPoolConfig config = new JedisPoolConfig(); return config; } /** * redis连接工厂配置 * @return */ @Bean @ConfigurationProperties(prefix="spring.redis") public JedisConnectionFactory getConnectionFactory(){ JedisConnectionFactory factory = new JedisConnectionFactory(); JedisPoolConfig config = getRedisConfig(); factory.setPoolConfig(config); return factory; } /** * Spring Data Redis提供了两个模板: * RedisTemplate * StringRedisTemplate * 这里创建一个RedisTemplate模板类,类型的key是String类型,value是Object类型(如果key和value都是String类型,建议使用StringRedisTemplate) * * 当保存一条数据的时候,key和value都要被序列化成json数据,取出来的时候反序列化成对象,key和value都会使用序列化器进行序列化,spring data redis提供多个序列化器 * GenericToStringSerializer:使用Spring转换服务进行序列化; * JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON; * Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON; * JdkSerializationRedisSerializer:使用Java序列化; * OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化; * StringRedisSerializer:序列化String类型的key和value。 * RedisTemplate会默认使用JdkSerializationRedisSerializer,这意味着key和value都会通过Java进行序列化。StringRedisTemplate默认会使用StringRedisSerializer * 例如,假设当使用RedisTemplate的时候,我们希望将value序列化为JSON,而key是String类型。需要用RedisTemplate的setKeySerializer()和setValueSerializer()方法进行设置 * * */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Bean("redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory factory){ // 创建一个模板类 RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); // 将刚才的redis连接工厂设置到模板类中 template.setConnectionFactory(factory); // 设置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); // 设置value的序列化器 //使用Jackson 2,将对象序列化为JSON Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); //json转对象类,不设置默认的会将json转成hashmap ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); template.setValueSerializer(jackson2JsonRedisSerializer); return template; } /** * 申明缓存管理器,会创建一个切面(aspect)并触发Spring缓存注解的切点(pointcut) * 根据类或者方法所使用的注解以及缓存的状态,这个切面会从缓存中获取数据,将数据添加到缓存之中或者从缓存中移除某个值 * 当然,缓存管理器除了RedisCacheManager还有一些其他的。例如 * SimpleCacheManager * NoOpCacheManager * ConcurrentMapCacheManager:这个简单的缓存管理器使用java.util.concurrent.ConcurrentHashMap作为其缓存存储。它非常简单,因此对于开发、测试或基础的应用来讲,这是一个很不错的选择. * CompositeCacheManager * EhCacheCacheManager * @return */ @SuppressWarnings("rawtypes") @Bean public RedisCacheManager cacheManager(RedisTemplate redisTemplate) { return new RedisCacheManager(redisTemplate); } } |
Spring Data Redis提供了两个模板:
- RedisTemplate
- StringRedisTemplate
上述代码中,创建一个RedisTemplate模板类,类型的key是String类型,value是Object类型(如果key和value都是String类型,建议使用StringRedisTemplate)。
key和value序列化:
当保存一条数据的时候,key和value都要被序列化,取出来的时候反序列化成对象,key和value都会使用序列化器进行序列化,spring data redis提供多个序列化器
- GenericToStringSerializer:使用Spring转换服务进行序列化;
- JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
- Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
- JdkSerializationRedisSerializer:使用Java序列化;
- OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化;
- StringRedisSerializer:序列化String类型的key和value。
RedisTemplate会默认使JdkSerializationRedisSerializer,即缓存的键值都通过Java进行序列化。StringRedisTemplate默认会使用StringRedisSerializer。上述代码中使用了RedisTemplate的setKeySerializer()和setValueSerializer()方法设置key和value的序列化器。
Redis测试代码如下
springboot缓存某个方法
向上面那样使用Redis,是一件比较麻烦的事,springboot为了能减少开发的工作量,允许对某个方法进行缓存操作。但必须配置缓存管理器。
缓存管理器代码如上面RedisConfig类中的cacheManager方法,且为了让缓存管理器生效,还需要在系统启动类(或者单元测试启动类)上添加@EnableCaching注解。这个注解会在系统启动时被springboot发现,并且在执行有CachePut、CacheEvict、Cacheable注解修饰的方法时,进行缓存操作。
内部原理应该是系统在启动时,使用动态代理初始化bean,在执行bean的相关方法时,看这个方法是否有CachePut等注解,如果有,则执行CacheInterceptor拦截器,该拦截器会根据注解的名称以及注解的配置进行相关的操作,并决定最终是否需要调用bean的目标方法,以及调用该方法后该如何处理。
springboot缓存管理器有如下几种:
- RedisCacheManager:Redis缓存管理器
- SimpleCacheManager
- NoOpCacheManager
- ConcurrentMapCacheManager:使用java中的 ConcurrentHashMap作为其缓存存储。
- CompositeCacheManager
- EhCacheCacheManager
@Cacheable:缓存方法返回值,主要用在查询方法中,先看缓存中是否有查询的数据,如果没有,则执行方法,并将方法执行的结果数据缓存到缓存中,示例代码如下:
@Cacheable(cacheNames="userCache15",key="'user_'+#id",condition="#id>10") public User getUser(Long id){ System.err.println("如果第二次没有走到这里说明缓存被添加了"); User u=new User(); u.setDataSourceName(DefaultDBConfigContent.DB1); User result=findById(id); return result; } |
value/cacheNames:缓存名称。
key:缓存的key,#id表示为id的参数,也可以写成#p0,表示第一个参数,#p1表示第二个参数,如果参数为对象,还可以写成#p0.id这种形式。
condition:缓存的条件,如果条件为true,则缓存,否则不缓存。
注意:
@Cacheable不能缓存所有数据,即代码如下所示:
@Cacheable(cacheNames="userCache15") public List<User> getUsers(){ System.out.println("如果第二次没有走到这里说明缓存被添加了"); User u=new User(); u.setDataSourceName(DefaultDBConfigContent.DB1); return findAll(u); } |
可能与我们常规的理解不一样。如果执行顺序这样:第一次向数据库插入一条数据(同时插入缓存),调用这个方法,第二次向数据库插入一条数据(同时插入缓存),再调用这个方法,常规理解,第二次调用这个方法时应该返回2条数据,但结果是只返回第一次插入的数据。可能是springboot在第一次调用这个方法后,将结果进行本地缓存,以后再调用这个方法,直接将本地缓存的结果返回给用户。至于要如何获得缓存中的所有数据,可能需要通过工具类直接操作Redis。
@CachePut:缓存方法返回值,主要用在新增和修改方法上,即方法先保存到数据库中,方法结束后,将返回值保存和更新到缓存中。示例代码如下:
@CachePut(cacheNames="userCache15",key="'user_'+#result.id") public User saveUser(User vo) { super.save(vo); return vo; } @CachePut(cacheNames="userCache15",key="'user_'+#result.id") public User updateUser(User vo) { super.update(vo); return vo; } |
#result为方法的返回值,不能用在@Cacheable中。
其他参数如value、key和@Cacheable意义一致。
@CacheEvict:删除缓存中的值。示例代码如下:
/** * 删除缓存中所有数据 * @return */ @CacheEvict(cacheNames="userCache15",allEntries=true,beforeInvocation=false) public String deleteUser(){ return "SUCCESS"; } /** * 删除缓存中id为指定id的数据,注意:如果这里的allEntries为true,则会删除所有值,而不是只删除id对应的值 * @param id * @return */ @CacheEvict(cacheNames="userCache15",key="'user_'+#id",allEntries=false,beforeInvocation=false) public String deleteUser(Long id){ return "SUCCESS"; } |
allEntries:表示是否删除所有值,默认为false
beforeInvocation:表示是否在执行本方法前删除缓存中符合条件的值,默认为false。
其他参数如value、key和@Cacheable意义一致。
除了上面介绍到的#result外,Spring Cache提供了一些供我们使用的SpEL上下文数据,如下表所示:
表达式 | 描述 | 实例 |
#root.args | 缓存方法的参数,是一个数组 | key="'user_'+#root.args[0]" |
#root.target | 当前被调用的目标对象 | condition = "#root.target.canCache()" //目标对象的canCache方法的返回值为条件 |
#root.targetClass | 目标对象的类 | condition = "#root.targetClass.canCache()" //目标对象的canCache静态方法返回值 |
#root.caches | 当前方法调用使用的缓存列表 (如@Cacheable(value= {“cache1”, “cache2”}), 则有两个cache | condition = "#root.caches[0].get(#user.id) .get().username ne #user.username" |
#root.methodName | 当前被调用的方法名 | condition = "#root.methodName ne 'getUser'" |
#root.method | 当前被调用的方法 | |
#result | 方法执行后的返回值 仅当方法执行之后且判断有效的 数据,如‘unless’过滤后的数据 | key="'user_'+#result.id" |
argument | 当前被调用的方法的参数, 如findById(Long id), | 我们可以通过#id或者#p0或者#a0拿到参数 |