美文网首页java高级开发
SpringBoot缓存(Ehcache)

SpringBoot缓存(Ehcache)

作者: 老鼠AI大米_Java全栈 | 来源:发表于2020-10-14 17:32 被阅读0次

Spring 3.1 中开始对缓存提供支持,核心思路是对方法的缓存,当我们调用一个方法时,将方法的参数和返回值作为 key/value 缓存起来,当再次调用该方法时,如果缓存中有数据,就直接从缓存中获取,否则再去执行该方法。不过并未提供缓存的实现,可以自由选择缓存的实现,目前 Spring Boot 支持的缓存有 JCache、EhCache 2.x、Redis等。不过无论使用那种缓存实现,不同的只是缓存配置,开发者使用的缓存注解都是一样的。

Ehchche 依赖及配置

  1. 首先编辑项目的 pom.xml 文件,添加 spring-boot-starter-cache 依赖以及 Ehcache 依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>
  1. 添加缓存配置文件
    在 resources 目录下创建 ehcache.xml 文件作为 Ehcache 缓存的配置文件,内容如下:
    (1)如果 Ehcache 的依赖存在,并且在 classpath 下又一个名为 encache.xml 的 Ehcache 配置文件,那么 EhCacheManager 将会自动作为缓存的实现。
    (2)这是一个常规的 Ehcache 配置文件,提供了两个缓存策略,一个是默认的,另一个名为 book_cache。具体参数作用如下:
  • name:缓存名称
  • maxElementsInMemory:缓存最大个数
  • eternal:缓存对象是否永久有效,一但设置了永久有效,timeout 将不起作用。
  • timeToIdleSeconds:缓存对象在失效前的允许闲置时间(单位:秒)。仅当 eternal=false 对象不是永久有效时,该属性才生效。默认值是 0,也就是可闲置时间无穷大。
  • timeToLiveSeconds:缓存对象在失效前允许存活时间(单位:秒)。仅当 eternal=false 对象不是永久有效时,该属性才生效。默认值是 0,也就是存活时间无穷大。
  • overflowToDisk:表示内存中的数量达到 maxElementsInMemory 时,Ehcache 是否将对象写到磁盘中。
  • diskPersistent:是否缓存虚拟机重启期数据。
  • diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是 120 秒。

(3)如果我们想要自定 Ehcache 配置文件的名称和位置,可以在 application.properties 中添加如下配置:

  • spring.cache.ehcache.config=classpath:config/another-config.xml
<ehcache>
    <diskStore path="java.io.tmpdir/cache"/>
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />
    <cache
            name="test-cache"
            maxElementsInMemory="1000"
            eternal="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="30"
            overflowToDisk="false"
            diskPersistent="false"
            memoryStoreEvictionPolicy="LRU">
    </cache>
</ehcache>

启用缓存

  1. 开启缓存
    在项目的入口类上添加 @EnableCaching 注解开启缓存,代码如下:
@SpringBootApplication
@EnableCaching
public class DemoApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context
                = SpringApplication.run(DemoApplication.class, args);
    }
}
  1. 开始测试
    创建一个 Service 并添加相关的缓存注解:
    (1) 添加 @CacheConfig 注解指定使用的缓存的名字,这个配置可选。若不使用 @CacheConfig 注解,则直接在 @Cacheable 注解中指明缓存名字。
    (2) 在方法上添加 @Cacheable 注解表示对该方法进行缓存:
    默认情况下,缓存的 key 是方法的参数,缓存的 value 是方法的返回值。
    当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有则直接使用缓存数据,该方法不会执行。
    否则执行该方法,执行成功后将返回值缓存起来。
    但若是在当前类中调用该方法,则缓存不会生效。
    (3)@Cacheable 注解中还有一个属性 condition 用来描述缓存的执行时机,例如:
    @Cacheable(condition="#id%2-0") 表示当 id 对 2 取模为 0 时才进行缓存,否则不缓存。
    (4)除了上面这种使用参数定义 key 的方式之外,Spring 还提供了一个 root 对象用来生成 key。下面是一些用法实例:
    #root.methodName:当前方法名
    #root.method.name:当前方法对象
    #root.caches[0].name:当前方法使用的缓存
    #root.target:当前被调用的对象
    #root.targetClass:当前被调用的对象的 class
    #root.args[0]:当前方法参数数组
    (5)@Cacheput 注解一般用于数据更新方法上:
    与 @Cacheable 注解不同,添加了 @Cacheput 注解的方法每次在执行时都不去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来。
    如果该 key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样可以避免再次加载数据时获取到脏数据。
    同时,@Cacheput 具有和 @Cacheable 类似的属性,这里不再赘述。
    (6)@CacheEvict 注解一般用于删除方法上,表示移除一个 key 对应的缓存。@CacheEvict 注解有两个特殊的属性 allEntries 和 beforelnvocation:
    allEntries 表示是否将所有的缓存数据都移除,默认为 false。
    beforelnvocation 表示是否在方法执行之前移除缓存中的数据,默认为 false,即在方法执行之后移除缓存中的数据。
@Service
public class UserService {
    ...
    //删除用户数据
    @CacheEvict(value = DEMO_CACHE_NAME,key = "'user_'+#id")//这是清除缓存
    public void delete(String id){
        userDao.delete(id);
    }

