欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

SpringBoot实践之---集成Spring cache和EhCache

程序员文章站 2024-02-29 09:41:22
...

声明式缓存

Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean。

Spring Boot 为我们自动配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。

默认使用 ConcurrenMapCacheManager

在我们不使用其他第三方缓存依赖的时候,springboot自动采用ConcurrenMapCacheManager作为缓存管理器。

引入依赖

build.gradle中

    //支持Spring cache
    compile("org.springframework.boot:spring-boot-starter-cache")

maven的pom.xml中

<dependency>          
   <groupId>org.springframework.boot</groupId>          
   <artifactId>spring-boot-starter-cache</artifactId> 
</dependency>

编写实体类:

package com.great.cache;

public class Book {
    private String isbn;
    private String title;

    public Book(String isbn, String title) {
        this.isbn = isbn;
        this.title = title;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

接口类和接口实现类

public interface BookRepository {

    Book getByIsbn(String isbn);

    Book getByIsbns(String isbn);

    Book setByIsbns(String isbn);

}

-------------------------

import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;

@Component
public class SimpleBookRepository implements BookRepository {

    @Override
    @Cacheable("bookstest")
    public Book getByIsbn(String isbn) {
        simulateSlowService();
        return new Book(isbn, "Some book");
    }

    @Cacheable(value = "book", key = "#isbn")
    public Book getByIsbns(String isbn) {
        simulateSlowService();
        return new Book(isbn, "Some book="+isbn);
    }

    @CachePut(value = "book", key = "#isbn")
    public Book setByIsbns(String isbn) {
        simulateSlowService();
        return new Book(isbn, "Some book222="+isbn);
    }
这个你可以写一个很复杂的数据查询操作,比如操作mysql、nosql等等。为了演示这个栗子,我只做了一下线程的延迟操作,当作是查询数据库的时间。

测试验证类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;


@Component
public class AppRunner implements CommandLineRunner {

    private static final Logger logger = LoggerFactory.getLogger(AppRunner.class);

    private final BookRepository bookRepository;

    public AppRunner(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    @Override
    public void run(String... args) throws Exception {
        logger.info(".... Fetching books");
        logger.info("isbn-1234 -->" + bookRepository.getByIsbns("isbn-1234").getTitle());
        logger.info("isbn-4567 -->" + bookRepository.getByIsbns("isbn-4567").getTitle());
        logger.info("isbn-1234 -->" + bookRepository.getByIsbns("isbn-1234").getTitle());
        logger.info("isbn-4567 -->" + bookRepository.setByIsbns("isbn-4567").getTitle());
        logger.info("isbn-1234 -->" + bookRepository.getByIsbns("isbn-1234").getTitle());
        logger.info("isbn-1234 -->" + bookRepository.getByIsbns("isbn-4567").getTitle());
    }

}

运行结果为:

2018-02-28 17:21:31.600  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : .... Fetching books
2018-02-28 17:21:34.613  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-1234 -->Some book=isbn-1234
2018-02-28 17:21:37.614  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-4567 -->Some book=isbn-4567
2018-02-28 17:21:37.615  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-1234 -->Some book=isbn-1234
2018-02-28 17:21:40.617  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-4567 -->Some book222=isbn-4567
2018-02-28 17:21:40.617  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-1234 -->Some book=isbn-1234
2018-02-28 17:21:40.617  INFO 51100 --- [  restartedMain] com.great.cache.AppRunner                : isbn-4567 -->Some book222=isbn-4567


补充:要使缓存生效,还必须在springboot主启动类上面加上 @EnableCaching

@EnableCaching
public class MainApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

另外使用缓存的地方需要相应的标注

对于缓存的操作,主要有:@Cacheable、@CachePut、@CacheEvict。 

@Cacheable 
Spring 在执行 @Cacheable 标注的方法前先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,执行该方法并将方法返回值放进缓存。 
参数: value缓存名、 key缓存键值、 condition满足缓存条件、unless否决缓存条件 

@Cacheable(value = "user", key = "#id")
public User findById(final Long id) {
    System.out.println("cache miss, invoke find by id, id:" + id);
    for (User user : users) {
        if (user.getId().equals(id)) {
            return user;
        }
    }
    return null;
}


@CachePut 
和 @Cacheable 类似,但会把方法的返回值放入缓存中, 主要用于数据新增和修改方法。 
@CachePut(value = "user", key = "#user.id")
public User save(User user) {
    users.add(user);
    return user;
}


@CacheEvict 
方法执行成功后会从缓存中移除相应数据。 
参数: value缓存名、 key缓存键值、 condition满足缓存条件、 unless否决缓存条件、 allEntries是否移除所有数据(设置为true时会移除所有缓存) 
@CacheEvict(value = "user", key = "#user.id") // 移除指定key的数据
public User delete(User user) {
    users.remove(user);
    return user;
}

@CacheEvict(value = "user", allEntries = true) // 移除所有数据
public void deleteAll() {
    users.clear();
}

使用 EhCache

引入EhCache,在build.gradle中加入

    //支持Encache
    compile("net.sf.ehcache:ehcache")

在resources目录下创建了ehcache.xml的配置文件,然后在application.properties 设置type为ehcache(intellij有明确的提示):

<ehcache>
    <!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下 -->
    <diskStore path="java.io.tmpdir"/>

    <!-- 设定缓存的默认数据过期策略 -->

    <cache name="book" maxElementsInMemory="10000" />


    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            overflowToDisk="true"
            timeToIdleSeconds="10"
            timeToLiveSeconds="120"
            diskPersistent="false"
            memoryStoreEvictionPolicy="LRU"
            diskExpiryThreadIntervalSeconds="120"/>

    <!-- maxElementsInMemory 内存中最大缓存对象数,看着自己的heap大小来搞 -->
    <!-- eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false -->
    <!-- maxElementsOnDisk:硬盘中最大缓存对象数,若是0表示无穷大 -->
    <!-- overflowToDisk:true表示当内存缓存的对象数目达到了maxElementsInMemory界限后,
    会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。-->
    <!-- diskSpoolBufferSizeMB:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。-->
    <!-- diskPersistent:是否缓存虚拟机重启期数据  -->
    <!-- diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认为120秒 -->

    <!-- timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,
    如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,
    EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,
    则表示对象可以无限期地处于空闲状态 -->

    <!-- timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,
    如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,
    EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,
    则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义 -->

    <!-- memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,
    Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、
    FIFO(先进先出)、LFU(最少访问次数)。-->

</ehcache>

application.properties 中增加

#encache配置
spring.cache.type=ehcache
spring.cache.ehcache.config=ehcache.xml

代码层级使用同Spring cache