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

相關文章
相關標籤/搜索