做服装广告素材网站,wordpress标题收起,如何查询网站备案进度,吉林电商网站建设公司电话1. 功能说明 Cacheable 注解在方法上#xff0c;表示该方法的返回结果是可以缓存的。也就是说#xff0c;该方法的返回结果会放在缓存中#xff0c;以便于以后使用相同的参数调用该方法时#xff0c;会返回缓存中的值#xff0c;而不会实际执行该方法。 注意#xff0c;这…1. 功能说明 Cacheable 注解在方法上表示该方法的返回结果是可以缓存的。也就是说该方法的返回结果会放在缓存中以便于以后使用相同的参数调用该方法时会返回缓存中的值而不会实际执行该方法。 注意这里强调了一点参数相同。这一点应该是很容易理解的因为缓存不关心方法的执行逻辑它能确定的是对于同一个方法如果参数相同那么返回结果也是相同的。但是如果参数不同缓存只能假设结果是不同的所以对于同一个方法你的程序运行过程中使用了多少种参数组合调用过该方法理论上就会生成多少个缓存的 key当然这些组合的参数指的是与生成 key 相关的。下面来了解一下 Cacheable 的一些参数
2. cacheNames value Cacheable 提供两个参数来指定缓存名value、cacheNames二者选其一即可。这是 Cacheable 最简单的用法示例
Override
Cacheable(menu)
public Menu findById(String id) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;
} 在这个例子中findById 方法与一个名为 menu 的缓存关联起来了。调用该方法时会检查 menu 缓存如果缓存中有结果就不会去执行方法了。
3. 关联多个缓存名 其实按照官方文档Cacheable 支持同一个方法关联多个缓存。这种情况下当执行方法之前这些关联的每一个缓存都会被检查而且只要至少其中一个缓存命中了那么这个缓存中的值就会被返回。示例
OverrideCacheable({menu, menuById})public Menu findById(String id) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;}---------
GetMapping(/findById/{id})
public Menu findById(PathVariable(id)String id){Menu menu0 menuService.findById(fe278df654adf23cf6687f64d1549c0a);Menu menu2 menuService.findById(fb6106721f289ebf0969565fa8361c75);return menu0;
} 为了直观起见直接将 id 参数写到代码里。现在我们来测试一下看一下结果 4. key keyGenerator 一个缓存名对应一个被注解的方法但是一个方法可能传入不同的参数那么结果也就会不同这应该如何区分呢这就需要用到 key 。在 spring 中key 的生成有两种方式显式指定和使用 keyGenerator 自动生成。 4.1. KeyGenerator 自动生成 当我们在声明 Cacheable 时不指定 key 参数则该缓存名下的所有 key 会使用 KeyGenerator 根据参数 自动生成。spring 有一个默认的 SimpleKeyGenerator 在 spring boot 自动化配置中这个会被默认注入。生成规则如下 a. 如果该缓存方法没有参数返回 SimpleKey.EMPTY b. 如果该缓存方法有一个参数返回该参数的实例 c. 如果该缓存方法有多个参数返回一个包含所有参数的 SimpleKey 默认的 key 生成器要求参数具有有效的 hashCode() 和 equals() 方法实现。另外keyGenerator 也支持自定义 并通过 keyGenerator 来指定。关于 KeyGenerator 这里不做详细介绍有兴趣的话可以去看看源码其实就是使用 hashCode 进行加乘运算。跟 String 和 ArrayList 的 hash 计算类似。 4.2. 显式指定 key 相较于使用 KeyGenerator 生成spring 官方更推荐显式指定 key 的方式即指定 Cacheable 的 key 参数。 即便是显式指定但是 key 的值还是需要根据参数的不同来生成那么如何实现动态拼接呢SpELSpring Expression LanguageSpring 表达式语言 能做到这一点。下面是一些使用 SpEL 生成 key 的例子。
OverrideCacheable(value {menuById}, key #id)public Menu findById(String id) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;}OverrideCacheable(value {menuById}, key id- #menu.id)public Menu findById(Menu menu) {return menu;}OverrideCacheable(value {menuById}, key hash #menu.hashCode())public Menu findByHash(Menu menu) {return menu;} 结果 显示指定的好处在于直观明了看到代码就能想象生成的 key 是什么样。而且 SpEL 也很强大。关于 SpEL 的详细用法这里不详述可以参考官方文档 Redirecting... 注意官方说 key 和 keyGenerator 参数是互斥的同时指定两个会导致异常。 5. cacheManager cacheResolver CacheManager缓存管理器是用来管理检索一类缓存的。通常来讲缓存管理器是与缓存组件类型相关联的。我们知道spring 缓存抽象的目的是为使用不同缓存组件类型提供统一的访问接口以向开发者屏蔽各种缓存组件的差异性。那么 CacheManager 就是承担了这种屏蔽的功能。spring 为其支持的每一种缓存的组件类型提供了一个默认的 manager如RedisCacheManager 管理 redis 相关的缓存的检索、EhCacheManager 管理 ehCache 相关的缓等。 CacheResolver缓存解析器是用来管理缓存管理器的CacheResolver 保持一个 cacheManager 的引用并通过它来检索缓存。CacheResolver 与 CacheManager 的关系有点类似于 KeyGenerator 跟 key。spring 默认提供了一个 SimpleCacheResolver开发者可以自定义并通过 Bean 来注入自定义的解析器以实现更灵活的检索。 大多数情况下我们的系统只会配置一种缓存所以我们并不需要显式指定 cacheManager 或者 cacheResolver。但是 spring 允许我们的系统同时配置多种缓存组件这种情况下我们需要指定。指定的方式是使用 Cacheable 的 cacheManager 或者 cacheResolver 参数。 注意按照官方文档cacheManager 和 cacheResolver 是互斥参数同时指定两个可能会导致异常。
6. sync 是否同步true/false。在一个多线程的环境中某些操作可能被相同的参数并发地调用这样同一个 value 值可能被多次计算或多次访问 db这样就达不到缓存的目的。针对这些可能高并发的操作我们可以使用 sync 参数来告诉底层的缓存提供者将缓存的入口锁住这样就只能有一个线程计算操作的结果值而其它线程需要等待这样就避免了 n-1 次数据库访问。 sync true 可以有效的避免缓存击穿的问题。
7. condition 调用前判断缓存的条件。有时候我们可能并不想对一个方法的所有调用情况进行缓存我们可能想要根据调用方法时候的某些参数值来确定是否需要将结果进行缓存或者从缓存中取结果。比如当我根据年龄查询用户的时候我只想要缓存年龄大于 35 的查询结果。那么 condition 能实现这种效果。 condition 接收一个结果为 true 或 false 的表达式表达式同样支持 SpEL 。如果表达式结果为 true则调用方法时会执行正常的缓存逻辑查缓存-有就返回-没有就执行方法-方法返回不空就添加缓存否则调用方法时就好像该方法没有声明缓存一样即无论传入了什么参数或者缓存中有些什么值都会执行方法并且结果不放入缓存。下面举个例子 我们看一下数据库以这两条数据为例 我们首先定义一个带条件的缓存方法
OverrideCacheable(value {menuById}, key #id, condition #conditionValue 1)public Menu findById(String id, Integer conditionValue) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;} 然后分两种情况调用为了直观可见直接将 id 写在代码中
GetMapping(/findById/{id})public Menu findById(PathVariable(id)String id){Menu menu0 menuService.findById(fe278df654adf23cf6687f64d1549c0a, 0);Menu menu2 menuService.findById(fb6106721f289ebf0969565fa8361c75, 2);return menu0;} 然后我们请求一下看看缓存中的结果和控制台打印 可以看到两次请求都执行方法因为原来缓存中都没有数据但是只有“微服务测试2”缓存了。这说明只有满足 condition 条件的调用结果才会被缓存。接下来我们再请求一遍看下结果和打印 可以看到“微服务测试2”由于已经有了缓存所以没有再执行方法体。而“微服务测试0”又一次执行了。
8. unless 执行后判断不缓存的条件。unless 接收一个结果为 true 或 false 的表达式表达式支持 SpEL。当结果为 true 时不缓存。举个例子 我们先清除 redis 中的数据。然后看看 mysql 中的数据 然后编写一个缓存方法在该方法中result代表方法的返回值。关于
OverrideCacheable(value {menuById}, key #id, unless #result.type folder)public Menu findById(String id) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;} 然后调用该方法
GetMapping(/findById/{id})public Menu findById(PathVariable(id)String id){Menu menu0 menuService.findById(fe278df654adf23cf6687f64d1549c0a);Menu menu2 menuService.findById(fb6106721f289ebf0969565fa8361c75);return menu0;} 看看缓存结果和打印 可以看到两次都执行了方法体其实unless 条件就是在方法执行完毕后调用所以它不会影响方法的执行但是结果只有 menu.type page 的缓存了说明 unless 参数生效了。 9. condition VS unless 既然 condition 和 unless 都能决定是否进行缓存那么同时指定这两个参数并且结果相冲突的时候会怎么样呢我们来试一试。 首先清除 redis 数据然后在缓存方法上加上 conditiontrue如
OverrideCacheable(value {menuById}, key #id, condition true, unless #result.type folder)public Menu findById(String id) {Menu menu this.getById(id);if (menu ! null){System.out.println(menu.name menu.getName());}return menu;} 其它代码不变我们来看一下缓存结果和打印 可以看到虽然两次调用都执行了但是typefolder 的还是被排除了。说明这种情况下unless 比 condition 优先级要高。接下来我们把 conditionfalse再来试试结果 可以看到两次调用的结果都没有缓存。说明在这种情况下condition 比 unless 的优先级高。总结起来就是 condition 不指定相当于 trueunless 不指定相当于 false 当 condition false一定不会缓存 当 condition true且 unless true不缓存 当 condition true且 unless false缓存