目錄
3 SpringBoot(2.0.3.RELEASE)+Redis項目測試準備
1 準備redis
1.1 docker安裝redis(centos7上安裝)
docker pull redis
查看鏡像是否下載成功:docker images
1.2 啓動redis
docker run -d -p 6379:6379 --name myredis docker.io/redis
-d:表示後臺啓動
-p:表示端口,6379:6379表示將 虛擬機的6379端口映射爲容器的6379端口
--name myredis docker.io/redis:命名,將要啓動的docker.io/redis命名爲myredis
查看是否啓動成功:docker ps
2 SpringBoot與緩存
2.1 JSR107緩存規範
Java Caching定義了5個核心接口,分別是CachingProvider、CacheManager、Cache、Entry、Expiry。
a)CachingProvider定義了創建、配置、獲取、管理和控制多個CacheManager。一個應用可以在運行期訪問多個CachingProvider。
b)CacheManager定義了創建、配置、獲取、管理和控制多個唯一命名的Cache,這些Cache存在於CacheManager的上下文中。一個CacheManager僅被一個CachingProvider所擁有。
c)Cache是一個類似Map的數據結構並臨時存儲以Key爲索引的值。一個Cache僅被一個CacheManager所擁有。
d)Entry是一個存儲在Cache中的key-value對。
e)Expiry 每一個存儲在Cache中的條目有一個定義的有效期。一旦超過這個時間,條目爲過期的狀態。一旦過期,條目將不可訪問、更新和刪除。緩存有效期可以通過ExpiryPolicy設置。
2.2 Spring緩存抽象
Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口來統一不同的緩存技術;並支持使用JCache(JSR-107)註解簡化開發。
Cache接口爲緩存的組件規範定義,包含緩存的各種操作集合。
Cache接口下Spring提供了各種xxxCache的實現;如RedisCache、EhCacheCache 、JCacheCache、ConcurrentMapCache...
每次調用需要緩存功能的方法時,Spring會檢查檢查指定參數的指定的目標方法是否已經被調用過;如果有就直接從緩存中獲取方法調用後的結果,如果沒有就調用方法並緩存結果後返回給用戶,下次調用直接從緩存中獲取。
使用Spring緩存抽象時我們需要關注兩點:確定方法需要被緩存以及他們的緩存策略、從緩存中讀取之前緩存存儲的數據。
2.3 概念&緩存註解
Cache:緩存接口,定義緩存操作,實現有RedisCache、EhCacheCache、JCacheCache、ConcurrentMapCache等。
CacheManager:緩存管理器,管理各種緩存組件。
keyGenerator:緩存數據時key的生成策略。
serialize:緩存數據時value序列化策略。
@EnableCaching:開啓基於註解的緩存。
@CacheConfig:可以抽取緩存公共配置,標註在類上。
@Cacheable:主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存。
@CachePut:保證方法被調用,又希望結果被緩存。(更新操作)
@CacheEvict:清空緩存。(刪除操作)
@Caching:組合註解,可根據需要配置多個@Cacheable、@CachePut、@CacheEvict註解。
@Cacheable/@CachePut/@CacheEvict主要的參數如下:
3 SpringBoot(2.0.3.RELEASE)+Redis項目測試準備
3.1 主要pom依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--數據庫相關-->
<!--mybatis-spring-boot-starter,mybatis依賴-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
3.2 yml文件配置
spring:
redis:
host: 192.168.219.141 # redis服務器地址
port: 6379 # 默認端口就是6379 可不配置
#password: root # redis服務器連接密碼(默認爲空)
database: 0 #使用Redis0號庫
jedis:
pool:
max-active: 8 # 連接池最大連接數(如果配置爲<=0,則沒有限制)
datasource:
url: jdbc:mysql://127.0.0.1:3306/spring_cache?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: root
mybatis:
configuration:
map-underscore-to-camel-case: true
logging:
level:
com:
cache:
mapper: DEBUG
3.3 數據庫測試表
CREATE TABLE `employee` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`lastName` VARCHAR(255) DEFAULT NULL,
`email` VARCHAR(255) DEFAULT NULL,
`gender` INT(2) DEFAULT NULL,
`d_id` INT(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8
三條測試數據:
4 Redis測試
4.1 @Cacheable註解
@Cacheable(cacheNames = "emp", key = "#id")
// @Cacheable(cacheNames = "emp", key = "#id",condition = "#id > 1")
//@Cacheable(cacheNames = "emp", key = "#id",unless = "#result == null")
public Employee getEmp(Integer id) {
System.out.println("查詢" + id + "號員工");
return employeeMapper.getEmpById(id);
}
說明:
@Cacheable(cacheNames = "emp", key = "#id"):
將查詢到的Employee存入緩存,key就是傳入的id。
@Cacheable(cacheNames = "emp", key = "#id",condition = "#id > 1"):
condition爲緩存的條件,當condition 判斷爲true時,緩存結果,即當員工的id大於1時,纔會緩存對應的結果。condition在方法執行之前之後都可以進行判斷。
@Cacheable(cacheNames = "emp", key = "#id",unless = "#result == null"):
unless用於否決緩存,只有當條件爲false時纔會緩存。並且,該表達式只在方法執行之後判斷。經過測試,默認就算不設置unless 條件,結果爲null也是不允許緩存的。
4.2 @CachePut註解
@CachePut(value = "emp", key = "#result.id") //返回結果中的ID
// @CachePut(value = "emp",key="#employee.id") //參數中的ID
public Employee updateEmp(Employee employee) {
System.out.println("employee:" + employee);
employeeMapper.update(employee);
return employeeMapper.getEmpById(employee.getId());
}
說明:
第一種方式,將結果中的ID作爲key。先緩存3號員工信息,窄將3號員工Eric的lastname改爲Andy,以下分別爲更新前和更新後的緩存。
第二種方式是將參數中的ID作爲key,測試發現最終的效果是一樣的,更新員工後,緩存也更新了。
4.3 @CacheEvict註解
// key 清除指定的數據
// beforeInvocation默認爲false,方法執行之後再刪除緩存;
// beforeInvocation爲true:方法執行之前刪除緩存,不管方法成不成功
@CacheEvict(value = "emp", key = "#id",beforeInvocation = false)
//allEntries默認爲false,當爲true,那麼emp緩存全部刪除
// @CacheEvict(value = "emp",allEntries = true,beforeInvocation = false)
public Integer deleteEmp(Integer id) {
Integer result = employeeMapper.deleEmpById(id);
System.out.println("刪除" + id + "號員工");
return result;
}
說明:
@CacheEvict主要就是兩個配置比較重要。 beforeInvocation默認爲false,方法執行之後再刪除緩存;beforeInvocation爲true,方法執行之前刪除緩存,不管方法成不成功。
allEntries默認爲false,當爲true,那麼對應的緩存全部刪除。
4.4 @Caching註解
// 組合註解
@Caching(
cacheable = {
@Cacheable(value = "emp", key = "#lastName")
},
put = {
@CachePut(value = "emp", key = "#result.id"),
@CachePut(value = "emp", key = "#result.email")
}
)
public Employee getEmpByLastname(String lastName) {
Employee employee = employeeMapper.getByLastname(lastName);
System.out.println("通過lastname查詢");
return employee;
}
說明:
@Caching源碼如下:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}
@Caching是個組合註解,可以通過這個註解來配置不同的緩存,比如示例代碼中,通過lastname查詢,可以根據查詢結果設置不同的緩存,也可以根據查詢結果更新緩存,按自己的需要進行組合配置。
通過lastname=Andy查詢,通過結果可以發現@Caching生效了。
5 SpringBoot Redis緩存序列化處理
SpringBoot RedisTemplate是用來操作key-value對象類型,默認採用JDK序列化類型。JDK序列化性能差,而且數據是以二進制的形式(如下圖所示)存儲在Redis服務端,不便於查詢,同時對象還需要實現Serializable接口。
如果要直接查看緩存數據,會比較困難,我們更希望數據以json的形式存儲。
RedisSerializer有很多種實現,默認是使用的Jdk序列化方式,那麼可以通過使用Jackson2JsonRedisSerializer來實現新的序列化。
更改默認的序列化器:
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheManager redisCacheManager =
RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(defaultCacheConfig(10000))
.withInitialCacheConfigurations(initCacheConfigMap())
.transactionAware()
.build();
return redisCacheManager;
}
private RedisCacheConfiguration defaultCacheConfig(Integer second) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer
= new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
RedisCacheConfiguration redisCacheConfiguration
= RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(second))
.serializeKeysWith(RedisSerializationContext
.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext
.SerializationPair
.fromSerializer(jackson2JsonRedisSerializer)
).disableCachingNullValues();
return redisCacheConfiguration;
}
// 不同緩存的過期時間,可以自定義配置
private Map<String, RedisCacheConfiguration> initCacheConfigMap() {
Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
// 自己按需配置
configMap.put("User", this.defaultCacheConfig(1000));
configMap.put("User1", this.defaultCacheConfig(1000));
configMap.put("User2", this.defaultCacheConfig(1000));
configMap.put("User3", this.defaultCacheConfig(1000));
return configMap;
}
}
6 總結
堅持寫博客!堅持寫博客!堅持寫博客!(其實已經很久沒有寫了)