1. 環境準備
系統工具 | 版本 | 備註 |
---|---|---|
Windows | 10 | |
Idea | 2019.3 | |
JDK | 1.8 | |
Redis | 5.0.3 | 參考 docker+docker compose 部署Redis |
2.創建工程
由於使用的是Idea,因此我們可以借用Idea提供的工程創建工具快速創建一個SpringBoot+Redis+SpringCache工程,具體步驟如下:
選擇新建工程,選擇項目類型如下:
完成Maven工程基本定義:
勾選需要的依賴:
這裏我勾選了DeveloperTools-Lombok、Web-Spring Web、SQL-Mybatis、MySQL driver、Spring Data JDBC、NoSQL-Spring Data Redis
由於工程需要添加commons-pool、druid、swagger相關依賴
最終生成工程POM如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency><!--添加Swagger依賴 -->
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency><!--添加Swagger-UI依賴 -->
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
初始項目創建完畢。
項目結構如下:
3.編寫項目配置
3.1 整體項目配置
SpringBoot項目是使用application.yml/application.properties完成項目配置,主要配置分爲項目端口配置、Mysql相關配置、MyBatis相關配置、Redis相關配置、SpringCache相關配置、Swagger相關配置。
根據本地環境編寫application.yml如下:
server:
port: 8080
spring:
datasource:
name: mysql_test
type: com.alibaba.druid.pool.DruidDataSource
#druid相關配置
druid:
#監控統計攔截的filters
filters: stat
driver-class-name: com.mysql.jdbc.Driver
#基本屬性
url: jdbc:mysql://localhost:3306/redis_demo?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: root
password: 123456
#配置連接池 初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
#獲取連接等待超時時間
max-wait: 60000
#間隔多久進行一次檢測,檢測需要關閉的空閒連接
time-between-eviction-runs-millis: 60000
#一個連接在池中最小生存的時間
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打開PSCache,並指定每個連接上PSCache的大小。oracle設爲true,mysql設爲false。分庫分表較多推薦設置爲false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
#redis相關配置 host把端口加進去(詳見RedisConfig的LettuceConnectionFactory)
redis:
database: 0
host: 192.168.1.130
port: 6379
timeout: 5000
lettuce:
pool:
max-idle: 8
max-wait: -1
max-active: 8
min-idle: 0
##Spring cache相關配置
cache:
cache-names: ca1
# 過期時間1天
expireTime: 86400
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.demo
#swagger
swagger:
title: spring-boot-starter-swagger
description: Starter for swagger 2.x
version: 1.1.0.RELEASE
license: Apache License, Version 2.0
license-url: https://www.apache.org/licenses/LICENSE-2.0.html
terms-of-service-url: https://github.com/github-sun/Funs
base-package: com.example.redis.demo
contact:
name: xxx
email: [email protected]
3.1 Redis配置
Redis的配置這裏主要是RedisTemplate相關,RedisTemplate是Spring Data Redis提供給用戶的最高級的抽象客戶端,用戶可直接通過RedisTemplate進行多種操作。但是默認的RedisTemplate是RedisTemplate<K, V>
,K、V都是泛型,假如直接用 RedisTemplate,並沒有指定序列化器,會導致存什麼類型的Value都會帶BOM_UTF8,標識編碼方式的字符,像下圖這個樣子:
因此我們需要爲不同類型的V制定不同類型的序列化器,好讓他能解析成我們能看的樣子。
編寫RedisConfig如下:
/**
* Redis配置類
*/
@Configuration
// 控制配置類的加載順序,先加載 RedisAutoConfiguration.class 再加載該類,這樣才能覆蓋默認的 RedisTemplate
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
/**
* 自定義 redisTemplate (方法名一定要叫 redisTemplate 因爲 @Bean 是根據方法名配置這個bean的name的)
* 默認的 RedisTemplate<K,V> 爲泛型,使用時不太方便,自定義爲 <String, Object>
* 默認序列化方式爲 JdkSerializationRedisSerializer 序列化後的內容不方便閱讀,改爲序列化成 json
*
* @param redisConnectionFactory
* @return
*/
@Bean
RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 創建並配置自定義 RedisTemplateRedisOperator
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// 將 key 序列化成字符串
template.setKeySerializer(new StringRedisSerializer());
// 將 hash 的 key 序列化成字符串
template.setHashKeySerializer(new StringRedisSerializer());
// 將 value 序列化成 json
// template.setValueSerializer(jacksonSerializer);
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
// 將 hash 的 value 序列化成 json
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
}
這樣結果就會像下圖這個樣子:
3.2 SpringCache配置
SpringCache是什麼呢?SpringCache之於Redis就相當於JPA之於Mybatis。
Spring Cache 提供了 @Cacheable 、@CachePut 、@CacheEvict 、@Caching 等註解,在方法上使用。通過註解 Cache 可以實現類似事務一樣、緩存邏輯透明的應用到我們的業務代碼上,且只需要更少的代碼。
核心思想:當我們調用一個方法時會把該方法的參數和返回結果最爲一個鍵值對存放在緩存中,等下次利用同樣的參數來調用該方法時將不會再執行,而是直接從緩存中獲取結果進行返回。
3.2.1 SpringCache配置
1.@EnableCaching
開啓緩存功能,一般放在啓動類上。
2.@CacheConfig
當我們需要緩存的地方越來越多,你可以使用@CacheConfig(cacheNames = {“cacheName”})註解在 class 之上來統一指定value的值,這時可省略value,如果你在你的方法依舊寫上了value,那麼依然以方法的value值爲準。
3.@Cacheable
根據方法對其返回結果進行緩存,下次請求時,如果緩存存在,則直接讀取緩存數據返回;如果緩存不存在,則執行方法,並把返回的結果存入緩存中。一般用在查詢方法上。
查看源碼,屬性值如下:
屬性/方法名 | 解釋 |
---|---|
value | 緩存名,必填,它指定了你的緩存存放在哪塊命名空間 |
cacheNames | 與 value 差不多,二選一即可 |
key | 可選屬性,可以使用 SpEL 標籤自定義緩存的key |
keyGenerator | key的生成器。key/keyGenerator二選一使用 |
cacheManager | 指定緩存管理器 |
cacheResolver | 指定獲取解析器 |
condition | 條件符合則緩存 |
unless | 條件符合則不緩存 |
sync | 是否使用異步模式,默認爲false |
4.@CachePut
使用該註解標誌的方法,每次都會執行,並將結果存入指定的緩存中。其他方法可以直接從響應的緩存中讀取緩存數據,而不需要再去查詢數據庫。一般用在新增方法上。
查看源碼,屬性值如下:
屬性/方法名 | 解釋 |
---|---|
value | 緩存名,必填,它指定了你的緩存存放在哪塊命名空間 |
cacheNames | 與 value 差不多,二選一即可 |
key | 可選屬性,可以使用 SpEL 標籤自定義緩存的key |
keyGenerator | key的生成器。key/keyGenerator二選一使用 |
cacheManager | 指定緩存管理器 |
cacheResolver | 指定獲取解析器 |
condition | 條件符合則緩存 |
unless | 條件符合則不緩存 |
5.@CacheEvict
使用該註解標誌的方法,會清空指定的緩存。一般用在更新或者刪除方法上
查看源碼,屬性值如下:
屬性/方法名 | 解釋 |
---|---|
value | 緩存名,必填,它指定了你的緩存存放在哪塊命名空間 |
cacheNames | 與 value 差不多,二選一即可 |
key | 可選屬性,可以使用 SpEL 標籤自定義緩存的key |
keyGenerator | key的生成器。key/keyGenerator二選一使用 |
cacheManager | 指定緩存管理器 |
cacheResolver | 指定獲取解析器 |
condition | 條件符合則緩存 |
allEntries | 是否清空所有緩存,默認爲 false。如果指定爲 true,則方法調用後將立即清空所有的緩存 |
beforeInvocation | 是否在方法執行前就清空,默認爲 false。如果指定爲 true,則在方法執行前就會清空緩存 |
3.2.2 CacheConfig
SpringCache是緩存的上層框架,通過它我們可以不需要編寫通過redisTemplate寫入/查詢緩存的操作,但是同樣的還是得序列化,具體配置如下:
@Configuration
// 開啓 Spring Cache
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
@Value("${spring.cache.expireTime}")
// 緩存超時時間
private int cacheExpireTime;
/**
* 配置@Cacheable、@CacheEvict等註解在沒有指定Key的情況下,key生成策略
* 該配置作用於緩存管理器管理的所有緩存
* 最終生成的key 爲 cache類註解指定的cacheNames::類名:方法名#參數值1,參數值2...
*
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer sb = new StringBuffer();
sb.append(target.getClass().getName());
sb.append(":");
sb.append(method.getName());
sb.append("#");
for (Object obj : params) {
sb.append(obj.toString());
sb.append(",");
}
return sb.substring(0, sb.length() - 1);
}
};
}
/**
* 配置緩存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
//關鍵點,spring cache 的註解使用的序列化都從這來,沒有這個配置的話使用的jdk自己的序列化,實際上不影響使用,只是打印出來不適合人眼識別
RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
// 將 key 序列化成字符串
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 將 value 序列化成 json
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// 設置緩存過期時間,單位秒
.entryTtl(Duration.ofSeconds(cacheExpireTime))
// 不緩存空值
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(cacheConfig)
.build();
}
}
4.使用EasyCode初始化數據庫對象相關類
爲了方便演示與其他章節的測試,因此需要初始化個數據庫對象,同時相應編寫entity、dao、service、controller類,mapper文件。
創建一個user表,含有id(主鍵 自增)以及name兩個屬性。
如題所示,我們將使用idea的easycode插件,完成entity、dao、service、controller類,mapper文件創建。
在idea中選擇Database標籤,右鍵表出現EasyCode,選擇GenerateCode。
然後按照需要配置包名,文件路徑,生成文件。
ok之後,在指定包生成相應文件如下:
5. 測試應用
5.1 普通web應用測試
爲了檢驗項目能不能用起來,我們一做不做二不休,全套做起來(後面章節方便),其實按照4生成的,我們啓動項目,訪問localhost:port/swagger-ui.html對接口操作已經是沒問題的。
保存操作成功
數據庫也寫入成功,其實idea自帶db還是可以用的。
5.2 應用緩存Redis+SpringCache
5.2.1 SpringCache應用
這個項目目的是使用Redis緩存提高查詢效率,降低數據庫壓力的,提高用戶體驗的,因此我們需要在目前項目基礎上添加相關緩存應用。
由於我們用了SpringCache,我們直接用註解來示範,如何快速完成緩存寫入/查詢定義。
首先定義對象所在的緩存命名空間,以操作User服務類爲例。
@CacheConfig(cacheNames = "ca1")
public class UserServiceImpl implements UserService
此處“ca1”與application.yml裏面的spring.cache.cache-names(可以多個name)中對應。
5.2.1.1 查詢
使用@Cacheable實現查詢通過id先查緩存,第一次查詢把結果放到緩存中,後面查詢直接查緩存。
/**
* 通過ID查詢單條數據
*
* @param id 主鍵
* @return 實例對象
*/
@Override
@Cacheable( key = "#id")
public User queryById(int id) {
return this.userDao.queryById(id);
}
5.2.1.2 更新
使用@CachePut實現數據更新刷新緩存。
/**
* 修改數據
*
* @param user 實例對象
* @return 實例對象
*/
@Override
@CachePut( key = "#id")
public User update(User user) {
this.userDao.update(user);
return this.queryById(user.getId());
}
5.2.1.3 刪除
/**
* 通過主鍵刪除數據
*
* @param id 主鍵
* @return 是否成功
*/
@Override
@CacheEvict
public boolean deleteById(int id) {
return this.userDao.deleteById(id) > 0;
}
5.2.2 定義測試接口
定義測試接口:
/**
* 通過主鍵查詢單條數據
*
* @param id 主鍵
* @return 單條數據
*/
@GetMapping("selectOne")
public User selectOne(int id) {
return this.userService.queryById(id);
}
/**
* 插入單條數據
* @param name
* @return
*/
@PostMapping("insertOne")
public User insert(String name) {
User user = new User(name);
return userService.insert(user);
}
/**
* 刪除單條數據
* @param id
* @return
*/
@PostMapping("deleteOne")
public Boolean delete(int id) {
return userService.deleteById(id);
}
5.2.3 測試接口
5.2.3.1 調用insertOne
調用新增User對象接口,結果如下:
此時觀察Redis數據,沒有key爲1011的value,因爲我們沒用加相關注解,因此是沒有把1011對象存到緩存,緩存一般是應用在對查詢較多的靜態對象。所以插入數據的時候可以選擇不放緩存。
5.2.3.2 調用selectOne
接下來對1011數據進行查詢,查詢結果如下:
查看redis數據:
可以看到1011數據。
爲了證明是從緩存查出來的,我們直接修改緩存的數據,但是維持數據庫的數據。
修改後Redis變成陰天
數據庫還是晴天:
繼續調用查詢接口,結果如下:
這就證明了數據是從緩存裏面查出來的。
6.總結
到這裏一個相對完整的Redis+SpringBoot+SpringCache基礎項目搭建完畢。在搭建基礎項目方面好好利用idea本身的功能(模板項目+database)以及插件功能(EasyCode),能夠爲我們搭建項目提供極大便利,另外就是SpringCache,搭建這個項目不僅僅只用了Redis,也同時使用SpringCache達到快速實現緩存讀寫對象功能,固然只使用Redis也能達到同樣效果,但是開發者爲效率而生,能偷懶就偷懶,何況不是野生框架。
有的朋友在redis/cache配置可能會報錯:
com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Missing type id when trying to resolve subtype of [simple type, class java.lang.Object]: missing type id property '@class'
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject
指路:https://blog.csdn.net/qq_28540443/article/details/104731948