Spring Cache缓存框架
**Spring Cache** 是Spring框架提供的一种缓存抽象机制,用于简化应用中的缓存操作。它通过将方法的返回值缓存起来,当下次调用同一方法时,如果传入的参数与之前的调用相同,就可以直接从缓存中获取结果,而不需要再执行方法体中的代码,从而提高系统的性能和响应速度。
Spring Cache的特点
-
声明式缓存 :通过在方法上添加注解,如
@Cacheable
、@CachePut
、@CacheEvict
等来声明缓存的行为,无需手动编写缓存代码。 -
多种缓存支持 :Spring Cache提供了对多种缓存框架的支持,包括Redis、Ehcache、Guava Cache、Caffeine等,可以根据需要选择合适的缓存实现。
-
缓存策略配置 :可以通过配置文件或者编程方式来配置缓存的策略,包括缓存的过期时间、缓存的淘汰策略等。
-
注解灵活应用:通过在方法上添加不同的注解,可以实现缓存的读取、更新和清除等操作,根据业务需求进行灵活配置。
-
缓存切面自动代理:Spring Cache通过AOP技术,利用代理模式在方法执行前后拦截,自动处理缓存相关的操作,对业务代码无侵入。
Spring Cache的核心概念和原理
Spring Cache利用AOP(面向切面编程)实现了基于注解的缓存功能。它通过在方法上添加注解来声明缓存行为,Spring Cache会在方法执行前后进行拦截,检查缓存中是否有对应的数据。如果有,则直接返回缓存结果;如果没有,则执行方法并将结果存入缓存。
Spring Cache的使用方法
Spring Cache提供了注解式和编程式两种使用方式:
-
注解式 :通过在方法上添加
@Cacheable
、@CacheEvict
等注解来声明缓存行为。例如,@Cacheable(value = "users", key = "#id")
表示将方法的返回值缓存到名为"users"的缓存中,缓存的key为方法的参数id。 -
编程式 :通过编程方式使用
CacheManager
接口来操作缓存。例如,获取或设置缓存的值等操作
常用注解
注解 | |
---|---|
@EnableCaching | 开启缓存功能,通常加在启动类上 |
@Cacheable | 在方法执行之前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法的返回值放到缓存中 |
@Cacheput | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
详解
@EnableCaching
@EnableCaching
是 Spring Framework 中用于启用缓存支持的一个注解。当您在 Spring Boot 应用程序的主类或其他配置类上使用此注解时,Spring 会自动检测并配置缓存相关的 Bean,以便您可以在应用程序中使用缓存功能。
作用
-
启用缓存抽象 :
@EnableCaching
注解告诉 Spring 容器启用缓存抽象,这包括解析缓存注解(如@Cacheable
、@CachePut
、@CacheEvict
等)以及注册必要的缓存基础设施 Bean(如CacheManager
)。 -
自动配置 :在 Spring Boot 应用程序中,
@EnableCaching
注解通常与自动配置一起使用,以简化缓存的配置过程。Spring Boot 会自动检测添加的缓存依赖(如 Caffeine、EhCache、Redis 等),并配置相应的 CacheManager。
@Cacheable
用于标记那些其返回值是可以被缓存的方法。当带有 @Cacheable
注解的方法被调用时,Spring Cache 会先检查指定的缓存中是否已经存在该方法的返回值。如果存在,则直接返回缓存中的值,避免重复计算;如果不存在,则调用该方法,将返回值缓存起来,并返回该值。
属性详解
-
value
或cacheNames
:指定缓存的名称。可以使用数组或逗号分隔的字符串来指定多个缓存。 -
key
:指定缓存的键。默认为方法参数。可以使用 SpEL(Spring Expression Language)表达式来动态生成键。 -
condition
:指定一个条件表达式,用于决定缓存是否生效。如果表达式的结果为false
,则不缓存方法的返回值。 -
unless
:指定一个条件表达式,用于在方法执行后决定是否缓存返回值。如果表达式的结果为true
,则不缓存方法的返回值。
@Cacheput
用于更新缓存中的值。与 @Cacheable
注解不同,@CachePut
注解的方法每次都会被调用,并将返回值放入指定的缓存中。这通常用于那些每次调用都需要更新缓存的场景。
属性详解(属性与 @Cacheable相似**)**
-
value
或cacheNames
:指定缓存的名称。可以使用数组或逗号分隔的字符串来指定多个缓存。 -
key
:指定缓存的键。默认为方法参数。可以使用 SpEL(Spring Expression Language)表达式来动态生成键。 -
condition
:指定一个条件表达式,用于决定缓存是否生效。如果表达式的结果为false
,则不更新缓存。
@CacheEvict
用于从缓存中移除数据。它通常用于在数据发生变化时(如数据被删除或更新)确保缓存的一致性。
注解详解
-
value
或cacheNames
:指定要移除数据的缓存名称。可以使用数组或逗号分隔的字符串来指定多个缓存。 -
key
:指定要移除数据的缓存键。默认为方法参数。可以使用 SpEL(Spring Expression Language)表达式来动态生成键。 -
allEntries
:指定是否移除缓存中的所有数据。如果设置为true
,则忽略key
属性,移除指定缓存中的所有条目。 -
beforeInvocation
:指定是否在方法执行前移除缓存数据。默认为false
,即在方法执行后移除。如果设置为true
,则在方法执行前移除缓存数据,这可以确保在方法执行过程中不会访问到旧的缓存数据。 -
condition
:指定一个条件表达式,用于决定缓存移除是否生效。如果表达式的结果为false
,则不移除缓存数据。
Spring 的表达式语言
**Spring Cache中的SpEL表达式(Spring Expression Language)用于动态生成缓存键、条件判断和排除策略等**
常见用法
-
属性访问 :使用
.
运算符访问对象的属性,如person.name
。 -
方法调用 :使用
()
运算符调用对象的方法,如person.getName()
。如果方法没有参数,括号可以省略,如person.isAdult
。 -
在SpEL表达式中,可以使用
#
符号引用变量,如#myVar
。
SpEL表达式可以基于上下文环境,使用缓存抽象提供与root对象相关的内置参数下面给出例子:
-
#root.methodName
:当前被调用的方法名。 -
#root.target
:当前被调用的目标对象实例。 -
#root.args
:当前被调用的方法参数列表。 -
#result
:方法执行后的返回值
项目中整合SpringCache框架
①改pom文件,导入相关的依赖
我演示的是使用Redis作为缓存数据库,所以一并导入整合Redis的技术依赖,你可以跟据你所使用的缓存数据库进行给更改和配置
XML
<!--spring整合cache的场景依赖-->
<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>
②写yml文件,声明相关配置信息
在yml配置文件中声明好数据库的相关配置,如登录所需要的用户名,密码等...
XML
#端口
server:
port: 8888
#数据库配置
spring:
redis:
host: 192.168.230.100 # Redis服务器地址
database: 0 # Redis数据库索引(默认为0)
port: 6379 # Redis服务器连接端口
# password: ld123456 # Redis服务器连接密码(默认为空)
datasource:
url: jdbc:mysql://192.168.230.100:3306/tmp_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver
#打印日志
logging:
level:
com.donleo.cache.mapper: debug
mybatis:
mapper-locations: classpath:mappers/*.xml
type-aliases-package: com.itheima.cache.model
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
③在配置类中进行Redis缓存的相关配置
java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) // 设置缓存条目的过期时间
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration)
.build();
}
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getSimpleName());
sb.append(".");
sb.append(method.getName());
sb.append("[");
for (Object obj : params) {
sb.append(obj.toString());
}
sb.append("]");
return sb.toString();
};
}
//=======================================================================================
上面是缓存管理器,key生成器的相关配置,如果需要可以自行修改,进行更加细致化的配置,获得更加好的使用体验。
//=======================================================================================
@Bean//配置缓存的模板对象
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建Redis的模板对象....................")
//开始创建Redis的模板对象
RedisTemplate template = new RedisTemplate();
//设置Redis的工厂连接对象
template.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
template.setKeySerializer(new StringRedisSerializer());
//设置redis value的序列化器
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
④在业务类中使用缓存框架注解进行缓存的使用
java
@RestController
@RequestMapping("/user")
@Slf4j
@EnableCaching
public class UserController {
@Autowired
private UserMapper userMapper;
@PostMapping
@CachePut(cacheNames = "userCache" ,key = "#user.id")//对象导航
//如果使用spring cache缓存数据 key的生成为cacheName::key
//插入数据的同时存入缓存
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")
//删除指定的键值对数据
public void deleteById(Long id){
userMapper.deleteById(id);
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)
//删除所有的键值对数据
public void deleteAll(){
userMapper.deleteAll();
}
@GetMapping
//判断缓存中是否存在数据,没有就查询数据库
@Cacheable(cacheNames = "userName",key = "#id")
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
}