Java Caching 5个核心接口:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
result | 执行上下文 | 法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意:
@Cacheable(key = "targetClass + methodName +#p0")
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&, |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],^ […],$[…] |
Spring从3.1开始定义了org.springframework.cache.Cache
和org.springframework.cache.CacheManager
接口来统一不同的缓存技术,并支持使用JCache(JSR-107)
注解简化我们开发。
使用Spring缓存抽象时我们需要关注以下两点:
名称 | 解释 |
---|---|
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其进行缓存 |
@CacheEvict | 清空缓存 |
@CachePut | 保证方法被调用,又希望结果被缓存。与@Cacheable区别在于是否每次都调用方法,常用于更新 |
@EnableCaching | 开启基于注解的缓存 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@CacheConfig | 统一配置本类的缓存注解的属性 |
名称 | 解释 | 实例 |
---|---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | @Cacheable(value=”mycache”) 或者@Cacheable(value={”cache1”,”cache2”}) |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 | @Cacheable(value=”testcache”,key=”#userName”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存,在调用方法之前之后都能判断 | @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
allEntries (@CacheEvict ) | 是否清空所有缓存内容,缺省为 false,如果指定为true,则方法调用后将立即清空所有缓存 | @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation (@CacheEvict) | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | @CachEvict(value=”testcache”,beforeInvocation=true) |
unless (@CachePut) (@Cacheable) | 用于否决缓存的,不像condition,该表达式只在方法执行之后判断,此时可以拿到返回值result进行判断。条件为true不会缓存,fasle才缓存 | @Cacheable(value=”testcache”,unless=”#result == null”) |
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
sql文件:
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
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 DEFAULT CHARSET=utf8;
application.yml
spring:
datasource:
# 数据源基本配置
username: root
password: "00000000"
url: jdbc:mysql://192.168.168.128/springboot_cache?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
redis:
host: 192.168.168.128
password: "00000000"
mybatis:
configuration:
# 开启驼峰命名匹配规则
map-underscore-to-camel-case: true
# 配置Sql语句日志
logging:
level:
com:
example:
cache:
mapper: debug
@EnableCaching // 开启基于注解的缓存
@MapperScan("com.example.cache.mapper") // 指定要扫描的mapper接口所在的包
@SpringBootApplication
public class CacheApplication {
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
}
// 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable(value = "emp", key = "#id")
public Employee getEmp(Integer id) {
System.out.println("查询" + id + "号员工");
return employeeMapper.getEmpById(id);
}
测试:
多次查询同一员工数据只执行一次数据库查询。
配置类
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator() {
return (o, method, objects) -> method.getName() + Arrays.asList(objects).toString();
}
}
修改注解
@Cacheable(value = "emp", keyGenerator = "myKeyGenerator")
public Employee getEmp(Integer id) {
System.out.println("查询" + id + "号员工");
return employeeMapper.getEmpById(id);
}
// 保证方法被调用,同时结果被缓存
@CachePut(value = "emp", key = "#employee.id")
public Employee updateEmp(Employee employee) {
System.out.println("更新了员工:" + employee.getLastName());
employeeMapper.updateEmp(employee);
return employee;
}
测试:
更新员工信息后缓存随之修改。查询仍然从缓存中获取。
属性 | 解释 | 示例 |
---|---|---|
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为true,则方法调用后将立即清空所有缓存 | @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 | @CachEvict(value=”testcache”,beforeInvocation=true) |
@CacheEvict(value = "emp", key = "#id")
public void deleteEmp(Integer id) {
System.out.println("删除了员工:" + id);
employeeMapper.deleteEmpById(id);
}
@CacheEvict(value = "accountCache", allEntries = true)
public void deleteAll() {
System.out.println("删除了所有员工");
employeeMapper.deleteAll();
}
@CacheEvict(value = "accountCache", beforeInvocation = true)
public void deleteAll() {
System.out.println("删除了所有员工");
employeeMapper.deleteAll();
}
@Caching(
cacheable = {
@Cacheable(value = "emp", key = "#lastName")
},
put = {
@CachePut(value = "emp", key = "#result.id"),
@CachePut(value = "emp", key = "#result.email")
},
evict = {
@CacheEvict(value = "emp", key = "#lastName")
}
)
public Employee getEmpByLastName(String lastName) {
return employeeMapper.getEmpByLastName(lastName);
}
@CacheConfig(cacheNames = "emp") // 抽取缓存的公共配置
@Service
public class EmployeeService {
...
}
docker pull redis
docker run --name redis -p 6379:6379 redis --requirepass 123456
导入此依赖时,不再需要spring-boot-starter-cache
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
@SpringBootTest
class CacheApplicationTests {
@Autowired
StringRedisTemplate stringRedisTemplate; // 操作k-v都是字符串的
@Autowired
RedisTemplate redisTemplate; // 操作k-v都是对象的
@Test
void Test02() {
// redis中保存数据
stringRedisTemplate.opsForValue().append("msg", "Hello");
// redis中获取数据
String msg = stringRedisTemplate.opsForValue().get("msg");
System.out.println(msg);
}
@Test
void Test03() {
// redis中保存对象
Employee empById = employeeMapper.getEmpById(1);
// 默认使用jdk序列化机制保存对象
redisTemplate.opsForValue().set("emp-01", empById);
}
}
将数据以 Json 的方式保存
@Configuration
public class MyRedisConfig {
// 自定义Employee序列化机制
@Bean
public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Employee> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
template.setDefaultSerializer(serializer);
return template;
}
// 自定义Department序列化机制
@Bean
public RedisTemplate<Object, Department> deptRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Department> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer<Department> serializer = new Jackson2JsonRedisSerializer<Department>(Department.class);
template.setDefaultSerializer(serializer);
return template;
}
}
测试
@SpringBootTest
class CacheApplicationTests {
@Resource
EmployeeMapper employeeMapper;
@Resource
DepartmentMapper departmentMapper;
@Autowired
StringRedisTemplate stringRedisTemplate; // 操作k-v都是字符串的
@Autowired
RedisTemplate redisTemplate; // 操作k-v都是对象的
@Autowired
RedisTemplate<Object, Employee> empRedisTemplate;
@Autowired
RedisTemplate<Object, Department> deptRedisTemplate;
@Test
void Test03() {
// redis中保存对象
Employee empById = employeeMapper.getEmpById(1);
// 默认使用jdk序列化机制保存对象
// redisTemplate.opsForValue().set("emp-01", empById);
empRedisTemplate.opsForValue().set("emp-01", empById);
}
@Test
void Test04() {
// redis中保存对象
Department deptById = departmentMapper.getDeptById(1);
// 使用Json保存对象
deptRedisTemplate.opsForValue().set("dept-01", deptById);
}
}
@Configuration
public class MyRedisConfig {
// 自定义CacheManager
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofDays(1))
.disableCachingNullValues()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory).cacheDefaults(cacheConfiguration).build();
}
}
测试
本文地址:https://blog.csdn.net/weixin_41105242/article/details/107187153
如对本文有疑问, 点击进行留言回复!!
springcloud中feign调用处理mybatis-plus Ipage反序列化问题。
Flume 史上最全面的大数据学习第十篇(一) 别再说不知道flume是什么了
网友评论