一、ehcache

1. 基本配置

  1. 引入依赖
  1. <!--ehcache3.9配置-->
  2. <dependency>
  3. <groupId>org.ehcache</groupId>
  4. <artifactId>ehcache</artifactId>
  5. <version>3.9.0</version>
  6. </dependency>
  7. <!--springboot配置-->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-cache</artifactId>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework.boot</groupId>
  14. <artifactId>spring-boot-starter-web</artifactId>
  15. <exclusions><!-- 去掉springboot默认配置 -->
  16. <exclusion>
  17. <groupId>org.springframework.boot</groupId>
  18. <artifactId>spring-boot-starter-logging</artifactId>
  19. </exclusion>
  20. </exclusions>
  21. </dependency>
  22. <!--JSR-107 API的jar 不用也行,xml配置去除jsr的相关配置-->
  23. <dependency>
  24. <groupId>javax.cache</groupId>
  25. <artifactId>cache-api</artifactId>
  26. <version>1.0.0</version>
  27. </dependency>
  1. 配置文件

    1. spring:
    2. cache:
    3. #ehcache配置文件路径
    4. ehcache:
    5. config: classpath:config/ehcache.xml
    6. #指定缓存类型,可加可不加
    7. #type: ehcache
  2. 创建缓存日志监听器

    1. /**
    2. * 缓存日志监听器
    3. * @author Zhang Xin
    4. * @date 2021-03-15
    5. */
    6. public class EventLoggerListener implements CacheEventListener<Object, Object> {
    7. private static final Logger logger = LoggerFactory.getLogger(EventLoggerListener.class);
    8. @Override
    9. public void onEvent(CacheEvent<?, ?> event) {
    10. logger.info("缓存事件: 类型[{}], 键值[{}], 原值[{}], 新值[{}]",
    11. event.getType(),
    12. event.getKey(),
    13. event.getOldValue(),
    14. event.getNewValue());
    15. }
    16. }
  3. 在 resource 的 config 目录下创建 encache.xml 文件

  1. <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2. xmlns="http://www.ehcache.org/v3"
  3. xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
  4. xsi:schemaLocation="
  5. http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
  6. http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
  7. <!-- <service>-->
  8. <!-- <jsr107:defaults enable-statistics="true"/>-->
  9. <!-- </service>-->
  10. <service>
  11. <jsr107:defaults>
  12. <!--用别名“person”定义一个缓存,该别名从缓存模板“cache-template-default”继承-->
  13. <jsr107:cache name="person" template="cache-template-default" />
  14. </jsr107:defaults>
  15. </service>
  16. <!-- 不用上面的jsr107配置用下方的定义方式也行-->
  17. <!-- 定义缓存空间CommonCache,之后的配置缓存空间名字person改成CommonCache即可 -->
  18. <cache alias="CommonCache" uses-template="cache-template-default">
  19. <expiry>
  20. <ttl unit="seconds">300</ttl>
  21. </expiry>
  22. </cache>
  23. <cache-template name="cache-template-default">
  24. <!--本部分允许您添加缓存事件侦听器。我为5个事件添加了一个侦听器。
  25. 发生时,每个事件将由EventLogger类记录。-->
  26. <listeners>
  27. <listener>
  28. <class>com.example.ehcache.ehcachelearn.listener.EventLoggerListener</class>
  29. <event-firing-mode>ASYNCHRONOUS</event-firing-mode>
  30. <event-ordering-mode>UNORDERED</event-ordering-mode>
  31. <!--使用此侦听器将条目添加到缓存时,定义CREATED事件。-->
  32. <events-to-fire-on>CREATED</events-to-fire-on>
  33. <!--使用此侦听器在缓存中更新条目时定义一个UPDATED事件-->
  34. <events-to-fire-on>UPDATED</events-to-fire-on>
  35. <!-- 当条目从缓存中使用此侦听器过期时,定义一个EXPIRED事件。-->
  36. <events-to-fire-on>EXPIRED</events-to-fire-on>
  37. <!--当使用此侦听器从缓存中删除条目时,定义一个REMOVED事件。-->
  38. <events-to-fire-on>REMOVED</events-to-fire-on>
  39. <!--当使用此侦听器从缓存中逐出条目时,定义EVICTED事件。-->
  40. <events-to-fire-on>EVICTED</events-to-fire-on>
  41. </listener>
  42. </listeners>
  43. <resources>
  44. <!--堆配置为允许2000个条目-->
  45. <heap unit="entries">2000</heap>
  46. <!--堆外存储配置有100 MB的空间。请记住,度量单位区分大小写。-->
  47. <offheap unit="MB">100</offheap>
  48. </resources>
  49. </cache-template>
  50. </config>
  1. 启动类加上注解@EnableCaching
  1. @EnableCaching
  2. @SpringBootApplication
  3. public class SpringbootCacheApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringbootCacheApplication.class, args);
  6. }
  7. }

