项目放在github:
在缓存开发中,有两个重要的接口:
在这里面:
@Cacheable: 如果用这个注解标注在方法上,那么方法的结果就会被缓存存起来,这个多用于在查询的时候进行使用
比如: public user getuser(Integer id) 这个方法用这个注解标注的话,通过id查到的内容就会杯存在缓存中进行保存,如果下次在进行查同样id的信息的话,直接从缓存中进行调取就行了,大大减少了大系统的数据库的负担
@CacheEvict: 如果用这个注解标注在方法的话,就会把对应的缓存进行删除,这个注解多用于再删除的模块上,再删除数据的时候,将对应的缓存也进行删除
@CachePut: 更新缓存,在更新的时候,多用到
比如:public User updadeuer(User user)这样的方法,就是将之前的信息进行修改,并且进行提交,这个缓存机制也是,在更新之后,将缓存的数据信息也进行更新
@EnableCacheing:要想使用注解,就得启用注解的模式,并且还有key,和value的的对应关系问题
在我们进行缓存项目创建的时候,我们要选中cache的缓存模块,将自动导入我们想要的缓存的依赖
并且要导入redis的依赖,才能使用,否则会报错:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
在@Cache中有几个重要的属性:
cacheName/value:指定缓存组件的名称,这个就是将缓存进行存取的时候的名称,将方法的返回结果放在缓存中,可以制定多个缓存(是数组的形式)。
key:如果不指定的话,系统默认的就是我们的方法传递的参数:
例如;
@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
System.out.println("查询"+id+"号员工信息");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
这种不进行指定的话,我们的key默认就是“2”
我们也可以自己进行指定:
1、利用key进行自定义生成
key="root.methodName"+'['+#id+']'"
这种形式生成的key就是getEmp【2】, root.methodName就是获取方法的名称
2、可以自定义keygenerator,按照自己生成器进行生成我们想要的key
@Bean
public KeyGenerator keyGenerator(){
// return (o, method, params) ->{
// StringBuilder sb = new StringBuilder();
//// sb.append(o.getClass().getName()); // 类目
//// sb.append(method.getName()); // 方法名
// for(Object param: params){
// sb.append(param.toString()); // 参数名
// }
// return sb.toString();
// };
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
return Arrays.asList(objects).toString();
}
};
}
并且在service上用@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")进行keygenerator的自定义器的使用
condition:做判断,例如一号员工做缓存,二号员工不做缓存:
@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",condition = "#id>1" and....)
这种就是判断在id>1的情况下,才会进行缓存生效,否则不生效
unless:是在条件成立的时候,不进行缓存,unless="#a0==2"",这种情况就是在id为2的情况下,不进行缓存,a0就是第一个参数的意思,也就是这里的id
@CachePut(更新数据信息,更新缓存的注解)
这种方法多用在更新信息的情况下进行使用
例如:
@CachePut(cacheNames = {"emp"},key = "#employee.id")
public Employee updateEmp(Employee employee){
System.out.println("修改"+employee.getId()+"号员工信息");
employeeMapper.updateEmp(employee);
return employee;
}
/*
* @CachePut:既调用方法,又更新缓存信息
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
* (这里需要注意的是,在更新缓存数据信息的时候,因为我们的key是不同的,前面默认的是id=xx,这里用的是employee,
* 所以需要将key进行统一,这样才能更新缓存的数据信息)
*
* */
这里就是要注意将我们更新的缓存的key和前面缓存的信息的key要保持一致,否则不能进行缓存的修改
@CacheEvict:在进行缓存的删除时候,也要指定对应的value和key,这样才能删除对应的缓存信息
@CacheEvict(value = {"emp"},key = "#id",allEntries = false,beforeInvocation = false)
public void deleteEmp(Integer id){
System.out.println("删除"+id+"号员工信息");
employeeMapper.deleteEmp(id);
}
其中的allEntries默认为false,如果改成true,那么emp中所有的缓存信息都会被清空
beforeInvocation = false,这个默认也是false,是指定是不是在方法运行之后进行缓存的清除
也可以在最上面加上CacheConfig,这个就是在上面抽取缓存的共同配置,如cacheName等等
然后就是在application.properties或者是application.yml文件中配置我们的redis的相关配置:
#配置redis
redis:
host: 192.168.43.197
port: 6379
database: 0
这些是最基本的一些配置,其余的配置可在官网进行查询得知:
redis是安装在虚拟机docker里面的,在本地安装上redisdesktop可以操作虚拟机里面的redis。
SpringBoot从1.x升级到2.x的redis进行了改革换代,前面的东西和后面的东西差距太大,这里操作2。x版本的SpringBoot需要进行相关的序列化的配置,自己编写config文件进行相关的序列化的操作,config文件代码:
package com.example.cache.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.*;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;
/*
* Redis中常见的五种数据类型
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
*
* */
@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport{
private static final Logger logger = LoggerFactory.getLogger(RedisCacheConfig.class);
// 自定义key生成器
@Bean
public KeyGenerator keyGenerator(){
// return (o, method, params) ->{
// StringBuilder sb = new StringBuilder();
//// sb.append(o.getClass().getName()); // 类目
//// sb.append(method.getName()); // 方法名
// for(Object param: params){
// sb.append(param.toString()); // 参数名
// }
// return sb.toString();
// };
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
return Arrays.asList(objects).toString();
}
};
}
// 配置缓存管理器
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600000)) // 60s缓存失效
// 设置key的序列化方式
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
// 设置value的序列化方式
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
// 不缓存null值
.disableCachingNullValues();
RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
logger.info("自定义RedisCacheManager加载完成");
return redisCacheManager;
}
/* @Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(keySerializer());
redisTemplate.setHashKeySerializer(keySerializer());
redisTemplate.setValueSerializer(valueSerializer());
redisTemplate.setHashValueSerializer(valueSerializer());
logger.info("序列化完成!");
return redisTemplate;
}*/
// key键序列化方式
private RedisSerializer<String> keySerializer() {
return new StringRedisSerializer();
}
// value值序列化方式
private GenericJackson2JsonRedisSerializer valueSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
然后再我们的service中进行相关的增删改查的时候,就可以加上缓存注解,进行操作redis的缓存数据库
package com.example.cache.service;
import com.example.cache.bean.Employee;
import com.example.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
//查询员工信息
/*
*@Cacheable
* 开启缓存,将方法的运行结果缓存起来,下次在相同的查询,就在缓存中查询
* CacheManager管理多个Cache组件,每一个缓存组件都有自己唯一的名称
* 几个属性:
* cacheName/value:指定缓存组件的名称
* key:缓存数据用的值,可以用它来指定,默认是使用方法参数的值,1-方法的返回值
* keyGenerator:主键生成器,也可以自己指定主键生成器
* key/keyGenerator二选一使用
* cacheManager:缓存管理器
* cacheResolver:缓存解析器
* cacheManager/cacheResolver二选一
* condition:指定符合条件的情况下,才进行缓存
* 可以进行指定条件
* unless:否定缓存,当unless指定的条件为true时候,方法的缓存不会被缓存
* sync:异步模式:指定是否使用异步模式
* */
@Cacheable(cacheNames = {"emp"})
// ,keyGenerator = "myKeyGenerator",condition = "#id>1" ,unless="#a0==2"
public Employee getEmp(String lastname){
System.out.println("查询"+lastname+"号员工信息");
Employee emp = employeeMapper.getEmpByName(lastname);
return emp;
}
//删除员工信息
/*
*@CacheEvict:清除注解,将缓存里面的信息进行清除
*,allEntries = false,
* beforeInvocation = false
*/
@CacheEvict(value = {"emp"},key = "'['+#lastname+']'")
public void deleteEmp(String lastname){
System.out.println("删除"+lastname+"员工信息");
employeeMapper.deleteEmp(lastname);
}
//增加员工信息,这里不做缓存
// @CacheEvict(value = {"emp"},key = "'['+#employee.getLastName()+']'")
public void insertEmp(Employee employee){
System.out.println("增加新的员工信息");
employeeMapper.insertEmp(employee);
}
//修改员工信息
/*
* @CachePut:既调用方法,又更新缓存信息
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存起来
* (这里需要注意的是,在更新缓存数据信息的时候,因为我们的key是不同的,前面默认的是id=xx,这里用的是employee,
* 所以需要将key进行统一,这样才能更新缓存的数据信息)
*
* */
@CachePut(cacheNames = {"emp"},key = "'['+#employee.getLastName()+']'")
public Employee updateEmp(Employee employee){
System.out.println("修改"+employee.getLastName()+"员工信息");
employeeMapper.updateEmp(employee);
return employee;
}
}
这样,redis的缓存数据库就配置好了