    //更新用户数据
    @CachePut(value = DEMO_CACHE_NAME,key = "'user_'+#user.getId()")
    public User update(User user) throws CacheException {
        User user1 = userDao.findById(user.getId());
        if (null == user1){
            throw new CacheException("Not Find");
        }
        userDao.update(user);
        return user;
    }

    //查找用户数据
    @Cacheable(value=DEMO_CACHE_NAME,key="'user_'+#id")
    public User findById(String id){
        //若找不到缓存将打印出提示语句
        System.err.println("直接查询数据库!"+id);
        User user = userDao.findById(id);
        return user;
    }

    //保存用户数据
    @CacheEvict(value=DEMO_CACHE_NAME,key=CACHE_KEY)
    public void save(User user){
        userDao.save(user);
    }
}

测试缓存

  1. 使用controller测试
@RequestMapping("/test")
    public String EhcacheTest(){
        System.out.println("====生成第一个用户====");
        User user1 = new User(UUID.randomUUID().toString(),"熊大",18);
        userService.save(user1);
        //第一次查询
        System.out.println(userService.findById(user1.getId()));
        //通过缓存查询
        System.out.println(userService.findById(user1.getId()));
        System.out.println("====修改数据====");
        User user2 = new User(user1.getId(),"熊二",20);
        try {
            userService.update(user2);
        } catch (CacheException e){
            e.printStackTrace();
        }
        System.out.println(userService.findById(user1.getId()));
        return user1.getId();
    }

执行结果下如下:


image.png

可以看到第一次从DB查询了数据,第二次则从缓存读取,当更新DB数据时同时也更新了缓存数据,再次取数据仍然走缓存。

灵活控制缓存

对于高频数据,一般希望长时间缓存,对于临时数据,如验证码,token等,一般需要一个指定的时间,到期则取消。
可以通过一个类去控制缓存的有效时间,一般指定timeToIdleSeconds=0表示数据一直有效,timeToLiveSeconds=X秒指定过期时间,如下:

public class EhCacheUtil {

    private static final String path = "./src/main/resources/ehcache/ehcache.xml";
    private CacheManager cacheManager;
    private static EhCacheUtil ehCache;

    private EhCacheUtil(String path) {
        cacheManager = CacheManager.create(path);
    }

    public static EhCacheUtil getInstance() {
        if (ehCache== null) {
            ehCache= new EhCacheUtil(path);
        }
        return ehCache;
    }

    // 默认的缓存存在时间(秒)
    private static final int DEFAULT_LIVE_SECOND = 1 * 10;

    /**
     * @Author: dian
     * @Date: 2020/5/25 18:29
     * @Description: 添加缓存
     */
    public void set(String key,String value){
        Cache cache = cacheManager.getCache("test-cache");
        Element element = new Element(key,value,0,DEFAULT_LIVE_SECOND);
        cache.put(element);
    }

    /**
     * @Author: dian
     * @Date: 2020/5/25 18:29
     * @Description: 添加缓存
     * @param timeToLiveSeconds 缓存生存时间(秒)
     */
    public void set(String key,String value,int timeToLiveSeconds){
        Cache cache = cacheManager.getCache("test-cache");
        Element element = new Element(key,value,0,timeToLiveSeconds);
        cache.put(element);
    }

    /**
     * @Author: dian
     * @Date: 2020/5/25 18:29
     * @Description: 添加缓存
     * @param timeToIdleSeconds 对象空闲时间,指对象在多长时间没有被访问就会失效。 * 只对eternal为false的有效。传入0,表示一直可以访问。以秒为单位。
     * @param timeToLiveSeconds 缓存生存时间(秒) 只对eternal为false的有效
     */
    public void set(String key,String value,int timeToIdleSeconds,int timeToLiveSeconds){
        Cache cache = cacheManager.getCache("test-cache");
        Element element = new Element(key,value,timeToIdleSeconds,timeToLiveSeconds);
        cache.put(element);
    }

    /**
     * @Author: dian
     * @Date: 2020/5/25 18:30
     * @Description: 获取缓存
     */
    public String get(String key){
        Cache cache = cacheManager.getCache("test-cache");
        Element element = cache.get(key);
        if(element == null){
            return null;
        }
        return (String) element.getObjectValue();
    }

    /**
     * @Author: dian
     * @Date: 2020/5/26 8:49
     * @Description: 获取缓存个数
     */
    public int geSize(){
        Cache cache = cacheManager.getCache("test-cache");
        return cache.getSize();
    }

    /**
     * @Author: dian
     * @Date: 2020/5/26 9:32
     * @Description: 删除缓存
     */
    public void delete(String key){
        Cache cache = cacheManager.getCache("test-cache");
        cache.remove(key);
    }
}

然后在controller灵活的调用

@RequestMapping("/get")
    public String get(){
        EhCacheUtil ehCacheUtil = EhCacheUtil.getInstance();
        String value = ehCacheUtil.get("dian");
        return value;
    }

    @RequestMapping("/set")
    public String set(){
        EhCacheUtil ehCacheUtil = EhCacheUtil.getInstance();
        ehCacheUtil.set("dian","你好",0, 600);
        return "成功添加";
    }

对于无状态的系统,可以方便的缓存token,验证码等短时有效的数据,方便存取。

相关文章

网友评论

    本文标题:SpringBoot缓存(Ehcache)

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