Spring Cache入門詳解

一、概述?

1.1緩存介紹

Spring提供了一套cache緩存抽象(注解/接口),使基于spring緩存的使用與實現解耦

  • 默認實現,Spring JDK ConcurrentMap-based?Cache
  • 第三方實現,caffeine/Ehcache/Redis等

https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache

Spring將緩存應用于方法,從而根據緩存中可用信息減少方法執行次數

即,每次調用目標方法時,spring基于緩存行為,檢查是否包含緩存數據。有則直接返回緩存結果,而不再實際調用目標方法。沒有則調用目標方法,并將方法返回結果置于緩存。從而下次調用目標方法時,基于緩存行為直接返回緩存結果

由于需要的數據對象已經保存在內存,從而可極大減少CPU執行/降低IO操作/減少數據庫請求等?

緩存數據,必須可重復使用;對于業務,緩存邏輯必須透明。即,不實際調用目標方法不會造成任何業務影響

Spring緩存基于AOP切面的實現

1.2SpringCache 特點

  • 通過少量的配置annotation 即可使得既有代碼支持緩存
  • 支持開箱即用Out-Of-The-Box, 即不用安裝和部署額外第三方組件即可使用緩存
  • 支持 Spring Express Language,能使用對象的任何屬性或者方法來定義緩存的key和condition
  • 支持AspectJ,并通過其實現任何方法的緩存支持
  • 支持自定義key和自定義緩存管理者,具有相當的靈活性和擴展性
  • 支持各種緩存實現,如對象,對象列表,默認基ConcurrentMap實現的 ConcurrentMapCache, 同時支持其他緩存實現

綜合來說,springCacho并不像正常緩存那樣存儲數和返回結果作為一個鍵值對存放在緩存中,等到下等數和返回結果作為一個新的參數來調用該方法時,將會把該方法參法,而是直接從緩存中獲取結果進行返回,從而實現緩存的效果

1.3幾個重要注解

@EnableCaching

@Cacheable

  • ·該注解用于標記緩存,就是對使用注解的位置進行緩存
  • ·該注解可以在方法或者類上進行標記,在類上標記時,該類所有方法都支持緩存

@Cacheable使用時通常搭配三個屬性使用

  • ·value,用來指定Cache的名稱,就是存儲哪個Cache上,簡單來說是cache的命名空間或者大的前綴
  • ·key,用于指定生成緩存對應的key,如果沒指定,則會默認策略生成key,也可以使用springEL編寫,默認是方法參數組合

// 使用@Cacheable注解對findUser方法進行緩存,緩存名稱為"users",緩存的key為用戶的id
@Cacheable(value="users", key="#user.id")
public User findUser(User user){return user;
}// 使用@Cacheable注解對findUser方法進行緩存,緩存名稱為"users",緩存的key為方法參數id
@Cacheable(value="users", key="#root.args[0]")
public User findUser(String id){return user;
}
  • condition,用來指定當前緩存的觸發條件,可以使用springEL編寫,如下代碼,則當user.id為偶數時才會觸發緩存 會觸發緩存
@Cacheable(value="users", key="#user.id",condition="#user.id%2==0")
public User findUser(User user){return user;
}

cacheManager

cacheManager, 用于指定當前方法或類使用緩存時的配置管理器,通過cacheManager的配置,可以為不同的方法使用不同的緩存策略,比如有的對象的緩存時間短,有的緩存的長,可以通過自定義配置 cacheManager來實現

@Configuration // 聲明這是一個配置類
@EnableCaching // 開啟緩存功能
public class SpringCacheConfig {@Bean // 定義一個Bean對象public CacheManager cacheManager() {// 創建Caffeine緩存管理器CaffeineCacheManager manager = new CaffeineCacheManager();// 創建緩存配置策略Cache<Object, Object> cache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 設置緩存過期時間為5秒.maximumSize(200) // 設置緩存最大容量為200個元素.build();// 為指定名稱的緩存,指定配置manager.registerCustomCache("user", cache);// 允許緩存值為空的鍵值對。避免緩存穿透manager.setAllowNullValues(true);// 將管理器注入容器,替換默認管理器對象return manager;}
}

@CachePut

該注解將標記的方法的返回值放入緩存中,使用方法與@Cacheable一樣,通常@CachePut放在數據更新的操作上,舉例來說,當getByUserid這樣一方法上使用了以userid為key的緩存時,如果更新了這條數據,該key對應的數據是不是要同步變更呢?


