美文网首页山炮服务器开发
09-SpringBoot的缓存-1

09-SpringBoot的缓存-1

作者: 梁寅plus | 来源:发表于2019-08-04 21:59 被阅读0次

    09-SpringBoot的缓存-1

    本文初概的介绍了SpringBoot的缓存,并介绍了EhCache的初级使用。更多深入的用法,请其他查看专业文档。

    目录


    image.png

    1.什么是缓存

    • 一种以为键值对方式存在于内存的数据。一旦数据被缓存,用户的请求可不通过代码逻辑快速提供数据反馈。

    • 在键值对中,用LRU(last recently used)算法来保证对最近不常访问的数据进行清除

    • 由于存在内存中,通常一断电,缓存数据就Over了。

    2.解决的问题

    • 提升性能

      主要用于读多写少的场景

    • 缓解数据库压力

      比如商城秒杀

    3.使用缓存的场景

    • 数据实时性要求不高

    • 对性能要求比较高

    4.SpringBoot中对缓存的支持

    • JCache

    • EhCache

    • Hazelcast

    • Infinispan

    • Couchbase

    • Redis

    • Caffeine

    • Simple

    • EhCache和Redis是目前比较主流的缓存使用方式

    5. EhCache

    单机,配置简单,快速上手,单机上性能好(JVM操作)。

    劣势:拓展性性能较差。在集群方案中,基本看不到应用身影。

    实现

    本实现采用的SpringBoot版本信息如下:

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.M4</version>
    <relativePath/>
    </parent>

    下面我们来一步一步实现。

    SpringBoot添加依赖

    <dependencies>
        ……
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
        </dependency>
    </dependencies>
    

    SpringBoot缓存配置

    缓存配置一般放在resources目下,名为一个ehcache.xml文件,如果没有,请自行创建。

    修改配置文件位置

    通常配置的位置在resouces目录下,如果要修改,请在application.properties中进行修改:

    spring.cache.ehcache.config=classpath:config/another-config.xml
    
    默认配置
        <defaultCache maxElementsInMemory="10000"
                       eternal="false"
                       timeToIdleSeconds="120"
                       timeToLiveSeconds="120"
                       overflowToDisk="false"
                       diskPersistent="false"
                       diskExpiryThreadIntervalSeconds="120"
        />
    

    部分配置的说明:

    ​ name 缓存名唯一标识
    ​ maxElementsInMemory="1000" 内存中最大缓存对象数
    ​ eternal="false" 是否永久缓存
    ​ timeToIdleSeconds="3600" 缓存清除时间 默认是0 即永不过期
    ​ timeToLiveSeconds="0" 缓存存活时间 默认是0 即永不过期
    ​ overflowToDisk="true" 缓存对象达到最大数后,将其写入硬盘
    ​ maxElementsOnDisk="10000" 磁盘最大缓存数
    ​ diskPersistent="false" 磁盘持久化
    ​ diskExpiryThreadIntervalSeconds="120" 磁盘缓存的清理线程运行间隔
    ​ memoryStoreEvictionPolicy="FIFO" 缓存清空策略
    ​ FIFO 先进先出
    ​ LFU less frequently used 最少使用
    ​ LRU least recently used 最近最少使用

    自定义配置

    在这里定义一个DiskCache,代表缓存会存储在磁盘中:

        <cache name="DiskCache"
               maxEntriesLocalHeap="10000"
               eternal="true"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               overflowToDisk="true"
               diskPersistent="true"
               diskExpiryThreadIntervalSeconds="120">
        </cache>
    

    配置的全貌为:

    <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
    <diskStore path="java.io.tmpdir/cache"/>
        <!--默认缓存设置-->
        <defaultCache maxElementsInMemory="10000"
                       eternal="false"
                       timeToIdleSeconds="120"
                       timeToLiveSeconds="120"
                       overflowToDisk="false"
                       diskPersistent="false"
                       diskExpiryThreadIntervalSeconds="120"
        />
         
        <cache name="DiskCache"
               maxEntriesLocalHeap="10000"
               eternal="true"
               timeToIdleSeconds="120"
               timeToLiveSeconds="120"
               overflowToDisk="true"
               diskPersistent="true"
               diskExpiryThreadIntervalSeconds="120">
        </cache>
    </ehcache>
    

    SpringBoot缓存启动定义

    重在@EnableCaching标签

    @SpringBootApplication
    @EnableCaching
    public class Demo1Application {
        public static void main(String[] args) {
            SpringApplication.run(Demo1Application.class, args);
        }
    }
    

    SpringBoot缓存功能实现

    Dao实现

    我们先来普及一下几个注解,如下:

    @CacheConfig

    在Dao类中进行声明,如果需要进行自定义类声明,需要配合CacheNames

    @Cacheable

    使用在方法的定义,标明是缓存方法,使用在查询方法上。

    @CachePut

    在使用update,以及insert方法的时候,需要使用这个注解,同时需要声明key,它代表了update的时候,key是使用哪一个(SQL上的术语)。

    一般这么写@CachePut(key = "book.id"),book是方法入参的参数,在Update的时候,通常使用的是一个对象对吧。

    这里有一个点需要注意,在定义@CachePut方法的时候,一定要将save对象返回,也就是方法return save对象,否则在后续get的时候,可能出现对象获取null的情况!切记切记。

    @CacheEvict

    在删除的时候使用,依然需要声明key,一般这样声明@CacheEvict(key = "#id"),id为我们需要删除对象的主键,也是我们删除方法的入参。

    最后,来一个整体示例:

    import org.springframework.cache.annotation.CacheConfig;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Repository;
    
    import com.example.demo.beans.Book;
    
    @Repository
    @CacheConfig(cacheNames = "DiskCache")
    public class BookDao {
        private Book mBook;
        /**
         *  初始化Book对象。方便做test
         * @return
         */
        public Book initBookCache() {
            mBook = new Book();
            mBook.setId(1);
            mBook.setName("西游记");
            mBook.setAuthor("罗贯中");
            return mBook;
        }
        
            
        @Cacheable
        public Book getBookById(Integer id) {
            System.out.println("getBookById:未使用缓存");
            if(id.equals(mBook.getId())) { // 模拟数据库查询
                return mBook;
            }
            return null;
        }
            
        @CachePut(key = "#mBook.id")
        public Book updateBookById(Book mBook) {
            System.out.println("updateBook:未使用缓存");
            this.mBook = mBook;  // 模拟数据库存储
            return mBook; // 请注意,这里一定要返回!否则缓存数据不会更新!!!!!
        }
        
        @CacheEvict(key = "#id")
        public void deleteBookById(Integer id) {
            System.out.println("deleteBookById:未使用缓存");
            // 这里执行删除操作,同时使用@CacheEvict,将会把缓存中的数据也清除。
            // 删除后,调用getBookById,不会使用缓存。
        }
    
    }
    
    Bean实现

    实现接口Serializabe

    import java.io.Serializable;
    
    public class Book implements Serializable{
            
        private static final long serialVersionUID = 1L;
        
        private Integer id;
        private String name;
        private String author;
        
        // ……getter/setter
    }
    

    SpringBoot测试

    使用通常的测试即可,有如下注解需要了解

    @RunWith(SpringRunner.class)

    @Spring BootTest

    @Autowired

    @Test

    @Before,@BeforeClass,@After,@AfterClass,@Igonore,都可以使用

    在代码中,如果使用了缓存,是不会进入方法的,所以我们需要结合打印来理解。部分注释已经写在代码上了。

    package com.example.demo;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import com.example.demo.beans.Book;
    import com.example.demo.services.BookDao;
    
    import static org.hamcrest.CoreMatchers.containsString;
    
    import org.junit.Assert;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class CacheTest {
    
        @Autowired
        BookDao mBookDao;
    
        @Test
        public void contextLoads() {
            mBookDao.initBookCache();
    
            // 查询测试
            mBookDao.getBookById(1); // 方法打印
            mBookDao.getBookById(1); // 使用缓存:不应打印
    
            // 删除测试
            mBookDao.deleteBookById(1); // 方法打印
            Book mBook = mBookDao.getBookById(1); // 若已经删除,这里再获取,应该打印未使用缓存
    
            // 升级测试
            Book mBook1 = new Book();
            mBook1.setId(1);
            mBook1.setName("西游记2");
            mBook1.setAuthor("罗贯中");
            mBookDao.updateBookById(mBook1);// 打印update
            mBook = mBookDao.getBookById(1); // 不应打印
            System.out.println(mBook.toString()); // 看到升級項
            Assert.assertThat(mBook.toString(),containsString("西游记2"));
    
            // insert测试
            mBook = mBookDao.getBookById(1);
            mBook.setId(10);
            mBookDao.updateBookById(mBook);
            mBook = mBookDao.getBookById(10); // 应打印
            System.out.println(mBook.toString()); 
            Assert.assertThat(mBook.toString(), containsString("id:10"));
        }
    }
    

    问题:

    Eclipse中出现问题:no tests found with test runner 'JUnit 5'

    解决:

    问题在Eclipse中出现,问题在于需要解决JUnit5切换为4。解决办法是:在对应的项目,右键->Run Configurations ,左面选择JUnit,右面就会看到有个Test runner的选项,选择JUnit4。

    提示不推荐使用Assert类型

    解决:把import junit.framework.TestCase;的引入,改为import org.junit.Assert;

    引用

    相关文章

      网友评论

        本文标题:09-SpringBoot的缓存-1

        本文链接:https://www.haomeiwen.com/subject/cfludctx.html