spring一级缓存二级缓存和三级缓存的区别?spring二级缓存有什么用
文章插图
一、聊聊什么是硬编码使用缓存?
在学习Spring Cache之前,笔者经常会硬编码的方式使用缓存 。
文章插图
我们来举个实际中的例子,为了提升用户信息的查询效率,我们对用户信息使用了缓存,示例代码如下:
@Autowire private UserMapper userMapper; @Autowire private RedisCache redisCache; //查询用户 public User getUserById(Long userId) { //定义缓存key String cacheKey = "userId_" + userId; //先查询redis缓存 User user = redisCache.get(cacheKey); //如果缓存中有就直接返回,不再查询数据库 if (user != null) { return user; } //没有再查询数据库 user = userMapper.getUserById(userId); //数据存入缓存,这样下次查询就能到缓存中获取 if (user != null) { stringCommand.set(cacheKey, user); } return user; }
相信很多同学都写过类似风格的代码,这种风格符合面向过程的编程思维,非常容易理解 。但它也有一些缺点:
代码不够优雅 。业务逻辑有四个典型动作:存储,读取,修改,删除 。每次操作都需要定义缓存Key ,调用缓存命令的API,产生较多的重复代码;
缓存操作和业务逻辑之间的代码耦合度高,对业务逻辑有较强的侵入性 。侵入性主要体现如下两点:
- 开发联调阶段,需要去掉缓存,只能注释或者临时删除缓存操作代码,也容易出错;
- 某些场景下,需要更换缓存组件,每个缓存组件有自己的API,更换成本颇高 。
如果说是下面这样的,是不是就优雅多了 。
@Mapperpublic interface UserMapper { /** * 根据用户id获取用户信息 * * 如果缓存中有直接返回缓存数据,如果没有那么就去数据库查询,查询完再插入缓存中,这里缓存的key前缀为cache_user_id_,+传入的用户ID */ @Cacheable(key = "'cache_user_id_' + #userId") User getUserById(Long userId);}
再看实现类
@Autowire private UserMapper userMapper; //查询用户 public User getUserById(Long userId) { return userMapper.getUserById(userId); }
这么一看是不是完全和缓存分离开来,如果开发联调阶段,需要去掉缓存那么直接注释掉注解就好了,是不是非常完美 。
而且这一整套实现都不要自己手动写,Spring Cache就已经帮我定义好相关注解和接口,我们可以轻易实现上面的功能 。
二、Spring Cache简介
Spring Cache是Spring-context包中提供的基于注解方式使用的缓存组件,定义了一些标准接口,通过实现这些接口,就可以通过在
方法上增加注解来实现缓存 。这样就能够避免缓存代码与业务处理耦合在一起的问题 。
Spring Cache核心的接口就两个:Cache和CacheManager
1、Cache接口
该接口定义提供缓存的具体操作,比如缓存的放入、读取、清理:
package org.Springframework.cache;import java.util.concurrent.Callable;public interface Cache {// cacheName,缓存的名字,默认实现中一般是CacheManager创建Cache的bean时传入cacheNameString getName();//得到底层使用的缓存,如EhcacheObject getNativeCache();// 通过key获取缓存值,注意返回的是ValueWrapper,为了兼容存储空值的情况,将返回值包装了一层,通过get方法获取实际值ValueWrapper get(Object key);// 通过key获取缓存值,返回的是实际值,即方法的返回值类型<T> T get(Object key, Class<T> type);// 通过key获取缓存值,可以使用valueLoader.call()来调使用@Cacheable注解的方法 。当@Cacheable注解的sync属性配置为true时使用此方法 。// 因此方法内需要保证回源到数据库的同步性 。避免在缓存失效时大量请求回源到数据库<T> T get(Object key, Callable<T> valueLoader);// 将@Cacheable注解方法返回的数据放入缓存中void put(Object key, Object value);// 当缓存中不存在key时才放入缓存 。返回值是当key存在时原有的数据ValueWrapper putIfAbsent(Object key, Object value);// 删除缓存void evict(Object key);// 清空缓存void clear();// 缓存返回值的包装interface ValueWrapper {// 返回实际缓存的对象Object get();}}
2、CacheManager接口
主要提供Cache实现bean的创建,每个应用里可以通过cacheName来对Cache进行隔离,每个cacheName对应一个Cache实现 。
package org.Springframework.cache;import java.util.Collection;public interface CacheManager {// 通过cacheName创建Cache的实现bean,具体实现中需要存储已创建的Cache实现bean,避免重复创建,也避免内存缓存 //对象(如Caffeine)重新创建后原来缓存内容丢失的情况Cache getCache(String name);// 返回所有的cacheNameCollection<String> getCacheNames();}
推荐阅读
- Spring容器这些扩展点你都清楚了吗?
- Springboot默认的错误页是如何工作及工作原理你肯定不知道?
- springboot 结合 logback 设置
- Spring中的SmartLifecycle作用
- Springboot配置文件加载路径及使用方式你真的都了解了吗?
- 手撸了一个 Spring MVC 框架
- 一个基于spring boot的Java开源商城系统
- excel下拉菜单如何设置一级二级-excel 下拉一二三级菜单-
- 教大家简单搭建一个SpringBoot项目
- 手把手带你编译Spring框架源码,让你的学习事半功倍