答案是肯的,于是,我們就需要在更新數據的地方添加@CachePut注解,當updateByUserid觸發之后,getByUserid上面的key對應的緩存對象數據也能同步變更

@CacheEvict

  • ·該注解用于清理緩存數據
  • ·使用在類上時,清除該類所有方法的緩存
  • ·@CacheEvict同樣擁有@Cacheable三個屬性,同時還有一個allEntries屬性,該屬性默認為false,當為true時,刪除該值所有緩存

@CacheEvict在實際使用中需要重點關注,比如一開始我們給用戶組,角色,部門等與用戶查詢相關的業務上面添加了key的時候,當一個userid對應的這條數據被清理的時候,那么關聯的key,即所說的用戶組,部門角色等關聯的用戶數據都需要一同清理

caching

  • 組合多個cache注解一起使用

  • 允許在同一方法上使用以上3個注解的組合

@CacheConfig

補充說明

以上簡單介紹了springcache中的幾個重要注解,以及各自的作用,通常來講,在開發過程中,使用springcache也就是在這些注解打交道,里面有一個點值得注意就是,關于方法級別上的key的使用規范和命名規范問題,這里可以關注和參考下springEL的寫法規范

二、與springboot的整合(redis版)

2.0應用場景

redis 應用場景
1.利用redis中字符串類型完成項目中手機驗證碼存儲的實現
2.利用redis中字符串類型完成具有失效性業務功能12306 淘寶 訂單還有:40分鐘
3.利用redis 分布式集群系統中Session共享 memcache 內存數據存儲上限 數據類型比較簡單 redis 內存 數據上限 數據類型豐富
4.利用redis 可排序set類型元素 分數排行榜之功能 dangdang 銷量排行 sales (商品id,商品銷量)
5.利用redis 分布式緩存 實現
6.利用redis存儲認證之后token信息 微信小程序 微信公眾號 用戶 openid令牌( token)超時
7.利用redis 解決分布式集群系統中分布式鎖問題 redistrict單線程序 n=20定義

2.1添加pom依賴

需要說明的是,springcache提供了多種緩存的實現,其中與redis的整合比較符合大家對redis的使用習慣,同時也更進一步了解springcache在redis中存儲的結構,因此這里需引入springboot-redis的依賴

 		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

2.2yml依賴

spring:application:name: redis-examplesjackson:default-property-inclusion: non_null # springmvc忽略空值屬性data:redis:host: port: password: database: 1cache: # 整合cache redis。代碼聲明詳細配置時無效redis:cache-null-values: true # 默認值,可省略。緩存空數據,避免緩存穿透time-to-live: 50000 # 單位毫秒,50秒logging:level:root: warncom:yanyu:springcache: debugpattern:console: '%-5level %C.%M[%line] - %msg%n'
server:port: 8083

2.3自定義cacheManager