* XML配置详情

FQCN(Full Qualified Class Name完全限定类名;

  1. <config
  2. xmlns:ehcache="http://www.ehcache.org/v3"
  3. xmlns:jcache="http://www.ehcache.org/v3/jsr107">
  4. <!--
  5. 可选
  6. 由CacheManager管理和生命周期化的服务
  7. -->
  8. <service>
  9. <!--
  10. 另一个命名空间中的一个元素,这里以我们的JSR-107扩展为例
  11. -->
  12. <jcache:defaults>
  13. <jcache:cache name="invoices" template="myDefaultTemplate"/>
  14. </jcache:defaults>
  15. </service>
  16. <!--
  17. 可选
  18. <cache>元素定义由强制性“ alias”属性标识的缓存,由CacheManager管理
  19. -->
  20. <cache alias="productCache">
  21. <!--
  22. 可选,默认为java.lang.Object
  23. -->
  24. <key-type copier="org.ehcache.impl.copy.SerializingCopier">java.lang.Long</key-type>
  25. <!--
  26. 可选,默认为java.lang.Object
  27. -->
  28. <value-type copier="org.ehcache.impl.copy.SerializingCopier">com.pany.domain.Product</value-type>
  29. <!--
  30. 可选,默认为无有效期
  31. 可以使缓存条目在给定时间后过期
  32. -->
  33. <expiry>
  34. <!--
  35. <tti>空闲时间,即条目保持原状的最长时间
  36. 可以使缓存条目在给定时间后过期
  37. 其他选项是:
  38. * <ttl>,生存时间;
  39. * <class>,用于自定义Expiry实现
  40. * <无>,无有效期
  41. -->
  42. <tti unit="minutes">2</tti>
  43. </expiry>
  44. <!--
  45. 可选,默认为不建议
  46. 驱逐顾问,可让您控制哪些条目仅应在万不得已时被逐出
  47. org.ehcache.config.EvictionAdvisor实现的FQCN
  48. -->
  49. <eviction-advisor>com.pany.ehcache.MyEvictionAdvisor</eviction-advisor>
  50. <!--
  51. 可选,
  52. 让我们将缓存配置为“直通式”,
  53. 也就是说,使用CacheLoaderWriter的Cache会在未命中加载,并在变异操作中进行写入。
  54. -->
  55. <loader-writer>
  56. <!--
  57. FQCN实现org.ehcache.spi.loaderwriter.CacheLoaderWriter
  58. -->
  59. <class>com.pany.ehcache.integration.ProductCacheLoaderWriter</class>
  60. <!-- 另一个名称空间中的任何其他元素 -->
  61. </loader-writer>
  62. <!--
  63. 逐出之前,要保留在缓存中的最大条目数
  64. -->
  65. <heap unit="entries">200</heap>
  66. <!--
  67. 可选
  68. 另一个名称空间中的任何其他元素
  69. -->
  70. </cache>
  71. <!--
  72. 可选
  73. <cache-template>定义了一个命名模板,该模板可以用作同一文件中的<cache>定义
  74. 它们具有与上述<cache>元素相同的属性
  75. -->
  76. <cache-template name="myDefaultTemplate">
  77. <expiry>
  78. <none/>
  79. </expiry>
  80. <!--
  81. 可选
  82. 另一个名称空间中的任何其他元素
  83. -->
  84. </cache-template>
  85. <!--
  86. 通过在uses-template属性中引用缓存模板的名称来使用上述模板的<cache>:
  87. -->
  88. <cache alias="customerCache" uses-template="myDefaultTemplate">
  89. <!--
  90. 添加键和值类型配置
  91. -->
  92. <key-type>java.lang.Long</key-type>
  93. <value-type>com.pany.domain.Customer</value-type>
  94. <!--
  95. 将模板设置的容量限制覆盖为新值
  96. -->
  97. <heap unit="entries">200</heap>
  98. </cache>
  99. </config>

2. 使用工具类

  1. 引入依赖 配置 JSONUtils
    1. <dependency>
    2. <groupId>com.alibaba</groupId>
    3. <artifactId>fastjson</artifactId>
    4. <version>1.2.75</version>
    5. </dependency>
    JSONUtils: ```java import com.alibaba.fastjson.JSON; import java.util.List;

/**

  • JSON工具类
  • @author Zhang Xin
  • @date 2021-3-23 */ public class JSONUtils { private JSONUtils() {}

    /**

    • 对象转JSON格式
    • @param o
    • @return */ public static String toJSONString(Object o){ return JSON.toJSONString(o); }

      /**

    • JSON格式字符串转泛型对象
    • @param text
    • @param clazz
    • @param
    • @return */ public static T parseObject(String text, Class clazz) { return JSON.parseObject(text, clazz); }

      public static List parseList(String text, Class clazz) { return JSON.parseArray(text, clazz); }

}

  1. 2. 配置EhcacheUtils
  2. 详见[https://blog.csdn.net/q1248807225/article/details/105536606](https://blog.csdn.net/q1248807225/article/details/105536606)
  3. ```java
  4. import org.ehcache.Cache;
  5. import org.ehcache.CacheManager;
  6. import org.ehcache.config.CacheConfiguration;
  7. import org.ehcache.config.builders.CacheConfigurationBuilder;
  8. import org.ehcache.config.builders.CacheManagerBuilder;
  9. import org.ehcache.config.builders.ExpiryPolicyBuilder;
  10. import org.ehcache.config.builders.ResourcePoolsBuilder;
  11. import org.ehcache.config.units.EntryUnit;
  12. import org.ehcache.config.units.MemoryUnit;
  13. import java.time.Duration;
  14. import java.util.List;
  15. /**
  16. * Ehcache缓存工具类
  17. * @author ZX
  18. * @date 2021-01-15
  19. */
  20. public class EhcacheUtils {
  21. /**
  22. * 初始化Ehcache配置
  23. */
  24. private static CacheConfiguration<String, String> usesConfiguredInCacheConfig;
  25. /**
  26. * 初始化管理器
  27. */
  28. private static CacheManager cacheManager;
  29. private static final String COMMON_CACHE = "person";
  30. static {
  31. init();
  32. }
  33. private static void init(){
  34. if(usesConfiguredInCacheConfig == null) {
  35. usesConfiguredInCacheConfig = CacheConfigurationBuilder
  36. .newCacheConfigurationBuilder(String.class, String.class,
  37. ResourcePoolsBuilder.newResourcePoolsBuilder()
  38. // 基于堆内存
  39. .heap(2000L, EntryUnit.ENTRIES)
  40. // 基于内存
  41. .offheap(20L, MemoryUnit.MB))
  42. .withSizeOfMaxObjectGraph(2000L)
  43. .withSizeOfMaxObjectSize(100L, MemoryUnit.KB)
  44. //失效时间5秒,time-to-idle 最后一次使用后计算,所存储的时间5秒没对条目进行操作则失效
  45. .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(5)))
  46. .build();
  47. }
  48. if(cacheManager == null) {
  49. cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
  50. .withDefaultSizeOfMaxObjectSize(50L, MemoryUnit.KB)
  51. .withDefaultSizeOfMaxObjectGraph(2000)
  52. .withCache(COMMON_CACHE, usesConfiguredInCacheConfig)
  53. .build(true);
  54. }
  55. }
  56. public static Cache<String, String> getCache(String cacheName){
  57. return cacheManager
  58. .getCache(cacheName, String.class, String.class);
  59. }
  60. /**
  61. * 设置缓存
  62. * @param key
  63. * @param Value
  64. */
  65. public static void set(String key, Object Value) {
  66. getCache(COMMON_CACHE).put(key, JSONUtils.toJSONString(Value));
  67. }
  68. /**
  69. * 获取缓存
  70. * @param key
  71. * @param clazz
  72. * @param <T>
  73. * @return
  74. */
  75. public static <T> T get(String key, Class<T> clazz) {
  76. return JSONUtils.parseObject(getCache(COMMON_CACHE).get(key), clazz);
  77. }
  78. /**
  79. * 获取缓存
  80. * @param key
  81. * @return
  82. */
  83. public static String get(String key) {
  84. return getCache(COMMON_CACHE).get(key);
  85. }
  86. /**
  87. * 获取缓存返回list列表
  88. * @param key
  89. * @param clazz
  90. * @param <T>
  91. * @return
  92. */
  93. public static <T> List<T> list(String key, Class<T> clazz) {
  94. return JSONUtils.parseList(getCache(COMMON_CACHE).get(key), clazz);
  95. }
  96. /**
  97. * 移除缓存key
  98. * @param key
  99. */
  100. public static void remove(String key) {
  101. getCache(COMMON_CACHE).remove(key);
  102. }
  103. /**
  104. * 缓存清理
  105. */
  106. public static void clear() {
  107. getCache(COMMON_CACHE).clear();
  108. }
  109. /**
  110. * 关闭管理器
  111. */
  112. public static void close() {
  113. cacheManager.close();
  114. }
  115. public static void main(String[] args) {
  116. init();
  117. set("haha", "哈哈哈");
  118. String haha = get("haha");
  119. System.out.println(haha);
  120. }
  121. }

如:withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofHours(24))

  1. no expiry 永不过期
  2. time-to-live 从缓存开始计算,所存储的时间
  3. time-to-idle 最后一次使用后计算,所存储的时间

  4. 用 Controller 测试 ```java @RestController @RequestMapping(“/ehcache”) public class PersonController {

    @GetMapping(“/a”) public void ehcacheSet(){

    1. EhcacheUtils.set("haha", "哈哈哈");
    2. System.out.println("存入缓存");

    }

    @GetMapping(“/b”) public void ehcacheGet(){

    1. String haha = EhcacheUtils.get("haha");
    2. System.out.println(haha);

    }

}

  1. 结果:<br />![微信图片_20210323154900.png](https://cdn.nlark.com/yuque/0/2021/png/12713233/1616485820389-68a68e45-1b5d-4726-8f15-18fa38de1182.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_9%2Ctext_RXDmtYHoi48%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10#height=124&id=Fa618&margin=%5Bobject%20Object%5D&name=%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20210323154900.png&originHeight=124&originWidth=82&originalType=binary&ratio=1&size=1859&status=done&style=none&width=82)<br />缓存工具类中设置了失效时间5秒,5秒没对条目进行操作则失效
  2. <a name="N7s36"></a>
  3. ## 3. 使用注解
  4. <a name="5UjaD"></a>
  5. ### 2.1 简单使用
  6. 1. 编写缓存配置类
  7. ```java
  8. import java.util.concurrent.TimeUnit;
  9. import javax.cache.CacheManager;
  10. import javax.cache.configuration.MutableConfiguration;
  11. import javax.cache.expiry.Duration;
  12. import javax.cache.expiry.TouchedExpiryPolicy;
  13. import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
  14. import org.springframework.stereotype.Component;
  15. @Component
  16. public class Ehcache3Config implements JCacheManagerCustomizer {
  17. private static final String NAME_CACHE = "person";
  18. @Override
  19. public void customize(CacheManager cacheManager) {
  20. // 创建别名为“person”的缓存。
  21. cacheManager.createCache(NAME_CACHE,
  22. new MutableConfiguration<>()
  23. // 该行设置了过期策略。在这种情况下,我们将其设置为 2 秒。
  24. // 因此,如果在过去的 2 秒钟内未触摸(创建,更新或访问)某个条目,则该条目将被驱逐。
  25. .setExpiryPolicyFactory(TouchedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, 2)))
  26. .setStoreByValue(true).setStatisticsEnabled(true));
  27. }
  28. }
  1. 编写 Service 类,person 代表要读取的数据,加上注解 @Cacheable ```java import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service;

@Service public class PersonService { @Cacheable(cacheNames = “person”,key = “#id”) public Person getPerson(Long id){ System.out.println(“调用Service”); Person person = new Person(id,”ramostear”,”ramostear@163.com”); return person; } }

  1. 3. Controller 调用
  2. ```java
  3. @RestController
  4. @RequestMapping("/persons")
  5. public class PersonController {
  6. @Autowired
  7. private PersonService personService;
  8. @GetMapping("/{id}")
  9. public ResponseEntity<Person> person(@PathVariable(value = "id") Long id){
  10. Person person = personService.getPerson(id);
  11. System.out.println("调用接口");
  12. return new ResponseEntity<>(person, HttpStatus.OK);
  13. }
  14. }

