Spring Cache

 

https://docs.spring.io/spring-framework/docs/5.3.8-SNAPSHOT/reference/html/integration.html#cache

自Spring 3.1版本以来,Spring框架支持低侵入的方式向已有Spring应用加入缓存特性。与声明式事务类似,声明式缓存Spring Cache抽象允许一致的API来已支持多种不同的缓存解决方案,同时将对代码的影响减少到最小。从Spring 4.1开始,Spring已完整支持JSR-107注解和更多的定制选项。

JSR-107与Spring 自带注解对比

0769f0665a3a44d8b6f3ef79a8d0ff3c.png

  • @Cacheable / @CacheResult 用于读取和设置缓存

  • @CachePut 添加/更新缓存

  • @CacheEvict / @CacheRemove 移除缓存

区分Cache与Buffer

很多情况下,Buffer(缓冲)与Cache(缓存)是类似的。然而在表现形式与应用场景上两个的差别还是比较明显的。

传统意义上,Buffer作为快速实体与慢速实体之间的桥梁。比如:硬盘上的文件数据会先到内存,再被CPU加载,内存作为Buffer可以减少等待时间,同时利用Buffer可将原本小块数据攒成整块一次性交给处理者,可以有效减少IO。此外,通常至少有一个对象对其可见。

而Cache缓存,相对来说是隐藏的,对于访问者与被访问者来说应该是隐藏的,好的程序设计可以让使用者对缓存无感知,同时它还可以提高性能,允许应用多次、快速的读取缓存数据。

如何使用Spring Cache

在启动类使用@EnableCaching注解中开启缓存

@SpringBootApplication
@EnableCaching
public class RedisDemoApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(RedisDemoApplication.class, args);
    }
​
}

创建配置类:想配置就自己配置,不配置也可以, 如果没有配置,最后异常,那么写上该配置试试,不行的话,就是代码问题

@Configuration
public class SpringCacheConfgiration {
    @Bean
    @Primary //设置默认的CacheManager
    public CacheManager cacheManager(LettuceConnectionFactory factory){
        //加载默认Spring Cache配置信息
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //设置有效期为1小时
        config = config.entryTtl(Duration.ofHours(1));
        //说明缓存Key使用单冒号进行分割
        config = config.computePrefixWith(cacheName -> cacheName + ":");
        //Redis Key采用String直接存储
        config = config.serializeKeysWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(new StringRedisSerializer()));
        //Redis Value则将对象采用JSON形式存储
        config = config.serializeValuesWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer()));
        //不缓存Null值对象
        config = config.disableCachingNullValues();
        //实例化CacheManger缓存管理器
        RedisCacheManager cacheManager = RedisCacheManager.RedisCacheManagerBuilder
                        .fromConnectionFactory(factory)  //绑定REDIS连接工厂
                        .cacheDefaults(config)  //绑定配置对象
                        .transactionAware()  //与声明式事务注解@Transactional进行兼容
                        .build();  //完成对象构建
        return cacheManager;
    }
}

创建实体类:

@Data
public class Emp implements Serializable {
    private Integer empno;
    private String name;
    private Date birthday;
    private Float salary;
    private String department;
}

创建EmpMapper:

@Repository
public class EmpDao {d
    public Emp findById(Integer empId){
        System.out.println("执行了findById方法:EmpId:" + empId);
        return new Emp(empId , "张三"  , new Date()  , 1000f ,"研发部");
    }
​
    public List<Emp> selectByParams(){
        System.out.println("已执行selectByPrams方法");
        List list = new ArrayList();
        for(int i = 0 ; i < 10 ; i++) {
            list.add(new Emp(i , "emp" +  i , new Date() , 5000 + i * 100f , "RESEARCH"));
        }
        return list;
    }
​
    public Emp insert(Emp emp){
        System.out.println("正在创建" + emp.getEmpno() + "员工信息");
        return emp;
    }
​
    public Emp update(Emp emp){
        System.out.println("正在更新" + emp.getEmpno() + "员工信息");
        return emp;
    }
​
    public void delete(Integer empno){
        System.out.println("删除" + empno + "员工信息");
    }
}

创建Service:

@Service
public class EmpService {
    @Resource
    EmpDao empDao;
​
    @Cacheable(value = "emp", key ="#empId", condition = "#empId != 1000")
    public Emp findById(Integer empId) {
        return empDao.findById(empId);
    }
​
    @Cacheable(value = "emp:rank:salary")
    public List<Emp> getEmpRank() {
        return empDao.selectByParams();
    }
​
    @CachePut(value = "emp", key = "#emp.empno")
    public Emp create(Emp emp) {
        return empDao.insert(emp);
    }
​
    @CachePut(value = "emp", key = "#emp.empno")
    public Emp update(Emp emp) {
        return empDao.update(emp);
    }
​
    @CacheEvict(value="emp" , key = "#empno")
    public void delete(Integer empno) {
        empDao.delete(empno);
    }
}

创建测试类:

@SpringBootTest
public class SpringCacheTestor {
    @Resource
    private EmpService empService;
​
    @Test
    public void testFindById(){
        Emp emp1 = empService.findById(1001);
        System.out.println(emp1.getName());
        emp1 = empService.findById(1001);
        System.out.println(emp1.getName());
​
        Emp emp2 = empService.findById(1000);
        System.out.println(emp2.getName());
        emp2 = empService.findById(1000);
        System.out.println(emp2.getName());
    }
​
    @Test
    public void testEmpRank() {
        List<Emp> list = empService.getEmpRank();  // 第一次执行getEmpRank()方法的返回值也会放入缓存
        list = empService.getEmpRank();  // 第二次会先从缓存中获取,若没有则执行getEmpRank()方法
        System.out.println("-----------------------------------------------");
        for(Emp emp:list){
            System.out.println(emp);
        }
    }
​
    @Test
    public void testCreate(){
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1234f , "MARKET"));
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1235f , "MARKET"));
        empService.create(new Emp(1002 , "emp" + new Date().getTime() , new Date() , 1236f , "MARKET"));
        Emp emp = empService.findById(1002);
        System.out.println(emp);
    }
​
    @Test
    public void testUpdate(){
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 2234f , "MARKET"));
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 3234f , "MARKET"));
        empService.update(new Emp(1002 , "u-emp" + new Date().getTime() , new Date() , 4234f , "MARKET"));
        Emp emp = empService.findById(1002);
        System.out.println(emp);
    }
​
    @Test
    public void testDelete(){
        empService.delete(1002);
        //Emp emp = empService.findById(1002);
        //System.out.println(emp);
    }
}

总结:

(1)要在启动类中添加@EnableCaching注解开启缓存,由于我们在配置文件中配置了redis,所以这里使用的redis作为缓存。
​
(2)@Cacheable注解放在service层中查询相关的方法上,若缓存中不存在对应的key则会执行service方法从数据库中查询数据,然后查询到的数据即会返回到上一层,也会在缓存中保存一份。若不满足查询条件condition则执行service方法去数据库查询数据。
​
(3)@CachePut注解放在service层中添加、修改相关的方法上,总结一句话:不管是放在添加还是修改的service方法上,若缓存中存在对应的key则对缓存中的数据进行修改,反之则会从缓存中添加。
​
(4)@CacheEvict注解则是根据key删除缓存中的数据

 

  • 17
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值