import java.time.Duration;@Configuration
@EnableCaching
@Slf4j
public class SpringCacheConfig {// 按默認jdk序列化對象。未聲明全局配置,緩存數據永不過期/*@Beanpublic CacheManager cacheManager(RedisConnectionFactory cf) {// 全局配置RedisCacheConfiguration configG = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(120L));// 獨立配置RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(60L));RedisCacheManager manager = RedisCacheManager.builder(cf).cacheDefaults(configG).withCacheConfiguration("user", config).build();return manager;}*/// -----------------------------------// 持基于jackson的序列化,以及自定義緩存策略@Beanpublic CacheManager cacheManager(Jackson2JsonRedisSerializer<Object> serializer,RedisConnectionFactory cf) {// 全局配置:設置默認的緩存配置,包括過期時間和序列化方式RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig() // 獲取默認的緩存配置.entryTtl(Duration.ofMinutes(4)) // 設置緩存過期時間為4分鐘.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)); // 使用指定的序列化器進行序列化// 獨立的緩存配置:為特定的緩存區域設置配置,包括過期時間和序列化方式RedisCacheConfiguration userR = RedisCacheConfiguration.defaultCacheConfig() // 獲取默認的緩存配置.entryTtl(Duration.ofMinutes(2)) // 設置緩存過期時間為2分鐘.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)); // 使用指定的序列化器進行序列化// 基于全局配置/獨立配置,創建redis緩存管理器RedisCacheManager manager = RedisCacheManager.builder(cf).cacheDefaults(defaults) // 應用全局配置.withCacheConfiguration("user", userR) // 為"user"緩存區域應用獨立配置.build(); // 構建緩存管理器實例return manager; // 返回緩存管理器實例}// 全局jackson-redis序列化配置。可直接注入到容器覆蓋默認配置@Beanpublic Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {// 創建 ObjectMapper 對象,用于配置序列化和反序列化的規則ObjectMapper mapper = new ObjectMapper();// 設置序列化時忽略空值屬性mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);// 設置反序列化時忽略不存在的屬性,避免異常mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);// 設置屬性訪問權限為任意可見性mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 設置多態類型驗證器,允許子類作為父類處理PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().allowIfSubType(Object.class).build();// 激活默認的多態類型處理方式mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);// 設置日期時間序列化為 ISO 字符串格式,而非對象mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);// 注冊 JavaTimeModule,支持 Java 8 日期時間類型mapper.registerModule(new JavaTimeModule());// 創建 Jackson2JsonRedisSerializer 對象,并設置 ObjectMapper 對象Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(mapper, Object.class);return serializer;}// ------------------------------------@Bean("cum") // 定義一個名為"cum"的Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {// 創建 ObjectMapper 對象,用于配置序列化和反序列化的規則ObjectMapper objectMapper = new ObjectMapper();// 設置反序列化時忽略不存在的屬性,避免異常objectMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);// 設置序列化時忽略空值屬性objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);// 設置屬性訪問權限為任意可見性objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);// 按ISO字符串序列化/反序列化日期時間,而非對象objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);// 注冊 JavaTimeModule,支持 Java 8 日期時間類型objectMapper.registerModule(new JavaTimeModule());// 創建 Jackson2JsonRedisSerializer 對象,并設置 ObjectMapper 對象Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);// 創建 RedisTemplate 對象,并設置序列化器、連接工廠等屬性RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(serializer);redisTemplate.setValueSerializer(serializer);redisTemplate.setHashValueSerializer(serializer);redisTemplate.setHashKeySerializer(serializer);redisTemplate.setConnectionFactory(cf);return redisTemplate;}// ---------------------------------------------// 也可自定義RedisTemplate注入/*@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {Jackson2JsonRedisSerializer<Object> serializer =new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper objectMapper = new ObjectMapper();objectMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);PolymorphicTypeValidator ptv =BasicPolymorphicTypeValidator.builder().allowIfSubType(Object.class).build();objectMapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(objectMapper);RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setKeySerializer(serializer);redisTemplate.setValueSerializer(serializer);redisTemplate.setHashValueSerializer(serializer);redisTemplate.setHashKeySerializer(serializer);redisTemplate.setConnectionFactory(cf);return redisTemplate;}*//*@Beanpublic CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate,RedisConnectionFactory cf) {// 全局配置RedisCacheConfiguration defaults = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(4)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));// 獨立的緩存配置RedisCacheConfiguration userR = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(2)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));// 基于全局配置/獨立配置,創建redis緩存管理器RedisCacheManager manager = RedisCacheManager.builder(cf).cacheDefaults(defaults).withCacheConfiguration("user", userR).build();return manager;}*/// -------------------------------}

2.4搭建基本框架

見3.5

2.5測試

GET http://localhost:8083/api/users/1
###
GET http://localhost:8083/api/users/2
###PATCH http://localhost:8083/api/users
Content-Type: application/json{"id": "1","name": "小明","detail": "956"
}
###
DELETE http://localhost:8083/api/users/1
###
GET http://localhost:8083/api/users###
GET http://localhost:8083/api/userdtos/1###
POST http://localhost:8083/api/userdto/1

三、與springboot的整合(Caffeine版)

2.1Caffeine介紹

2.2添加依賴

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId></dependency>

2.3添加yml配置

spring:cache:caffeine: # 如果在代碼聲明配置并注入緩存管理器,此處配置無效spec: expireAfterWrite=240s, maximumSize=200 # 設置Caffeine緩存的配置參數,過期時間為240秒,最大容量為200個元素logging:level: # 設置日志級別root: warn # 根日志級別為警告級別com:example: debug # 對于com.example包下的日志級別為調試級別pattern: # 設置日志輸出格式console: '%-5level %C.%M[%line] - %msg%n' # 控制臺日志輸出格式,包括日志級別、類名、方法名、行號和日志信息

2.4自定義cacheManager

@Configuration // 聲明這是一個配置類
@EnableCaching // 開啟緩存功能
public class SpringCacheConfig {@Bean // 定義一個Bean對象public CacheManager cacheManager() {// 創建Caffeine緩存管理器CaffeineCacheManager manager = new CaffeineCacheManager();// 創建緩存配置策略Cache<Object, Object> cache = Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.SECONDS) // 設置緩存過期時間為5秒.maximumSize(200) // 設置緩存最大容量為200個元素.build();// 為指定名稱的緩存,指定配置manager.registerCustomCache("user", cache);// 允許緩存值為空的鍵值對。避免緩存穿透manager.setAllowNullValues(true);// 將管理器注入容器,替換默認管理器對象return manager;}
}

2.5搭建基礎框架

entity

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Address {private int id;private String detail;private User user;
}
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {private Long id;private String name;
}

mapper

@Repository // 標記為Spring框架的持久層組件
public class UserMapper {private static final List<User> USERS; // 靜態用戶列表static { // 靜態代碼塊,用于初始化用戶列表User u1 = User.builder().id(1L).name("BO").build(); // 創建用戶1User u2 = User.builder().id(2L).name("SUN").build(); // 創建用戶2USERS = new ArrayList<>(); // 初始化用戶列表USERS.add(u1); // 添加用戶1到列表USERS.add(u2); // 添加用戶2到列表}// 根據用戶ID獲取用戶信息public User getUser(long uid) {return USERS.stream() // 使用Java 8的Stream API進行過濾操作.filter(u -> u.getId() == uid) // 過濾出ID等于給定ID的用戶.findFirst() // 獲取第一個匹配的用戶(如果存在).orElse(null); // 如果沒有匹配的用戶,返回null}// 更新用戶信息public User updateUser(User user) {for (int i = 0; i < USERS.size(); i++) { // 遍歷用戶列表if (Objects.equals(user.getId(), USERS.get(i).getId())) { // 如果找到匹配的用戶IDUSERS.set(i, user); // 更新用戶信息}}return user; // 返回更新后的用戶信息}// 獲取所有用戶信息public List<User> listUsers() {return USERS; // 返回用戶列表}
}

service

@Service // 標記為Spring框架的服務層組件
@Slf4j // 使用Lombok提供的日志功能
public class UserService {@Autowired // 自動注入UserMapper對象private UserMapper userMapper;@Cacheable(value = "user", key = "#uid") // 緩存用戶信息,key為用戶IDpublic User getUser(long uid) {User user = userMapper.getUser(uid); // 從UserMapper獲取用戶信息log.debug("called UserService getUser() user: {}", user); // 記錄調試日志return user; // 返回用戶信息}@Cacheable(value = "users") // 緩存所有用戶信息public List<User> listUsers() {return userMapper.listUsers(); // 從UserMapper獲取所有用戶信息}@CachePut(value = "user", key = "#user.id") // 更新緩存中的用戶信息// 以鍵值對緩存一個集合對象時,緩存對象是一個整體。無法修改其中某一個元素// 因此清空整個集合緩存@CacheEvict(value = "users", allEntries = true)public User updateUser(User user) {User u = userMapper.updateUser(user); // 更新用戶信息log.debug("updateUser(), user: {}", u); // 記錄調試日志return user; // 返回更新后的用戶信息}@CacheEvict(value = "user", key = "#uid") // 刪除緩存中的用戶信息public void delUser(long uid) {// 從緩存刪除,沒有調用模擬的持久層刪除// 因此會實際調用getUser()方法,重新從持久層獲取}
}

controller

@RestController
@RequestMapping("/api/")
public class MyController {@Autowiredprivate UserService userService;@GetMapping("users/{uid}")public User getUser(@PathVariable long uid) {return userService.getUser(uid);}@GetMapping("users")public List<User> listUsers() {return userService.listUsers();}@PatchMapping("users")public User patchUser(@RequestBody User user) {return userService.updateUser(user);}@DeleteMapping("users/{uid}")public void delUser(@PathVariable long uid) {userService.delUser(uid);}
}

測試

GET http://localhost:8081/api/users/1
###
GET http://localhost:8081/api/users/2
###PATCH http://localhost:8081/api/users
Content-Type: application/json{"id": "1","name": "LIU"
}
###
DELETE http://localhost:8081/api/users/1
###
GET http://localhost:8081/api/users

四、Ecache緩存

4.1Ecache緩存

