一.JSR107缓存(了解)
Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry和Expiry。
- CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以在运行期访问多个CachingProvider。
- CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。
- Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。
- Entry是一个存储在Cache中的key-value对。
- Expiry每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。
使用JSR107需要导入如下包
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
二.spring缓存(简化了JSR-107缓存)
1.Spring从3.1开始定义了Cache和CacheManager接口来统一不同的缓存技术;并支持使用JCache(JSR-107)注解简化我们开发;
-
Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
-
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache 、ConcurrentMapCache等;
-
每次调用需要缓存功能的方法时,Spring会检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
-
使用Spring缓存抽象时我们需要关注以下两点;
- 确定方法需要被缓存以及他们的缓存策略
- 从缓存中读取之前缓存存储的数据
2.spring中常用的注解
3.常用属性
@Cacheable:作用于方法上,执行方法对结果进行缓存(若是有该缓存,则不用进入该方法).常用语查询的方法上面
@CacheEvice:删除缓存,常用户删除方法
@CachePut:更新缓存,常用于更新方法;
下面是其属性值:
常用值:
1.cacheNames/value:给该缓存取名字(必须要)
2.key 缓存是存储key value的形式,所以给key取个名族,不写,系统自己取(遵循SPEL)
会生成一个key为方法名[参数id]
2.keyGenerator与key两者选择一个
自定义一个keyGenerator的bean,加载到容器中
package com.wcy.springbootcache.config;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.util.Arrays;
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
return method.getName()+"["+ Arrays.asList(objects).toString() +"]";
}
};
}
}
3.cacheManager、cacheResolver生成缓存
3.condition:方法之前执行,条件为true则缓存(遵循SPEL)
4.unless:方法之后完,条件为false则缓存(遵循SPEL)
5.sync是否采取异步
CacheEvict可以用:
6.allEntries删除cacheName下的所有缓存
7.beforeInvocation在方法之前删除,若为true如果方法里面出现异常,缓存还是会被删除,若为false,则方法出现异常不用清空缓存
下图:
遵循SPEL,上图属性可以选择下面的属性
4.springboot使用缓存:
- 导入缓存的启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
- @EnableCaching开启缓存
- 方法上使用注解:
1.@Cacheable的使用:
/*
该方法如果有缓存不会执行
key缓存的key
condition:id>1才缓存
unless不为空才缓存
keyGenerator = "myKeyGenerator"自定义的keyGenerator的配置类
*/
@Cacheable(cacheNames = {"emp","test"},key = "#id",condition = "#id>1",unless = "#result==null")
@Override
public Employee findEmployeeById(Integer id) {
System.out.println("查询成功");
return employeeMapper.findEmployeeById(id);
}
2.@CachePut的使用:
/*
CachePut注解该方法必执行,并更新缓存,不过注意key与Cacheable的key相同才行
*/
@CachePut(cacheNames = {"emp"},key = "#employee.id")
@Override
public Employee editEmployee(Employee employee) {
System.out.println("更新了数据");
employeeMapper.editEmployee(employee);
return employee;
}
3.@CacheEvict的用法
/*
CacheEvict删除缓存,指定key删除
allEntries删除cacheName下的所有缓存
beforeInvocation在方法之前删除,若为true如果方法里面出现异常,缓存还是会被删除,若为false,则方法出现异常不用清空缓存
*/
@CacheEvict(cacheNames = {"emp","test"},key = "#id" /*,allEntries = true,beforeInvocation = true*/)
@Override
public Integer deleteEmployee(Integer id) {
System.out.println(id+"号员工删除");
//模拟删除
//employeeMapper.deleteEmployee(id);
return 1;
}
4.Caching组合注解
即可以定义cacheable又可以定义put还有evict
5.@CacheConfig统一配置相同的属性(标注在类上)
这样1CacheEvict、CachePut、Cacheable都不用写cacheNames 了
@CacheConfig(cacheNames = {"emp"})
@Service
public class EmployeeServiceImpl implements EmployeeService {
三.整合redis实现缓存(上面那些注解存储的数据就会存储到redi中)
- 引入spring-boot-starter-data-redis
- application.yml配置redis连接地址
- 使用RestTemplate操作redis
- redisTemplate.opsForValue();//操作字符串
- redisTemplate.opsForHash();//操作hash
- redisTemplate.opsForList();//操作list
- redisTemplate.opsForSet();//操作set
- redisTemplate.opsForZSet();//操作有序set
- 配置缓存、 CacheManagerCustomizers
- 测试使用缓存、 切换缓存、 CompositeCacheManager
3.1.Springboot自动加入了redisTemplate和对stringRedisTemplate的配置(两者操作都是一样的,只是stringRedisTemplate操作k-v都为String的,redisTemplate操作k-v为Object)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
3.2.若要保存对象,对象一定记得序列化(spring默认的对象序列化是通过JDK来序列化的,我们是看不懂的,所以需要切换为其他的序列化器)
3.3.springboot配置redis.
1.引入redis的start
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.yml配置.
spring:
redis:
host: 192.168.0.104
port: 6379
password:
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
3.自定义配置redis(这样存储对象通过存储json字符串的形式)
package com.wcy.springbootcache.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import java.net.UnknownHostException;
import java.time.Duration;
@Configuration
public class MyRedisConfig {
// @Bean
// public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
// RedisTemplate<String, Object> template = new RedisTemplate();
// template.setConnectionFactory(redisConnectionFactory);
// Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
// template.setDefaultSerializer(serializer);
// return template;
// }
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 缓存管理器
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
//初始化一个RedisCacheWriter
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
//设置CacheManager的值序列化方式为json序列化
RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
.fromSerializer(jsonSerializer);
RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(pair);
//设置默认超过期时间是30秒
defaultCacheConfig.entryTtl(Duration.ofSeconds(30));
//初始化RedisCacheManager
return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}
}
4.测试缓存
@Test
void TestRedisTemplate(){
Employee employee = employeeService.findEmployeeById(2);
redisTemplate.opsForValue().set("k1",employee);
// Employee k1 = (Employee)redisTemplate.opsForValue().get("k1");
// System.out.println(k1);
}
5.可使用@Cacheable、@CacheEvict、@CachePut,这样他会把数据缓存到redis中,1以前是存在ConconcaHashMap
例如:@Cacheable
@Service
public class DepatmentServiceImpl implements DepatmentService {
@Autowired
private DepatmentMapper depatmentMapper;
@Cacheable(cacheNames = "dep",key = "#id")
@Override
public Department findDepa(Integer id) {
System.out.println("查询了数据库");
return depatmentMapper.findDepa(id);
}
}