springboot整合redis使用技术分享

Springboot整合redis使用技巧分享

 

一、Spring Cache简介

Spring 3.1引入基于注解Cache支持,且提供了Cache抽象。

Spring 的缓存技术还具备相当的灵活性,不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 condition,还提供开箱即用的缓存临时存储方案,也支持和主流的专业缓存例如 EHCache、Redis 集成。

Spring提供的核心Cache接口:

  1. package org.springframework.cache;  
  2.   
  3. public interface Cache {  
  4.     String getName();  //缓存的名字  
  5.     Object getNativeCache(); //底层使用的缓存,如EhcacheRedis  
  6.     ValueWrapper get(Object key); //根据key得到一个ValueWrapper,然后调用其get方法获取值  
  7.     <T> T get(Object key, Class<T> type);//根据key,和value的类型直接获取value  
  8.     void put(Object key, Object value);//往缓存放数据  
  9.     void evict(Object key);//移除key对应的缓存  
  10.     void clear(); //清空缓存  
  11.   
  12.     interface ValueWrapper { //缓存值的Wrapper  
  13.         Object get(); //得到真实的value  
  14.         }  
  15. }

spring cache可以满足一般应用对缓存的需求,但是对于一些复杂的应用场景就不能直接使用,比如在Spring提供抽象API中并没有提供缓存策略的解决方案,缓存策略都是由底层Cache自行维护。

在Springboot中提供了基于spring cache的自动化配置,默认提供了Ehcache、Guava、Redis等的配置实现。我们在应用开发中使用的是Redis缓存,并且出现了一些复杂的应用场景,需要对于不同缓存制定不同的策略。

查看了Springboot实现Redis自动化配置的源码。发现在RedisCacheManager类中有这两个属性defaultExpiration和expires。

 

这时候就可以通过在Spring注册一个RedisCacheManager实现缓存策略。

 

二、配置多个Cache Manager实例与使用

如果想要实现多个Cache Manager实例也是可以的,比如还想要一个内存缓存GuavaCache,需要使用@Primary注解指定一个Cache Manager作为默认缓存。(在创建bean的时候最好指定缓存管理实例的bean名称),使用的时候不指定缓存管理实例名称就使用默认缓存,指定缓存管理实例就是使用指定的缓存管理实例。
例如:
@Cacheable(value = " cache ",key = "key")//使用redisCacheManager, @Primary指定
@Cacheable(cacheNames = "guavaCacheManager",value = " guavaCache ",key = "key")//使用guavaCacheManager

三、Redis缓存使用技巧

在redisCacheManager配置中有一个比较有意思的配置属性:usePrefix;这个表示缓存数据时,缓存的键是否使用前缀。
通过源码可以看到,如果不使用前缀,redis将会在缓存中维护一个有序集合(SortedSet)来保存该类缓存数据的key,方便维护(如清空缓存)。但是需要注意,这个有序集合在缓存的数据量过大的时候,会导致redis访问出现异常甚至宕机。
当使用缓存前缀时,将会使用缓存名称作为前缀,并且默认以冒号分割(cache:key)。
这个中使用方式可以缓存上亿数据,redis也能正常提供服务。这种使用方式禁止使用clear()方法清空缓存(特别是在redis中有大量数据时),这个方法使用的是keys去找对应的key并删除数据,clear()方法调用将导致redis长时间不可用。

四、内存缓存使用注意

使用内存缓存时,应该作为只读缓存。不要修改数据,特别是一些引用数据,内存缓存对象没有实现序列化,当你修改的时候,会改变原有引用的数据。如果需要修改数据,可以自己实现序列化和反序列化。

五、示例代码

1、配置文件
 
 
2、代码实现

 

 

 

package com.zto.thrall.business.redis;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.guava.GuavaCache;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.cache.CacheBuilder;

import redis.clients.jedis.JedisPoolConfig;

/**
 * Created by yt on 2017-3-6.
 */


@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisProperties.class)
@ConfigurationProperties(prefix ="redis-expires")
public class RedisConfig extends CachingConfigurerSupport {

   @Autowired
   private  RedisProperties redisProperties;



   private List<String> cacheNames = new ArrayList<String>();

   private long defaultExpiration;

   private Map<String,Long> expires;

   public List<String> getCacheNames() {
      return cacheNames;
   }

   public void setCacheNames(List<String> cacheNames) {
      this.cacheNames = cacheNames;
   }

   public long getDefaultExpiration() {
      return defaultExpiration;
   }

   public void setDefaultExpiration(long defaultExpiration) {
      this.defaultExpiration = defaultExpiration;
   }

   public Map<String, Long> getExpires() {
      return expires;
   }

   public void setExpires(Map<String, Long> expires) {
      this.expires = expires;
   }

   @Bean
   public KeyGenerator keyGenerator(){ //
重写缓存key生成机制
      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();
         }
      };

   }

   @Bean(name = "redisCacheManager")
   @Primary
   public CacheManager redisCacheManager(RedisTemplate redisTemplate) {
      RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
      redisCacheManager.setCacheNames(cacheNames);
      redisCacheManager.setDefaultExpiration(defaultExpiration);
      redisCacheManager.setExpires(expires); //
设置缓存过期时间
      redisCacheManager.setUsePrefix(true); //是否使用缓存前缀
      redisCacheManager.afterPropertiesSet();
      return redisCacheManager;
   }

   @Bean(name = "guavaCacheManager")
   public CacheManager guavaCacheManager() {
      SimpleCacheManager cacheManager = new SimpleCacheManager();
      ArrayList<GuavaCache> caches = new ArrayList<GuavaCache>();
      caches.add(new GuavaCache("guavaCache", CacheBuilder.newBuilder().maximumSize(30000).expireAfterWrite(2, TimeUnit.HOURS).build()));
      cacheManager.setCaches(caches);
      return cacheManager;
   }

   @Bean
   public RedisTemplate<Object, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
      RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
      redisTemplate.setConnectionFactory(jedisConnectionFactory);

      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
      ObjectMapper objectMapper = new ObjectMapper();
      objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
      jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
      redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
      redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
      redisTemplate.afterPropertiesSet();
      return redisTemplate;
   }


   @Bean
   public JedisConnectionFactory redisConnectionFactory() {
      JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
      redisConnectionFactory.setHostName(redisProperties.getHost());
      redisConnectionFactory.setPort(redisProperties.getPort());
      redisConnectionFactory.setPassword(redisProperties.getPassword());
      redisConnectionFactory.setTimeout(redisProperties.getTimeout());
      redisConnectionFactory.setPoolConfig(jedisPoolConfig());
      redisConnectionFactory.afterPropertiesSet();
      return redisConnectionFactory;
   }

   private JedisPoolConfig jedisPoolConfig() {
      JedisPoolConfig config = new JedisPoolConfig();
      RedisProperties.Pool props = this.redisProperties.getPool();
      config.setMaxTotal(props.getMaxActive());
      config.setMaxIdle(props.getMaxIdle());
      config.setMinIdle(props.getMinIdle());
      config.setMaxWaitMillis(props.getMaxWait());
      return config;
   }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值