  • 基于Java的開源的使用最廣泛的緩存組件;
  • 使用簡單,一個jar包,簡單配置,即可使用;
  • 可以進程內緩存(內存),也可以進程外緩存(磁盤上持久化);
  • 目前已經有三個系列版本,1.x(已經過時不用)、2.x和3.x;
  • 著名的Hibernate、Shiro里面的緩存就采用了Ecache;
  • Ecache還可以支持集群;

4.2使用場景

  • 比較適合緩存一些不經常改變的數據;
  • 對數據實時性要求不高的場景,多臺應用服務器中的緩存是不能進行實時同步的;
  • 可以作為其他緩存如Redis的輔助方案,比如做Redis的二級緩存,作為Redis緩存宕機導致大量請求讀數據庫的解決方案;

4.3使用

添加相關依賴

<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache --><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.10.4</version></dependency>

ehchche配置,關于ehcache的使用,大家可自行查找一下相關資料補習一下,用起來很簡單,只要xml的配置文件沒問題就可以,更深入的其實都在hcache的配置文件中,作為本地的堆緩存,在應對數據量不是特別大的場景,使用ehcache是個不錯的選擇,一般是配合redis和其他的緩存工具以一起使用,這里直接貼上,提供參考,

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="false"><!-- diskStore:ehcache其實是支持內存+磁盤+堆外內存,幾個層級的緩存 --><!-- 在這里設置一下,但是一般不用的 ,這里是基于磁盤的緩存--><diskStore path="java.io.tmpdir/Tmp_EhCache" /><!-- defaultCache,是默認的緩存策略 --><!-- 如果你指定的緩存策略沒有找到,那么就用這個默認的緩存策略 --><!-- external:如果設置為true的話,那么timeout就沒有效果,緩存就會一直存在,一般默認就是false --><!-- maxElementsInMemory:內存中可以緩存多少個緩存條目,在實踐中,你是需要自己去計算的,比如你計算你要緩存的對象是什么?有多大?最多可以緩存多少MB,或者多少個G的數據?除以每個對象的大小,計算出最多可以放多少個對象 --><!-- overflowToDisk:如果內存不夠的時候,是否溢出到磁盤 --><!-- diskPersistent:是否啟用磁盤持久化的機制,在jvm崩潰的時候和重啟之間,不用 --><!-- timeToIdleSeconds:對象最大的閑置的時間,如果超出閑置的時間,可能就會過期,我們這里就不用了,緩存最多閑置5分鐘就被干掉了 --><!-- timeToLiveSeconds:對象最多存活的時間,我們這里也不用,超過這個時間,緩存就過期,就沒了 --><!-- memoryStoreEvictionPolicy:當緩存數量達到了最大的指定條目數的時候,需要采用一定的算法,從緩存中清除一批數據,LRU,最近最少使用算法,最近一段時間內,最少使用的那些數據,就被干掉了 --><defaultCacheeternal="false"maxElementsInMemory="1000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="300"timeToLiveSeconds="0"memoryStoreEvictionPolicy="LRU" /><!-- 手動指定的緩存策略 --><!-- 比如你一個應用吧,可能要緩存很多種不同的數據,比如說商品信息,或者是其他的一些數據 --><!-- 對不同的數據,緩存策略可以在這里配置多種 --><cachename="local"  eternal="false"maxElementsInMemory="1000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="300"timeToLiveSeconds="0"memoryStoreEvictionPolicy="LRU" /><!-- ehcache這種東西,簡單實用,是很快速的,1小時上手可以用在項目里了,沒什么難度的 -->   <!-- ehcache這個技術,如果講深了,里面的東西還是很多的,高級的feature,但是我們這里就不涉及了 -->  </ehcache>

接下來是ehcache的配置類和redis的配置類,springbooti在啟動的時候會自動將這兩個配置類納入全局的bean容器管理中,

/*** 本地堆緩存配置類* @author asus*/
@Configuration
@EnableCaching
public class EhcacheConfig {@Beanpublic EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean();cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));cacheManagerFactoryBean.setShared(true);return cacheManagerFactoryBean;}@Beanpublic EhCacheCacheManager eCacheCacheManager(EhCacheManagerFactoryBean bean) {return new EhCacheCacheManager(bean.getObject());}
}
/*** redis序列化bean* @author asus**/
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate(factory);//定義value的序列化方式Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);template.setValueSerializer(jackson2JsonRedisSerializer);template.setHashValueSerializer(jackson2JsonRedisSerializer);template.afterPropertiesSet();return template;}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19378.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19378.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19378.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Postman快捷功能-快速填寫請求頭

