SpringBoot 学习笔记_缓存 —— Ehcache 2.x

SpringBoot 学习笔记_缓存 —— Ehcache 2.x

声明:

本次学习参考 《SpringBoot + Vue 开发实战》 · 王松(著) 一书。

本文的目的是记录我学习的过程和遇到的一些问题以及解决办法,其内容主要来源于原书。

如有侵权,请联系我删除

Spring 3.1 开始对缓存提供支持,核心思路对方法的缓存,当开发者调用一个方法时,将方法中的参数/返回值作为key/value缓存起来,当再次调用该方法时,如果缓存中有数据,就直接从缓存获取,否则再去执行方法。但是,Spring 中并未提供缓存的实现,而是提供一套缓存 API。

SpringBoot 缓存 —— Ehcache 2.x

  • 创建 SpringBoot 项目,添加依赖

    <!--    添加 Ehcache 2.x 缓存依赖    -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    
    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
    </dependency>
    
  • 添加缓存配置文件(ehcache.xml

    如果 Ehcache 的依赖存在,并且在 classpath 下有一个名为 ehcache.xml 的 Ehcache 配置文件,那么 EhCacheCacheManager 将会自动作为缓存的实现。因此,在 resource 目录下创建 ehcache.xml 作文 Ehcache 缓存的配置文件。

    如果想自定义 Ehcache 配置文件位置和名称,可以在 application.properties 添加配置

    spring.cache.ehcache.config=classpath:config/ehcache-config.xml
    

    ehcache.xml 中配置如下:

    <!-- 常规 Ehcache 配置文件 -->
    <ehcache>
        <!--  提供两个缓存策略  -->
        <diskStore path="java.io.tmpdir/cache"/>
    
        <!--  1、默认缓存配置  -->
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                overflowToDisk="false"
                diskPersistent="false"
                diskExpiryThreadIntervalSeconds="120"
        />
    
        <!--  2、自定义缓存配置  -->
        <!--  name: 缓存名称  -->
        <!--  maxElementsInMemory: 缓存最大个数  -->
        <!--  eternal: 缓存对象是否永久有效。为 true 时 timeout 不生效  -->
        <!--  timeToIdleSeconds: 缓存对象在失效前的允许闲置时间(单位:秒)。eternal 为 false 时才生效  -->
        <!--  timeToLiveSeconds: 缓存对象在失效前的允许存活时间(单位:秒)。eternal 为 false 时才生效  -->
        <!--  overflowToDisk: 内存中的对象数量达到 maxElementsInMemory 时,Ehcache 是否将对象写到磁盘中  -->
        <!--  diskExpiryThreadIntervalSecond: 磁盘失效线程运行时间间隔  -->
        <cache
                name="book_cache"
                maxElementsInMemory="10000"
                eternal="true"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                overflowToDisk="true"
                diskPersistent="true"
                diskExpiryThreadIntervalSeconds="600"
        />
    </ehcache>
    
  • 开启缓存

    在项目入口类添加 @EnableCaching 注解开启缓存

    @SpringBootApplication
    @EnableCaching
    public class Chapter041Application {
        public static void main(String[] args) {
            SpringApplication.run(Chapter041Application.class, args);
        }
    }
    
  • 创建实体类

    public class Book {
        private Integer id;
        private String name;
        private String author;
        /* Getter & Setter */
    }
    
  • 创建 Dao

    @Repository
    // @CacheConfig 注解指定使用的缓存名称(配置在 ehcache.xml 中)
    // 也可以在 @Cacheable 注解中指明缓存名称
    @CacheConfig(cacheNames = "book_cache")
    public class BookDao {
        // 方法上添加 @Cacheable 注解,表示对该方法进行缓存。
        // 默认情况下,缓存的 key 是方法的参数,缓存的 value 是方法的返回值。
        // 当在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,直接使用缓存数据,该方法不会执行。
        // 否则,执行该方法,执行成功后将返回值缓存起来
        // 但是,若在当前类中调用该方法,则缓存不会生效
        // 该注解还有一个 condition 属性用来表明缓存的执行时机,如 @Cacheable(condition= "#id%2==0") 表示 id 为偶数时才进行缓存
        @Cacheable
        public Book getBookById(Integer id) {
            System.out.println("getBookId");
            Book book = new Book();
            book.setId(1);
            book.setName("三国演义");
            book.setAuthor("罗贯中");
            return book;
        }
    
        // 如果不想使用默认的 key,也可以自定义
        // 表示缓存的 key 为参数 book 对象中 id 的值
    
        // CachePut 一般用于数据更新方法上。其属性与 Cacheable 类似
        // 与 Cacheable 不同的是:
        // 添加 CachePut 注解的方法每次执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将结果缓存,
        // 如果该 key 对应的数据已经被缓存,则会覆盖之前的数据,避免获取到脏数据
        @CachePut(key = "#book.id")
        public Book updateBookById(Book book) {
            System.out.println("updateBookById");
            book.setName("新三国演义");
            return book;
        }
    
        // 如果不想使用默认的 key,也可以自定义
        // 表示缓存的 key 为参数 id
    
        // CacheEvict 一般用于删除方法上,表示移除一个 key 对应的缓存。其有两个特殊属性:
        // allEntries:表示是否将所有的缓存数据都移除。默认 false
        // beforeInvocation:表示是否在方法执行之前移除缓存中的数据。默认 false,即在方法执行之后移除缓存中的数据。
        @CacheEvict(key = "#id")
        public void deleteBookById(Integer id){
            System.out.println("deleteBookById");
        }
    
        // 除了以上两种,Spring 还提供了 root 对象用来生成 key
        // #root.methodName     当前方法名
        // #root.method.name    当前方法对象
        // #root.caches[0].name 当前方法使用的缓存
        // #root.target         当前被调用的对象
        // #root.targetClass    当前被调用的对象的 class
        // #root.args[0]        当前方法参数数组
    }
    
  • 如果默认提供的 key 不满足要求,也可以自定义缓存 key 生成器

    /**
     * 自定义缓存 key 的生成器
     */
    @Component
    public class MyKeyGenerator implements KeyGenerator {
        /**
         *
         * @param target 当前对象
         * @param method 当前请求方法
         * @param params 当前参数
         * @return 生成的 key
         */
        @Override
        public Object generate(Object target, Method method, Object... params) {
            return Arrays.toString(params);
        }
    }
    
  • BookDao 应该如下使用自定义的 key 生成器

    @Service
    @CacheConfig(cacheNames = "book_cache")
    public class Book1Dao {
        @Autowired
        MyKeyGenerator myKeyGenerator;
        @Cacheable(keyGenerator = "myKeyGenerator")
        public Book getBookById(Integer id){
            System.out.println("getBookById");
            Book book = new Book();
            book.setId(1);
            book.setName("三国演义");
            book.setAuthor("罗贯中");
            return book;
        }
    }
    
  • 创建测试类

    @RunWith(SpringRunner.class)
    @SpringBootTest
    class Chapter041ApplicationTests {
    
        @Autowired
        BookDao bookDao;
        @Test
        void contextLoads() {
            // 执行查询方法
            bookDao.getBookById(1);// 会执行方法
            // 执行查询方法
            bookDao.getBookById(1);// 没有执行方法,说明这里使用了缓存数据
    
            // 执行删除方法
            bookDao.deleteBookById(1);
            // 执行查询方法
            Book book3 = bookDao.getBookById(1);// 会执行查询方法。因为删除方法中缓存已经被删除了。
    
            System.out.println("book3: " + book3);
    
            // 执行更新方法
            Book book = new Book();
            book.setId(1);
            book.setName("三国演义");
            book.setAuthor("罗贯中");
            bookDao.updateBookById(book); // 这里不仅会更新数据,也会更新缓存
            // 执行查询方法
            Book book4 = bookDao.getBookById(1);// 没有执行方法,说明这里使用了缓存数据
            System.out.println("book4: " + book4);// 但是输出数据,发现数据已经被更新。所以说明 update 的时候是同步更新了缓存数据的。
        }
    }
    
  • 输出结果

    getBookId
    deleteBookById
    getBookId
    book3: Book{id=1, name='三国演义', author='罗贯中'}
    updateBookById
    book4: Book{id=1, name='新三国演义', author='罗贯中'}
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HolaSecurity

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值