结果:
微信图片_20210323145000.png
在 Config 配置文件中设置了过期策略为2秒。 因此,如果在过去的 2 秒钟内未触摸(创建,更新或访问)某个条目,则该条目将被驱逐,将重新调用Service中的方法

2.2 注解配置详情

@Cacheable

@Cacheable 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@Cacheable 作用和配置方法

参数 解释 example
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:
@Cacheable(value=”mycache”)
@Cacheable(value={”cache1”,”cache2”}

| | key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有入参进行组合 | @Cacheable(value=”testcache”,key=”#userName”) | | condition | (执行方法之前)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 | @Cacheable(value=”testcache”,condition=”#userName.length()>2”) |

@Cacheable(value=”accountCache”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。

@CachePut

@CachePut 的作用 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
@CachePut 作用和配置方法

参数 解释 example
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 @CachePut(value=”my cache”)
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @CachePut(value=”testcache”,key=”#userName”)
condition (执行完方法后)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@CachePut(value=”testcache”,condition=”#userName.length()>2”)

@CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。

  1. @CachePut(value="accountCache",key="#account.getName()")// 更新accountCache 缓存
  2. public Account updateAccount(Account account) {
  3. return updateDB(account);
  4. }

@CacheEvict

@CachEvict 的作用 主要针对方法配置,能够根据一定的条件对缓存进行清空
@CacheEvict 作用和配置方法

参数 解释 example
value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 @CacheEvict(value=”my cache”)
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 @CacheEvict(value=”testcache”,key=”#userName”)
condition (默认方法执行之后,看配置beforeInvocation)
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存
@CacheEvict(value=”testcache”,condition=”#userName.length()>2”)
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 @CachEvict(value=”testcache”,beforeInvocation=true)

实例

  1. @CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 缓存
  2. public void updateAccount(Account account) {
  3. updateDB(account);
  4. }
  5. @CacheEvict(value="accountCache",allEntries=true)// 方法调用后将立即清空 accountCache 缓存
  6. public void reload() {
  7. reloadAll()
  8. }
  9. @Cacheable(value="accountCache",condition="#userName.length() <=4")// 缓存名叫 accountCache
  10. public Account getAccountByName(String userName) {
  11. // 方法内部实现不考虑缓存逻辑,直接实现业务
  12. return getFromDB(userName);
  13. }

@CacheConfig

所有的@Cacheable()里面都有一个value=“xxx”的属性,这显然如果方法多了,写起来也是挺累的,如果可以一次性声明完 那就省事了, 所以,有了@CacheConfig这个配置,@CacheConfig is a class-level annotation that allows to share the cache names,如果你在你的方法写别的名字,那么依然以方法的名字为准。
例:

  1. @CacheConfig("books")
  2. public class BookRepositoryImpl implements BookRepository {
  3. @Cacheable
  4. public Book findBook(ISBN isbn) {...}
  5. }

条件缓存(使用示例)

一些常用的条件缓存
EQ 就是 EQUAL 等于
NE 就是 NOT EQUAL 不等于
GT 就是 GREATER THAN 大于
GE 就是 GREATER THAN OR EQUAL 大于等于
LT 就是 LESS THAN 小于
LE 就是 LESS THAN OR EQUAL 小于等于

  1. //@Cacheable将在执行方法之前( #result还拿不到返回值)判断condition,如果返回true,则查缓存;
  2. @Cacheable(value = "user", key = "#id", condition = "#id lt 10")
  3. public User conditionFindById(final Long id)
  4. //@CachePut将在执行完方法后(#result就能拿到返回值了)判断condition,如果返回true,则放入缓存;
  5. @CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")
  6. public User conditionSave(final User user)
  7. //@CachePut将在执行完方法后(#result就能拿到返回值了)判断unless,
  8. //如果返回false,则放入缓存;(即跟condition相反)
  9. @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")
  10. public User conditionSave2(final User user)
  11. //@CacheEvict, beforeInvocation=false表示在方法执行之后调用(#result能拿到返回值了);
  12. //且判断condition,如果返回true,则移除缓存;
  13. @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")
  14. public User conditionDelete(final User user)

@Caching

(key)–>(value)
有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id–>user;username—>user;email—>user的缓存;此时就需要@Caching组合多个注解标签了。

  1. @Caching(put = {
  2. @CachePut(value = "user", key = "#user.id"),
  3. @CachePut(value = "user", key = "#user.username"),
  4. @CachePut(value = "user", key = "#user.email")
  5. })
  6. public User save(User user) {

自定义缓存注解

比如之前的那个@Caching组合,会让方法上的注解显得整个代码比较乱,此时可以使用自定义注解把这些注解组合到一个注解中,如:

  1. @Caching(put = {
  2. @CachePut(value = "user", key = "#user.id"),
  3. @CachePut(value = "user", key = "#user.username"),
  4. @CachePut(value = "user", key = "#user.email")
  5. })
  6. @Target({ElementType.METHOD, ElementType.TYPE})
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Inherited
  9. public @interface UserSaveCache {
  10. }

这样我们在方法上使用如下代码即可,整个代码显得比较干净。

  1. @UserSaveCache
  2. public User save(User user)

扩展优化

(key)–>(value)
比如findByUsername时,不应该只放username–>user,应该连同id—>user和email—>user一起放入;这样下次如果按照id查找直接从缓存中就命中了

  1. @Caching(
  2. cacheable = {
  3. @Cacheable(value = "user", key = "#username")
  4. },
  5. put = {
  6. @CachePut(value = "user", key = "#result.id", condition = "#result != null"),
  7. @CachePut(value = "user", key = "#result.email", condition = "#result != null")
  8. }
  9. )
  10. public User findByUsername(final String username) {
  11. System.out.println("cache miss, invoke find by username, username:" + username);
  12. for (User user : users) {
  13. if (user.getUsername().equals(username)) {
  14. return user;
  15. }
  16. }
  17. return null;
  18. }

其实对于:id—>user;username—->user;email—>user;更好的方式可能是:id—>user;username—>id;email—>id;保证user只存一份;如:

  1. @CachePut(value="cacheName", key="#user.username", cacheValue="#user.username")
  2. public void save(User user)
  3. @Cacheable(value="cacheName", key="#user.username", cacheValue="#caches[0].get(#caches[0].get(#username).get())")
  4. public User findByUsername(String username)

SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:

名称 位置 描述 示例
methodName root对象 当前被调用的方法名 root.methodName
method root对象 当前被调用的方法 root.method.name
target root对象 当前被调用的目标对象 root.target
targetClass root对象 当前被调用的目标对象类 root.targetClass
args root对象 当前被调用的方法的参数列表 root.args[0]
caches root对象 当前方法调用使用的缓存列表(如@Cacheable(value={“cache1”, “cache2”})),则有两个cache root.caches[0].name
argument name 执行上下文 当前被调用的方法的参数,如findById(Long id),我们可以通过#id拿到参数 user.id
result 执行上下文 方法执行后的返回值(仅当方法执行之后的判断有效,如‘unless’,’cache evict’的beforeInvocation=false) result

使用例子:

  1. @CacheEvict(value = "user", key = "#user.id", condition = "#root.target.canCache() and #root.caches[0].get(#user.id).get().username ne #user.username", beforeInvocation = true)
  2. public void conditionUpdate(User user)

二、jetcache

https://github.com/alibaba/jetcache/wiki/Home_CN

1.基本概念

(1) 简介:
JetCache是一个基于Java的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache 提供了比SpringCache 更加强大的注解,可以原生的支持TTL、两级缓存、分布式自动刷新,还提供了Cache接口用于手工缓存操作。当前有四个实现,RedisCacheTairCache(此部分未在github开源)CaffeineCache(in memory) 和一个简易的 LinkedHashMapCache(in memory),要添加新的实现也是非常简单的。

(2) 全部特性:

  • 通过统一的API访问Cache系统
  • 通过注解实现声明式的方法缓存,支持TTL和两级缓存
  • 通过注解创建并配置Cache实例
  • 针对所有Cache实例和方法缓存的自动统计
  • Key的生成策略和Value的序列化策略是可以配置的
  • 分布式缓存自动刷新,分布式锁 (2.2+)
  • 异步Cache API (2.2+,使用Redis的lettuce客户端时)
  • Spring Boot支持

(3) 要求:
JetCache需要JDK1.8、Spring Framework4.0.8以上版本。Spring Boot为可选,需要1.1.9以上版本。如果不使用注解(仅使用jetcache-core),Spring Framework也是可选的,此时使用方式与Guava/Caffeinecache类似。

** 依赖哪个jar?

  • jetcache-anno-api:定义jetcache的注解和常量,不传递依赖。如果你想把Cached注解加到接口上,又不希望你的接口jar传递太多依赖,可以让接口jar依赖 jetcache-anno-api。
  • jetcache-core:核心api,完全通过编程来配置操作Cache,不依赖Spring。两个内存中的缓存实现LinkedHashMapCache 和 CaffeineCache 也由它提供。
  • jetcache-anno:基于Spring提供@Cached和@CreateCache注解支持。
  • jetcache-redis:使用jedis提供Redis支持。
  • jetcache-redis-lettuce(需要JetCache2.3以上版本):使用lettuce提供Redis支持,实现了JetCache异步访问缓存的的接口。
  • jetcache-starter-redis:Spring Boot方式的Starter,基于Jedis。
  • jetcache-starter-redis-lettuce(需要JetCache2.3以上版本):Spring Boot方式的Starter,基于Lettuce。

2.使用jetcache

  1. 引入依赖

    1. <dependency>
    2. <groupId>com.alicp.jetcache</groupId>
    3. <artifactId>jetcache-starter-redis</artifactId>
    4. <version>2.4.4</version>
    5. </dependency>
  2. 配置yml文件

  1. jetcache:
  2. # 每隔多久统计信息的时长配置
  3. statIntervalMinutes: 15
  4. # 是否配置前缀
  5. areaInCacheName: false
  6. local:
  7. default:
  8. # 本地缓存类型
  9. type: linkedhashmap
  10. # key的序列化转化的协议
  11. keyConvertor: fastjson
  12. # 本地缓存最大个数
  13. limit: 10000
  14. # 缓存的时间全局默认值
  15. defaultExpireInMillis: 10000
  16. remote:
  17. default:
  18. # 缓存数据库类型
  19. type: redis
  20. keyConvertor: fastjson
  21. valueEncoder: java
  22. valueDecoder: java
  23. poolConfig:
  24. minIdle: 5
  25. maxIdle: 20
  26. maxTotal: 50
  27. host: 127.0.0.1
  28. port: 6379
  1. @EnableMethodCache @EnableCreateCacheAnnotation这两个注解分别激活@Cached @CreateCache 注解
  1. @SpringBootApplication
  2. // 激活Cached 注解
  3. @EnableMethodCache(basePackages = "com.zx.mypackage")
  4. // 激活CreateCache 注解
  5. @EnableCreateCacheAnnotation
  6. public class ServerApplication {
  7. public static void main(String[] args) {
  8. SpringApplication.run(ServerApplication.class, args);
  9. }
  10. }
  1. 代码中使用cache注解
  1. @Service
  2. public class UserServiceImpl implements UserService {
  3. @Autowired
  4. private UserMapper userMapper;
  5. //使用ehcache配置的缓存名users_test
  6. private final String USER_CACHE_NAME = "users_test";
  7. @Override
  8. public List<User> listUser() {
  9. return userMapper.selectUserList();
  10. }
  11. @Override
  12. // @Cacheable(value = USER_CACHE_NAME, key = "#id")
  13. @Cacheable(value = USER_CACHE_NAME, key = "'user' + #id")
  14. public User selectUserById(Integer id) {
  15. return userMapper.selectUserById(id);
  16. }
  17. @Override
  18. // @CacheEvict(value = USER_CACHE_NAME, key = "#id")
  19. @CacheEvict(value = USER_CACHE_NAME, key = "'user_' + #id")
  20. public void delete(Integer id) {
  21. userMapper.delete(id);
  22. }
  23. @Override
  24. // @CacheEvict(value = USER_CACHE_NAME, key = "#user.userId")
  25. @CacheEvict(value = USER_CACHE_NAME, key = "'user' + #user.userId")
  26. // @CachePut(value = USER_CACHE_NAME, key = "'user' + #user.userId")
  27. // 测试发现只将结果清除,key未清除,导致查询继续使用缓存但结果为空????
  28. public void update(User user) {
  29. userMapper.update(user);
  30. }
  31. }