本篇做爲SpringBoot2.1版本的我的開發框架 子章節,請先閱讀SpringBoot2.1版本的我的開發框架再次閱讀本篇文章html
項目地址:SpringBoot2.1版本的我的應用開發框架前端
前端項目地址:ywh-vue-adminvue
項目中爲何要用Redis緩存?其實在我實習時是用到過Redis緩存的,可是我只是知道用到了Redis,如何使用的,爲何要用,這些我通通都不知道。引用網上一句話就是,我此次集成也是爲了Redis而Redis了,並非拿Redis來解決實際的問題,可是我以爲只有先學會了,才能知道在什麼狀況下能夠用Redis來解決問題。java
爲何要用redis?引用網上的言論git
1.解決應用服務器的cpu和內存壓力 2.在項目中使用 Redis,主要考慮兩個角度:性能和併發。 3.咱們在碰到須要執行耗時特別久,且結果不頻繁變更的 SQL,就特別適合將運行結果放入緩存。這樣,後面的請求就去緩存中讀取,使得請求可以迅速響應。 4.在大併發的狀況下,全部的請求直接訪問數據庫,數據庫會出現鏈接異常。這個時候,就須要使用redis作一個緩衝操做,讓請求先訪問到redis,而不是直接訪問數據庫。 5.排行榜及相關問題。排行榜(leader board)按照得分進行排序。zadd命令能夠直接實現這個功能,而zrevrange命令能夠用來按照得分來獲取前100名的用戶,zrank能夠用來獲取用戶排名,很是直接並且操做容易。 6.計數的問題,好比點贊和轉發數,經過原子遞增保持計數;getset用來重置計數器;過時屬性用來確認一個關鍵字何時應該刪除。github
個人學習筆記:redis
並且Redis能夠master-slave(主從)模式進行數據備份,在分佈式的系統中,能夠很好保證數據的備份,Redis會自動把主數據庫(master)中的數據備份到從數據庫(slave)中,關於爲何要用Redis這件事情上,除了自己本身項目中的緣由;剩下的有不少緣由均可以事先在網絡上汲取知識以及經驗。json
咱們把緩存的事情放到ywh-starter-cache這個模塊中來集成,在cache中的pom.xml文件中引入spring-boot-starter-data-redis依賴
<!-- redis依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
複製代碼
cache項目結構分爲
爲何用戶須要本身建立一個redis的配置類呢?
SpringBoot提供了對Redis的自動配置功能,在RedisAutoConfiguration類中默認爲咱們配置了客戶端鏈接(Lettuce和Jedis),以及數據操做模板(StringRedisTemplate和RedisTemplate),下列代碼有一個@ConditionalOnMissingBean和@Bean的註解,@ConditionalOnMissingBean註解判斷是否執行初始化代碼,即若是用戶已經建立了bean,則相關的初始化代碼再也不執行。這致使了默認的是redisTemplate方法會被執行。
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
複製代碼
RedisTemplate這個數據操做模板類咱們能夠點擊去看一看,在類中有一段代碼
if (this.defaultSerializer == null) {
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
複製代碼
若是默認的序列化爲空則使用jdk來序列化咱們的數據,而 defaultSerializer 這個私有屬性,默認爲NULL,因此默認的序列方式時jdk的方式,可是這個序列化方式會把數據變得人看不懂,因此才須要建立一個Redis配置類覆蓋默認的序列化,若是我有分析的有不對的地方,望指正。
分析事後,就以兩種方式來進行鏈接。第一種:不更改默認配置使用StringRedisTemplate和RedisTemplate
在咱們寫代碼測試以前須要在cache模塊中配置application-redis.yml文件,而且把core下的application.yml文件中的active屬性添加redis 以逗號相隔這樣就可在運行的時候讀取application-redis.yml的內容,把cache模塊下application.properties修改爲application-redis.yml文件進行配置。
spring:
redis:
# Redis數據庫索引(默認爲0)
database: 0
# Redis服務器地址
host: 127.0.0.1
# Redis服務器鏈接端口
port: 6379
# Redis服務器鏈接密碼(默認爲空)若是沒有配置密碼就不要寫這個屬性了
password: 123456
#鏈接池
lettuce:
pool:
#鏈接池最大鏈接數(使用負值表示沒有限制)
max-active: 8
#鏈接池最大阻塞等待時間(使用負值表示沒有限制)
max-wait: 60000
#鏈接池中的最大空閒鏈接
max-idle: 8
#鏈接池中的最小空閒鏈接
min-idle: 0
#鏈接超時時間(毫秒)
timeout: 10000
複製代碼
在SpringBoot測試類中編寫代碼並運行添加數據。
@Autowired
private StringRedisTemplate stringRedisTemplate;
/** * 測試鏈接redis,並存入數據 */
@Test
public void redisTest(){
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
stringRedisTemplate.opsForValue().set("abc","測試");
stringRedisTemplate.opsForList().leftPushAll("ywh",list);
}
複製代碼
咱們在用StringRedisTemplate添加的數據顯示正常,也是咱們人眼能讀懂的,接下來咱們用RedisTemplate這個類來鏈接Redis添加數據看一看數據是什麼樣的,默認的序列化JdkSerializationRedisSerializer的二進制數據序列化方式,代碼跟上面差很少。
@Autowired
private RedisTemplate<String,Object> redisTemplate;
@Test
public void redisTest1(){
List<String> list = new ArrayList<>();
list.add("y");
list.add("w");
list.add("h");
redisTemplate.opsForValue().set("redisTemplate","鏈接成功了");
redisTemplate.opsForList().leftPushAll("redis",list);
}
複製代碼
果真添加的數據咱們人眼分辨不出來這是什麼,因此下面咱們要進行覆蓋默認的配置,定製本身的序列化方式。
spring爲咱們提供了多種序列化方式,都在org.springframework.data.redis.serializer包下,經常使用的分別是:
這四種咱們只使用StringRedisSerializer來序列化Key值,value值由咱們本身建立的序列化類,serializer包下建立咱們自定義的FastJsonRedisSerializer類,須要實現RedisSerializer接口,實現接口中的序列化方法和反序列化方法,使用的是alibaba的Fastjson實現。
package com.ywh.cache.serializer;
/** * CreateTime: 2018-12-19 16:51 * ClassName: FastJsonRedisSerializer * Package: com.ywh.cache.serializer * Describe: * 自定義的序列化類 * @author YWH */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private ObjectMapper objectMapper = new ObjectMapper();
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz);
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull(objectMapper, "'objectMapper' must not be null");
this.objectMapper = objectMapper;
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
}
複製代碼
建立好自定義的序列化類後,進行覆蓋默認的配置,接下來在config包下建立RedisCacheConfig類,配置好Redis配置類之後咱們再次從新運行測試類,會發現值再也不是亂碼了。
package com.ywh.cache.config;
/** * CreateTime: 2018-12-18 23:34 * ClassName: RedisCacheConfig * Package: com.ywh.cache.config * Describe: * Redis緩存配置 @EnableCaching 開啓聲明式緩存支持. 以後就可使用 @Cacheable/@CachePut/@CacheEvict 註解緩存數據. * @author YWH */
@Configuration
@EnableCaching
public class RedisCacheConfig {
private RedisConnectionFactory redisConnectionFactory;
@Autowired
public void setRedisConnectionFactory(RedisConnectionFactory redisConnectionFactory){
this.redisConnectionFactory = redisConnectionFactory;
}
/** * 覆蓋默認的配置 * @return RedisTemplate */
@Bean
public RedisTemplate<String,Object> redisTemplate(){
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 設置value的序列化規則和key的序列化規則
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
template.setDefaultSerializer(fastJsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
/** * 解決註解方式存放到redis中的值是亂碼的狀況 * @param factory 鏈接工廠 * @return CacheManager */
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// 配置註解方式的序列化
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
RedisCacheConfiguration redisCacheConfiguration =
config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer))
//配置註解默認的過時時間
.entryTtl(Duration.ofDays(1));
// 加入白名單 https://github.com/alibaba/fastjson/wiki/enable_autotype
ParserConfig.getGlobalInstance().addAccept("com.ywh");
ParserConfig.getGlobalInstance().addAccept("com.baomidou");
return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();
}
}
複製代碼
redis能夠對五種類型操做,能夠本身再進行擴展,工具類咱們放在cache模塊下建立utils包,建立RedisUtil工具類,類上添加@Component註解,交給Spring來管理,咱們能夠直接在其餘的方法中用@AtuoWired獲取。
package com.ywh.cache.utils;
/** * CreateTime: 2018-12-19 17:45 * ClassName: RedisUtil * Package: com.ywh.cache.utils * Describe: * Redis工具類 * * @author YWH */
@Component
public class RedisUtil {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
//---------------------- common --------------------------
/** * 指定緩存失效時間 * @param key key值 * @param time 緩存時間 * @return true設置成功,time <=0 設置失敗 */
public void expire(String key, long time){
if(time > 0){
redisTemplate.expire(key,time,TimeUnit.SECONDS);
}else{
throw MyExceptionUtil.mxe("設置的時間不能爲0或者小於0!!");
}
}
/** * 判斷key是否存在 * @param key * @return true 存在 false 不存在 */
public Boolean existsKey(String key){
return redisTemplate.hasKey(key);
}
。。。。省略代碼,具體代碼可前往github查看
}
複製代碼