大家好&#xff0c;之前給大家分享關于 Postman 工具的基礎使用&#xff0c;今天給大家介紹一個快捷功能&#xff0c;可以一定程度提高我們使用 Postman 工具的效率&#xff0c;在我們進行接口測試時&#xff0c;幾乎每個接口都需要填寫 Headers&#xff0c;且 Headers 中的參數…

【ai】livekit服務本地開發模式2:模擬1個發布者

是一個會議用軟件:LiveKit is an open source project that provides scalable, multi-user conferencing based on WebRTC. It’s designed to provide everything you need to build real-time video audio data capabilities in your applications.LiveKit’s server is wr…

【Python】 Django 框架如何支持百萬級日訪問量

基本原理 Django 是一個高級的 Python Web 框架&#xff0c;它鼓勵快速開發和干凈、實用的設計。Django 遵循 MVC&#xff08;模型-視圖-控制器&#xff09;設計模式&#xff0c;允許開發者通過編寫更少的代碼來構建高質量的 Web 應用程序。Django 自帶了許多內置功能&#xf…

發現沒:隨便搞個B端頁面,就想在客戶那里過關,難啦。

客戶對B端界面要求越來越高的原因可以有以下幾點&#xff1a; 用戶體驗要求提升&#xff1a;隨著用戶對移動應用和網頁的使用經驗增加&#xff0c;他們對于界面的交互、流暢性和易用性要求也越來越高。他們希望能夠在使用B端應用時&#xff0c;能夠快速、方便地完成任務&#…

