一、概述
? ? ? ? 上篇文章?系列九、SpringBoot + MyBatis + Redis實現分布式緩存?介紹了基于xml方式實現分布式緩存的效果,當前大家使用的技術棧基本是springboot+各種框架的組合,而springboot顯著的一個特點就是去xml配置,那么在無xml配置的情形下,又該如何實現分布式緩存呢?請看下面的代碼實戰
二、代碼實戰
2.1、分布式緩存相關的注解
? ? ? ? 基于注解方式的分布式緩存,主要涉及到如下幾個注解:
? ? ? ? (1)@EnableCaching:一般標注在配置類上,表示開啟Spring的緩存,如果不加此注解的話Spring自帶的緩存將不生效;
? ? ? ? (2)@CacheConfig(cacheNames = "xxx"):一般標注在service類上,用于配置cache的名字,建議以當前service類的全路徑名作為cache的名字;
? ? ? ? (3)@Cacheable:一般標識在service層的查詢方法上,表示將一個方法的返回值緩存起來, ?默認情況下,緩存的key就是方法的參數,緩存的value就是方法的返回值,如果查詢 方法無參數,則會使用默認的key,即SimpleKey [];
? ? ? ? (4)@CachePut(key = "#department.id"):一般加在service層的更新方法上(update),當數據庫中的數據更新后,緩存中的數據也要跟著更新,使用此注解,可以將方法的返回值 自動更新到已經存在的key上
? ? ? ? (5)@CacheEvict:一般加在service層的刪除方法上,當數據庫中的數據刪除后,相關的緩存也會被刪除,使用該注解的時候,也可以配置按照某種條件刪除(某種條件:@CacheEvict注解中的條件,例如:value、cacheNames、key、keyGenerator...)
2.2、項目概覽
2.3、pom
<dependencies><!-- springboot --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId></dependency><!-- 數據源 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.26</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.3.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 工具 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.21</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-collections4</artifactId><version>4.4</version></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.25</version></dependency></dependencies>
2.4、yml
server:port: 9999spring:redis:host: port: 6379database: 0password: 123456datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/20231018_redis?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMTusername: rootpassword: 123456mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: org.stat.entity.modelconfiguration:map-underscore-to-camel-case: truelogging:level:org:star:mapper: debug
2.5、主啟動
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 12:44* @Description:**/
@MapperScan(basePackages = "org.star.mapper")
@SpringBootApplication
public class SpringbootRedisDistributeCacheAnnotationApplication {public static void main(String[] args) {SpringApplication.run(SpringbootRedisDistributeCacheAnnotationApplication.class, args);}}
2.6、MyRedisConfig
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 15:28* @Description:* @EnableCaching的作用:開啟Spring的緩存,如果不加此注解的話Spring自帶的緩存將不生效**/
@EnableCaching
@Configuration
public class MyRedisConfig {/*** RedisTemplate k v 序列化** @param connectionFactory* @return*/@Beanpublic RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(connectionFactory);redisTemplate.setKeySerializer(RedisSerializer.string());redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.setHashKeySerializer(RedisSerializer.string());redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());redisTemplate.afterPropertiesSet();return redisTemplate;}@Beanpublic RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()// 設置默認的超時時間為2小時.entryTtl(Duration.ofHours(2)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))// 設置默認的緩存前綴.prefixCacheNameWith("REDIS_CACHE_");return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);}}
2.7、DepartmentDO
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 12:48* @Description:*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ToString(callSuper = true)
public class DepartmentDO implements Serializable {/*** 編號*/private Integer id;/*** 部門名稱*/private String departmentName;}
2.8、DepartmentMapper.java
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 12:50* @Description:*/
public interface DepartmentMapper {/*** 查詢所有部門* @return*/List<DepartmentDO> listAllDepartment();/*** 根據id查詢部門信息* @param id* @return*/DepartmentDO getDepartmentById(Integer id);/*** 根據id和departmentName查詢部門* @param id* @param departmentName* @return*/DepartmentDO getDepartment(Integer id,String departmentName);/*** 更新Department* @param department* @return*/int updateDepartment(DepartmentDO department);/*** 刪除部門* @param id*/void deleteDepartment(Integer id);
}
2.9、DepartmentMapper.xml
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="org.star.mapper.DepartmentMapper"><select id="listAllDepartment" resultType="org.star.entity.model.DepartmentDO">select id,department_name from department</select><select id="getDepartmentById" resultType="org.star.entity.model.DepartmentDO">select id,department_name from department where id = #{id}</select><select id="getDepartment" resultType="org.star.entity.model.DepartmentDO">select id,department_name from department where id = #{id} and department_name = #{departmentName}</select><update id="updateDepartment" useGeneratedKeys="true" keyProperty="id">update department set department_name = #{departmentName} where id = #{id}<selectKey resultType="org.star.entity.model.DepartmentDO" order="AFTER" keyProperty="id">select id,department_name from department where id = #{id}</selectKey></update><delete id="deleteDepartment">delete from department where id = #{id}</delete></mapper>
2.10、DepartmentService
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 20:00* @Description:* 基于注解的分布式緩存,redis中key的生成規則:${prefixCacheNameWith} + "_" + ${cacheNames} + "_" + ${key}* 說明:prefixCacheNameWith為RedisCacheManager中配置的前綴* 舉例:* (1)listAllDepartment ===> REDIS_CACHE_org.star.service.DepartmentService::SimpleKey []* (2)getDepartmentById ===> REDIS_CACHE_org.star.service.DepartmentService::1* (3)getDepartment ===> REDIS_CACHE_org.star.service.DepartmentService::SimpleKey [1,研發部]**/
@Service
@CacheConfig(cacheNames = "org.star.service.DepartmentService")
public class DepartmentService {@Resourceprivate DepartmentMapper departmentMapper;/*** @return* @Cacheable的作用:* @Cacheable注解一般加在查詢方法上,表示將一個方法的返回值緩存起來,* 默認情況下,緩存的key就是方法的參數,緩存的value就是方法的返回值,如果查詢* 方法無參數,則會使用默認的key,即SimpleKey []*/@Cacheablepublic List<DepartmentDO> listAllDepartment() {List<DepartmentDO> departments = departmentMapper.listAllDepartment();return departments;}/*** 對于只有一個參數的查詢方法,其key位id對應的值* @param id* @return*/@Cacheablepublic DepartmentDO getDepartmentById(Integer id) {return departmentMapper.getDepartmentById(id);}/**** 對于有多個參數的查詢方法,其key為所有的參數,如果想修改,可以單獨指定,例如:@Cacheable(key = "#id")* @param id* @param departmentName* @return*/@Cacheablepublic DepartmentDO getDepartment(Integer id,String departmentName) {return departmentMapper.getDepartment(id,departmentName);}/*** @CachePut作用:* @CachePut注解一般加在更新方法上(update),當數據庫中的數據更新后,緩存中的數據也要跟著更新,使用此注解,可以將方法的返回值* 自動更新到已經存在的key上,示例如下:* @param department* @return*/@CachePut(key = "#department.id")public DepartmentDO updateDepartment(DepartmentDO department) {departmentMapper.updateDepartment(department);return department;}/*** @CacheEvict()作用:* @CacheEvict()注解一般加在刪除方法上,當數據庫中的數據刪除后,相關的緩存也會被刪除,使用該注解的時候,也可以配置按照某種條件* 刪除(某種條件:@CacheEvict注解中的條件,例如:value、cacheNames、key、keyGenerator...)* @param id*/@CacheEvictpublic void deleteDepartment(Integer id) {departmentMapper.deleteDepartment(id);}}
2.11、DepartmentServiceTest
/*** @Author : 一葉浮萍歸大海* @Date: 2023/12/10 20:07* @Description:*/
@SpringBootTest
public class DepartmentServiceTest {@Resourceprivate DepartmentService departmentService;@Testpublic void listAllDepartmentTest() {List<DepartmentDO> departments1 = departmentService.listAllDepartment();System.out.println("departments1 = " + departments1);System.out.println("=============================");List<DepartmentDO> departments2 = departmentService.listAllDepartment();System.out.println("departments2 = " + departments2);}@Testpublic void getDepartmentByIdTest() {DepartmentDO department1 = departmentService.getDepartmentById(1);System.out.println("department1 = " + department1);System.out.println("========================");DepartmentDO department2 = departmentService.getDepartmentById(1);System.out.println("department2 = " + department2);}@Testpublic void getDepartmentTest() {DepartmentDO department1 = departmentService.getDepartment(1, "研發部");System.out.println("department1 = " + department1);System.out.println("============================");DepartmentDO department2 = departmentService.getDepartment(1, "研發部");System.out.println("department2 = " + department2);}@Testpublic void updateDepartmentTest() {DepartmentDO department = new DepartmentDO().setDepartmentName("研發部444").setId(1);DepartmentDO updatedDepartment = departmentService.updateDepartment(department);System.out.println("updatedDepartment = " + updatedDepartment);}@Testpublic void deleteDepartmentTest() {departmentService.deleteDepartment(1);}}