? ? ? ? 上篇文章?系列九、SpringBoot + MyBatis + Redis实现分布式缓存?介绍了基于xml方式实现分布式缓存的效果,当前大家使用的技术栈基本是springboot+各种框架的组合,而springboot显著的一个特点就是去xml配置,那么在无xml配置的情形下,又该如何实现分布式缓存呢?请看下面的代码实战
? ? ? ? 基于注解方式的分布式缓存,主要涉及到如下几个注解:
? ? ? ? (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...)
<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>
server:
port: 9999
spring:
redis:
host:
port: 6379
database: 0
password: 123456
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/20231018_redis?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=GMT
username: root
password: 123456
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: org.stat.entity.model
configuration:
map-underscore-to-camel-case: true
logging:
level:
org:
star:
mapper: debug
/**
* @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);
}
}
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/12/10 15:28
* @Description:
* @EnableCaching的作用:开启Spring的缓存,如果不加此注解的话Spring自带的缓存将不生效
*
*/
@EnableCaching
@Configuration
public class MyRedisConfig {
/**
* RedisTemplate k v 序列化
*
* @param connectionFactory
* @return
*/
@Bean
public 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;
}
@Bean
public 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);
}
}
/**
* @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;
}
/**
* @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);
}
<!DOCTYPE mapper
PUBLIC "-//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>
/**
* @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 {
@Resource
private DepartmentMapper departmentMapper;
/**
* @return
* @Cacheable的作用:
* @Cacheable注解一般加在查询方法上,表示将一个方法的返回值缓存起来,
* 默认情况下,缓存的key就是方法的参数,缓存的value就是方法的返回值,如果查询
* 方法无参数,则会使用默认的key,即SimpleKey []
*/
@Cacheable
public List<DepartmentDO> listAllDepartment() {
List<DepartmentDO> departments = departmentMapper.listAllDepartment();
return departments;
}
/**
* 对于只有一个参数的查询方法,其key位id对应的值
* @param id
* @return
*/
@Cacheable
public DepartmentDO getDepartmentById(Integer id) {
return departmentMapper.getDepartmentById(id);
}
/**
*
* 对于有多个参数的查询方法,其key为所有的参数,如果想修改,可以单独指定,例如:@Cacheable(key = "#id")
* @param id
* @param departmentName
* @return
*/
@Cacheable
public 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
*/
@CacheEvict
public void deleteDepartment(Integer id) {
departmentMapper.deleteDepartment(id);
}
}
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/12/10 20:07
* @Description:
*/
@SpringBootTest
public class DepartmentServiceTest {
@Resource
private DepartmentService departmentService;
@Test
public 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);
}
@Test
public 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);
}
@Test
public 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);
}
@Test
public void updateDepartmentTest() {
DepartmentDO department = new DepartmentDO().setDepartmentName("研发部444").setId(1);
DepartmentDO updatedDepartment = departmentService.updateDepartment(department);
System.out.println("updatedDepartment = " + updatedDepartment);
}
@Test
public void deleteDepartmentTest() {
departmentService.deleteDepartment(1);
}
}