
本文共 6188 字,大约阅读时间需要 20 分钟。
首先我来简单的说一下为什么要使用缓存:使用缓存的最大好处就是他能减少服务端频繁的访问数据库,比如将第一次从数据库查询的商品信息放入缓存中,之后如果有很多用户查看商品信息的话,他就直接从缓存中查询,验证码这些生命周期短暂的信息也可放入缓存中处理。
创建SpringBoot项目的时候,勾选下面的几个模块:
正式使用前应在springboot主类上加上下面的注解:
省略数据库表的创建,以及配置信息,和mapper文件(有问题的话可以看)…毕竟本文主要讲缓存嘛。
项目是借助注解来使用缓存的,一般而言注解一般用于Service层!
主要参数如下:
直接举例子:
1.@Cacheable
下面是Service层的一条根据id查询员工的方法,加上@Cacheable注解之后,第二次在调用该方法就会直接在缓存中查询。
/** * 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法; * CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字; * * * 原理: * 1、自动配置类;CacheAutoConfiguration * 2、缓存的配置类 * org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration * org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration * org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration * org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration * org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration * org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration * org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration * org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration * org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration * org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】 * org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration * 3、哪个配置类默认生效:SimpleCacheConfiguration; * * 4、给容器中注册了一个CacheManager:ConcurrentMapCacheManager * 5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中; * * 运行流程: * @Cacheable: * 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取; * (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。 * 2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数; * key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key; * SimpleKeyGenerator生成key的默认策略; * 如果没有参数;key=new SimpleKey(); * 如果有一个参数:key=参数的值 * 如果有多个参数:key=new SimpleKey(params); * 3、没有查到缓存就调用目标方法; * 4、将目标方法返回的结果,放进缓存中 * * @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存, * 如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据; * * 核心: * 1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件 * 2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator * * * 几个属性: * cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存; * * key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值 * 编写SpEL; #i d;参数id的值 #a0 #p0 #root.args[0] * getEmp[2]: root.methodName+"["+#id+"]" * * keyGenerator:key的生成器;可以自己指定key的生成器的组件id * key/keyGenerator:二选一使用; * * * cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器 * * condition:指定符合条件的情况下才缓存; * ,condition = "#id>0" * condition = "#a0>1":第一个参数的值》1的时候才进行缓存 * * unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断 * unless = "#result == null" * unless = "#a0==2":如果第一个参数的值是2,结果不缓存; * sync:是否使用异步模式 * @param id * @return * */ @Cacheable(value = { "emp"}) public Employee getEmp(Integer id){ System.out.println("查询"+id+"号员工"); Employee emp = employeeMapper.getEmpById(id); return emp; }
key默认就是id,而value和cacheNames是一个意思,所以上面的注解等价于@Cacheable(cacheNames = “emp”,key="#id")
如果想换成自己的key,比如想要换成 getEmp[2]
则key="#root.methodName+’[’+#id+’]’ "
也可以用keyGenerator来指定key值:
先给容器注入下面的bean:
@Bean("myKeyGenerator") public KeyGenerator keyGenerator(){ return new KeyGenerator(){ @Override public Object generate(Object target, Method method, Object... params) { return method.getName()+"["+ Arrays.asList(params).toString()+"]"; } }; }
用的时候:
@Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2")
2.@CachePut
加上该注解,我觉得有最值得注意的意义是:1.加上该注解的方法,一定会被执行(什么意思?意思就是如果方法是查询方法,即便结果是在缓存中,只要方法里面有调用数据库的查询,他依旧访问数据库)2.一定要确定好他的key(比如我查询了1号学生的信息,并且放在了key为stu1的缓存中,你现在想在修改学生数据库信息的同时,也修改缓存中的信息,就得加上该注解,且key值也必须是stu1,不能是其他的!)
/** * @CachePut:既调用方法,又更新缓存数据;同步更新缓存 * 修改了数据库的某个数据,同时更新缓存; * 运行时机: * 1、先调用目标方法 * 2、将目标方法的结果缓存起来 * * 测试步骤: * 1、查询1号员工;查到的结果会放在缓存中; * key:1 value:lastName:张三 * 2、以后查询还是之前的结果 * 3、更新1号员工;【lastName:zhangsan;gender:0】 * 将方法的返回值也放进缓存了; * key:传入的employee对象 值:返回的employee对象; * 4、查询1号员工? * 应该是更新后的员工; * key = "#employee.id":使用传入的参数的员工id; * key = "#result.id":使用返回后的id * @Cacheable的key是不能用#result * 为什么是没更新前的?【1号员工没有在缓存中更新】 * */ @CachePut(/*value = "emp",*/key = "#result.id") public Employee updateEmp(Employee employee){ System.out.println("updateEmp:"+employee); employeeMapper.updateEmp(employee); return employee; }
3.@CacheEvict
这里我就说一下 beforeInvocation属性吧,默认为false,意思是在执行在该方法之后,清除key所对应的缓存如果遇到异常就清除不了了,如果属性值为true则在方法调用前执行清除操作。
/** * @CacheEvict:缓存清除 * key:指定要清除的数据 * allEntries = true:指定清除这个缓存中所有的数据 * beforeInvocation = false:缓存的清除是否在方法之前执行 * 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除 * * beforeInvocation = true: * 代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除 * * */ @CacheEvict(value="emp",beforeInvocation = true/*key = "#id",*/) public void deleteEmp(Integer id){ System.out.println("deleteEmp:"+id); //employeeMapper.deleteEmpById(id); int i = 10/0; }
4.Caching
这是个复合注解,可以涵盖上面的注解:
// @Caching 定义复杂的缓存规则 @Caching( cacheable = { @Cacheable(/*value="emp",*/key = "#lastName") }, put = { @CachePut(/*value="emp",*/key = "#result.id"), @CachePut(/*value="emp",*/key = "#result.email") } ) public Employee getEmpByLastName(String lastName){ return employeeMapper.getEmpByLastName(lastName); }
另外补充一个注解:
可以在Service类的前面加上@CacheConfig:抽取缓存的公共配置,下面的缓存注解就可以不写value了,value值统一为emp!
发表评论
最新留言
关于作者
