閱讀本文須要對Spring和Redis比較熟悉。javascript
Spring Framework 提供了Cache Abstraction對緩存層進行了抽象封裝,經過幾個annotation能夠透明給您的應用增長緩存支持,而不用去關心底層緩存具體由誰實現。目前支持的緩存有java.util.concurrent.ConcurrentMap
,Ehcache 2.x,Redis等。html
通常咱們使用最經常使用的Redis作爲緩存實現(Spring Data Redis),java
spring-boot-starter-data-redis
,spring-boot-starter-cache
;RedisConnectionFactory
, StringRedisTemplate
, RedisTemplate
, RedisCacheManager
,自動配置的Bean能夠直接注入咱們的代碼中使用;# 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
並不能知足大部分項目的需求,好比咱們基本都須要設置特定的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
有時候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
}
};
}複製代碼
咱們都知道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
。
@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的實例注入爲ZSetOperations
、ListOperations
、ValueOperations
等類型(Spring IoC Container幫咱們作了轉化工做,能夠參考org.springframework.data.redis.core.ZSetOperationsEditor
)。
org.springframework.data.redis.support
包中提供了一些以Redis做爲後端存儲的組件,包括原子計數器和Java Collections的一些實現類。
@Service
public class ActivityService {
RedisAtomicInteger counter;
public ActivityService(RedisConnectionFactory connectionFactory) {
counter = new RedisAtomicInteger(counterKey,
connectionFactory, 1);
}
}複製代碼
以上代碼建立了一個分佈式的原子計數器。
#coding/spring