2024年華為OD機試真題-文本統計分析-C++-OD統一考試(C卷D卷)

題目描述: 有一個文件, 包含以一定規則寫作的文本, 請統計文件中包含的文本數量 規則如下 1. 文本以";"分隔,最后一條可以沒有";",但空文本不能算語句,比如"COMMAND A; ;"只能算一條語句. 注意, 無字符/空白字符/制表符都算作"空&qu…

設計模式詳解(六):適配器模式——Adapter

目錄導航 適配器模式及其作用現實生活舉例 適配器模式的好處適配器模式的實現關系圖實現步驟 適配器模式的適用場景適配器模式示例 適配器模式及其作用 適配器模式是一種結構型設計模式。所謂結構型是指在代碼結構方面的設計模式。適配器模式作為中間層&#xff0c;可以讓交互…

Vue3 圖片或視頻下載跨域或文件損壞的解決方法

Vue3 圖片或視頻下載跨域或文件損壞的解決方法 修改跨域配置文件下載方法 修改跨域配置文件 修改vite.config.ts文件proxy里面寫跨域地址&#xff0c;如下圖&#xff0c;圖片地址就是我們要跨域的目標地址&#xff1a; 下載方法 如下就是我取消上面那句后的報錯 然后調用兩…

【C++風云錄】C++與智能交通:智能交通系統與車聯網

解鎖C的力量&#xff1a;在智能交通系統與車聯網中使用關鍵庫 前言 本文關注于C在智能交通系統與車聯網中的應用&#xff0c;并提供了五個常見庫的簡介和使用方法。這些庫包括&#xff1a;Veins, SUMO-GUI, OMNeT, NS-3和PLEXE&#xff0c;每個庫都有其獨特的功能和優點&…

【Java】Sping Boot中使用Javax Bean Validation

目錄 Javax Bean Validation在Spring Boot中集成Javax Bean Validation使用案例功能測試配置全局異常處理器重新測試返回特定形式的信息方式一方式二 附&#xff1a;常用的注解 Javax Bean Validation Javax Bean Validation是Java平臺的一項規范&#xff0c;旨在提供一種簡單…

想知道股指期貨和期權有什么不同嗎?

市場上目前有中金所的滬深300ETF&#xff0c;中證500和中證1000股指期貨&#xff0c;期權市場有上證50ETF&#xff0c;滬深300etf和中證500ETF期權&#xff0c;股指期貨和期權在買賣雙方的權利義務、風險收益特征、保證金制度、上市合約數量等方面均有較大區別&#xff0c;下文…

