Spring Boot In Practice (1):Redis缓存实战

阅读本文须要对Spring和Redis比较熟悉。javascript

Spring Framework 提供了Cache Abstraction对缓存层进行了抽象封装,经过几个annotation能够透明给您的应用增长缓存支持,而不用去关心底层缓存具体由谁实现。目前支持的缓存有java.util.concurrent.ConcurrentMap,Ehcache 2.x,Redis等。html

通常咱们使用最经常使用的Redis作为缓存实现(Spring Data Redis),java

  • 须要引入的starter: spring-boot-starter-data-redis,spring-boot-starter-cache;
  • 自动配置生成的Beans: RedisConnectionFactory, StringRedisTemplate , RedisTemplate, RedisCacheManager,自动配置的Bean能够直接注入咱们的代码中使用;

I. 配置

application.properties

# REDIS (RedisProperties)
spring.redis.host=localhost # Redis server host.
spring.redis.port=6379 # Redis server port.
spring.redis.password= # Login password of the redis server.复制代码

具体对Redis cluster或者Sentinel的配置能够参考这里git

开启缓存支持

@SpringBootApplication
@EnableCaching//开启caching
public class NewsWebServer {
//省略内容
}复制代码

定制RedisTemplate

自动配置的RedisTemplate并不能知足大部分项目的需求,好比咱们基本都须要设置特定的Serializer(RedisTemplate默认会使用JdkSerializationRedisSerializer)。github

Redis底层中存储的数据只是字节。虽然Redis自己支持各类类型(List, Hash等),但在大多数状况下,这些指的是数据的存储方式,而不是它所表明的内容(内容都是byte)。用户本身来决定数据如何被转换成String或任何其余对象。用户(自定义)类型和原始数据类型之间的互相转换经过RedisSerializer接口(包org.springframework.data.redis.serializer)来处理,顾名思义,它负责处理序列化/反序列化过程。多个实现能够开箱即用,如:StringRedisSerializer和JdkSerializationRedisSerialize。Jackson2JsonRedisSerializer或GenericJackson2JsonRedisSerializer来处理JSON格式的数据。请注意,存储格式不只限于value 它能够用于key,Hash的key和value。redis

声明本身的RedisTemplate覆盖掉自动配置的Bean:spring

//通用的RedisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory) {
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    template.setConnectionFactory(jedisConnectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    //template.setHashKeySerializer(template.getKeySerializer());
    //template.setHashValueSerializer(template.getValueSerializer());
    return template;
}复制代码

这里咱们使用GenericJackson2JsonRedisSerializer而不是Jackson2JsonRedisSerializer,后者的问题是你须要为每个须要序列化进Redis的类指定一个Jackson2JsonRedisSerializer由于其构造函数中须要指定一个类型来作反序列化:sql

redis.setValueSerializer(new Jackson2JsonRedisSerializer<Product>(Product.class));复制代码

若是咱们应用中有大量对象须要缓存,这显然是不合适的,而前者直接把类型信息序列化到了JSON格式中,让一个实例能够操做多个对象的反序列化。express

定制RedisCacheManager

有时候Spring Boot自动给咱们配置的RedisCacheManager也不能知足咱们应用的需求,我看到不少用法都直接声明了一个本身的RedisCacheManager,其实使用CacheManagerCustomizer能够对自动配置的RedisCacheManager进行定制化:后端

@Bean
    public CacheManagerCustomizer<RedisCacheManager> cacheManagerCustomizer() {
        return new CacheManagerCustomizer<RedisCacheManager>() {
            @Override
            public void customize(RedisCacheManager cacheManager) {
                cacheManager.setUsePrefix(true); //事实上这是Spring Boot的默认设置,为了不key冲突

                Map<String, Long> expires = new HashMap<>();
                expires.put("myLittleCache", 12L*60*60);  // 设置过时时间 key is cache-name
                expires.put("myBiggerCache", 24L*60*60);
                cacheManager.setExpires(expires);  // expire per cache

                cacheManager.setDefaultExpiration(24*60*60);// 默认过时时间:24 hours
            }
        };
    }复制代码

II. 使用

缓存Key的生成

咱们都知道Redis是一个key-value的存储系统,不管咱们想要缓存什么值,都须要制定一个key。

@Cacheable(cacheNames = "user")
 public User findById(long id) {
     return userMapper.findById(id);
 }复制代码

上面的代码中,findById方法返回的对象会被缓存起来,key由默认的org.springframework.cache.interceptor.SimpleKeyGenerator生成,生成策略是根据被标注方法的参数生成一个SimpleKey对象,而后由RedisTemplate中定义的KeySerializer序列化后做为key(注意StringRedisSerializer只能序列化String类型,对SimpleKey对象无能为力,你只能定义其余Serializer)。

不过大多数状况下咱们都会采用本身的key生成方案,方式有两种:

1.实现本身的KeyGenerator;

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
    @Bean
    public KeyGenerator customKeyGenerator() {
        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());
            }
            return sb.toString();
          }
        };
   }
}复制代码

2.在@Cacheable标注中直接声明key:

@Cacheable(cacheNames = "user", key="#id.toString()") ❶
 public User findById(long id) {
     return userMapper.findById(id);
 }

@Cacheable(cacheNames = "user", key="'admin'") ❷
 public User findAdmin() {
     return userMapper.findAdminUser();
 }

@Cacheable(cacheNames = "user", key="#userId + ':address'") ❸
 public List<Address> findUserAddress(long userId) {
     return userMapper.findUserAddress(userId);
 }复制代码

key的声明形式支持SpEL
❶ 最终生成的Redis key为:user:100234,user部分是由于cacheManager.setUsePrefix(true),cacheName会被添加到key做为前缀避免引发key的冲突。之因此#id.toString()要long型转为String是由于咱们设置的KeySerializer为StringRedisSerializer只能用来序列化String。
❷ 若是被标注方法没有参数,咱们能够用一个静态的key值,最终生成的key为user:admin
❸ 最终生成的key为user:100234:address

这种方式更符合咱们之前使用Redis的习惯,因此推荐。

直接使用RedisTemplate

有时候标注不能知足咱们的使用场景,咱们想要直接使用更底层的RedisTemplate

@Service
public class FeedService {

    @Resource(name="redisTemplate") ❶
    private ZSetOperations<String, Feed> feedOp;

    public List<Feed> getFeed(int count, long maxId) {
        return new ArrayList<>(feedOp.reverseRangeByScore(FEED_CACHE_KEY, 0, maxId, offset, count));
    }   
  //省略
}复制代码

❶ 咱们能够直接把RedisTemplate的实例注入为ZSetOperationsListOperationsValueOperations等类型(Spring IoC Container帮咱们作了转化工做,能够参考org.springframework.data.redis.core.ZSetOperationsEditor)。

除了当缓存,Redis还能干啥

org.springframework.data.redis.support包中提供了一些以Redis做为后端存储的组件,包括原子计数器和Java Collections的一些实现类。

@Service
public class ActivityService {
    RedisAtomicInteger counter;
    public ActivityService(RedisConnectionFactory connectionFactory) {
        counter = new RedisAtomicInteger(counterKey,
                connectionFactory, 1);
    }
}复制代码

以上代码建立了一个分布式的原子计数器。

III. 参考

#coding/spring

相关文章
相关标签/搜索