每天學點小知識:Windows終端Powershell美化

前言 本章的旨在教會你美化自己的終端&#xff0c;powershell需要以管理員運行 經過我的測試&#xff0c;不同的電腦可能會有不同的報錯&#xff0c;具體操作根據官方為主https://ohmyposh.dev/docs 效果展示 Oh My Posh&#xff1a;提供美觀的 PowerShell 提示符主題 1.安裝…

揭秘CISA:你不知道的信息安全認證,輕松掌握職場先機!

在當今的信息化時代&#xff0c;信息系統的安全和穩定是企業和組織的重要資產。信息系統審計是一項專業的工作&#xff0c;需要具備豐富的知識和經驗&#xff0c;以及敏銳的洞察力和判斷力。信息系統審計師是信息系統審計領域的專業人士&#xff0c;他們負責對信息系統的設計、…

MVVM模式的優點以及與MVC模式的區別?

1.MVVM 模式的優點&#xff1a; 1 、低耦合&#xff1a; 視圖&#xff08; View &#xff09;可以獨?于 Model 變化和修改&#xff0c;?個 ViewModel 可以綁定到不同的 "View" 上&#xff0c;當 View 變化的時候 Model 可以不變&#xff0c;當 Model 變化的時…

【OpenGL實踐12】關于緩存區Framebuffer的運用

文章目錄 一、說明二、幀緩沖區三、創建新的幀緩沖區四、附屬裝飾4.1 紋理圖像4.2 渲染緩沖區對象圖像 五、使用幀緩沖區5.1 后期處理5.2 更改代碼 六、后期處理效果6.1 色彩處理6.2 模糊6.3 Sobel算子 七、結論練習 一、說明 關于FrameBuffer的使用&#xff0c;是OpenGL的高級…

橫截面分位數回歸

一、分位數回歸簡介 分位數回歸&#xff08;英語&#xff1a;Quantile regression&#xff09;是回歸分析的方法之一。最早由Roger Koenker和Gilbert Bassett于1978年提出。一般地&#xff0c;傳統的回歸分析研究自變量與因變量的條件期望之間的關系&#xff0c;相應得到的回歸…

AI時代的服裝設計師--AIGC

AI時代的服裝設計師--AIGC AIGCAIGC設計能替代真正的設計師嗎森馬T恤設計AIGC優勢、優化 本文記錄于去年參加的一次森馬T恤設計活動的感受。 AIGC 可以說&#xff0c;近期以來&#xff0c;隨著ChatGPT的不斷發展&#xff0c;從ChatGPT-3到ChatGPT-4的飛速發展&#xff0c;AIGC…

Windows和Linux系統部署Docker(2)

目錄 一、Linux系統部署docker 前置環境&#xff1a; 1.安裝需要的軟件包&#xff0c; yum-util 提供yum-config-manager功能 2.添加阿里云 docker-ce 倉庫 3.安裝docker軟件包 4.啟動 docker并設置開機自啟 5.查看版本&#xff1a; 二、windows系統部署docker 1.查看…

Type ‘null‘ is not assignable to type ‘T‘. - ArkTSCheck

設置泛型將參數配置為 null 時拋出了如下異常: Type null is not assignable to type T. T could be instantiated with an arbitrary type which could be unrelated to null. <ArkTSCheck> 解決辦法 在 null 后面添加 ! 即可,以表示該值不會為 null data: T null! 以…

Qt 基于FFmpeg的視頻轉換器 - 轉GIF動圖

Qt 基于FFmpeg的視頻轉換器 - 轉GIF動圖 引言一、設計思路二、核心源碼三、參考鏈接 引言 gif格式的動圖可以通過連續播放一系列圖像或視頻片段來展示動態效果&#xff0c;使信息更加生動形象&#xff0c;可以很方便的嵌入到網頁或者ppt中。上圖展示了視頻的前幾幀轉為gif動圖的…

隨身WIFI 路由器等嵌入式Linux 編程器固件解包打包

一、固件提取 (1)直接使用CH341A編程器提取全量包,適合于無adb場景 (2)使用adb 、dd工具提取rootfs分區,通常是mtd4,一般我們也只需要修改rootfs 二、firmware 分析 Ubuntu18.04及以上,低版本需解決很多依賴問題 安裝python3和pyhton3-pip 1.下載工具 ?git clo…