SpringBoot 整合 Ehcache 实现缓存

前文

SpringBoot 整合 JDBCTemplate

SpringBoot 整合 Redis 的简单使用

Spring 提供的缓存

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

  • JCache(JSR-107)
  • EhCache 2.X
  • Hazelcast
  • Infinispan
  • Couchbase
  • Redis
  • Caffeine
  • Simple

EhCache

EhCache 缓存在 Java 开发领域已是久负盛名,在 SpringBoot 中,只需要一个配置文件就可以将 EhCache 集成到项目中

添加依赖

<dependencies>
	<!-- 导入 web -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	
	<!-- 导入 SpringBoot 与 Cache 的整合包-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-cache</artifactId>
	</dependency>
	
	<!-- 导入 Ehcache -->
	<dependency>
		<groupId>net.sf.ehcache</groupId>
		<artifactId>ehcache</artifactId>
	</dependency>
</dependencies>

创建缓存配置文件

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

<ehcache>
    <diskStore path="java.io.tmpdir/cache" />
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="false"
        diskPersistent="false"
        diskExpiryThreadIntervalSeconds="120"
    />

    <!-- name 表示缓存名称 -->
    <!-- maxElementsInMemory 表示缓存的最大个数 -->
    <!-- eternal 表示缓存对象是否永久有效,一旦设置永久有效,那么 timeout 将不起作用 -->
    <!-- timeToIdleSeconds 表示缓存对象在失效前的允许闲置时间(单位:s),当 eternal 设置为 false 时该属性才生效 -->
    <!-- timeToLiveSeconds 表示缓存对象在失效前允许存活的时间(单位:s),当 eternal 设置为 false 时该属性才生效 -->
    <!-- overflowToDisk 表示当内存中的对象数量达到 maxElementsInMemory 时,Ehcache 是否将对象写进磁盘中  -->
    <!-- diskExpiryThreadIntervalSeconds 表示磁盘失效线程运行时间间隔 -->
    <cache name="user_cache"
           maxElementsInMemory="10000"
           eternal="true"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           overflowToDisk="true"
           diskPersistent="true"
           diskExpiryThreadIntervalSeconds="600"
    />
</ehcache>

开启缓存

在启动类中添加 @EnableCaching 注解开启缓存,代码如下:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching // 开启缓存
public class DemoApplication { 
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class,args);
    }
}

创建实体类

package com.example.demo.entity;

import java.io.Serializable;

public class User implements Serializable {

    private Integer id;

    private String name;

    private String info;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {`在这里插入代码片`
        this.info = info;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}

创建 UserDao

package com.example.demo.dao;

import com.example.demo.entity.User;
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;

@Repository
@CacheConfig(cacheNames = "user_cache")
public class UserDao {

    @Cacheable
    public User getUserById(Integer id) {
        System.out.println("执行 getUserById 方法");
        User user = new User();
        user.setId(id);
        user.setName("John");
        user.setInfo("I like basketball");
        return user;
    }

    @CachePut(key = "#user.id")
    public User updateUserById(User user) {
        System.out.println("执行 updateUserById 方法");
        user.setInfo("我爱打篮球");
        return user;
    }

    @CacheEvict(key = "#id")
    public void deleteUserById(Integer id) {
        System.out.println("执行 deleteUserById 方法");
    }
}

说明:

  • @CacheConfig 注解表示指明的缓存的名字,这个配置是可选的,若不使用 @CacheConfig 注解,则直接在 @Cacheable 注解中指明缓存的名字
  • @Cacheable 注解表示对该方法进行缓存,默认情况下,缓存的 key 是方法的参数,缓存的 value 是方法的返回值。当开发者在其他类中调用该方法时,首先会根据调用参数查看缓存中是否有相关数据,若有,则直接使用缓存数据,该方法不会执行,否则执行该方法,执行成功后将返回值缓存起来,但如果是在当前类中调用该方法,则缓存不会生效
  • @Cacheable 注解中还有一个属性 condition 用来描述的是缓存的执行时机,例如 @Cacheable(condition="#id%2==0") 表示当 id 对 2 取模为 0 时才进行缓存,否则不缓存
  • 如果开发者不想使用默认的 key,也可以自己自定义 key,另外 Spring 还提供了一个 root 对象用来生成 key
  • @CachePut 注解一般用于数据更新的方法上,与 @Cacheable 注解不同,添加了 @CachePut 注解的方法每次在执行时都不会去检查缓存中是否有数据,而是直接执行方法,然后将方法的执行结果缓存起来,如果该 key 对应的数据已经被缓存起来了,就会覆盖之前的数据,这样做的目的是可以避免再次加载数据时获取到的脏数据。同时,@CachePut 具有和 @Cacheable 类似的属性
  • @CacheEvict 注解一般用于删除方法上,表示移除一个 key 对应的缓存。@CacheEvict 注解有两个特殊的属性:allEntries 和 beforeInvocation,其中 allEntries 表示是否将所有的缓存数据移除,默认为 false,beforeInvocation 表示是否在方法执行之前移除缓存中的数据,默认为 false,即在方法执行之后移除缓存中的数据
属性名称 属性描述 用法示例
methodName 当前方法名 #root.methodName
method 当前方法对象 #root.method.name
caches 当前是使用的缓存 #root.caches[0].name
target 当前被调用的对象 #root.target

自定义缓存 key 的生成器

如果上面的这些 key 不能够满足开发需求,开发者也可以自定义缓存 key 的生成器 KeyGenerator,代码如下:

使用方法直接注入即可

package com.example.demo.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Arrays;

@Component
public class MyKeyGenerator implements KeyGenerator {
    @Override
    public Object generate(Object o, Method method, Object... objects) {
        return Arrays.toString(objects);
    }
}

然后为了测试该缓存 key 的生成器,我们写一个 KeyDao,代码如下:

package com.example.demo.dao;

import com.example.demo.config.MyKeyGenerator;
import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
@CacheConfig(cacheNames = "user_cache")
public class KeyDao {

    @Autowired
    MyKeyGenerator myKeyGenerator;

    @Cacheable(keyGenerator = "myKeyGenerator")
    public User getUserById(Integer id) {
        System.out.println("getUserById");
        User user = new User();
        user.setId(1);
        user.setName("ling");
        user.setInfo("我爱看书");
        return user;
    }
}

创建测试类

package com.example.demo;

import com.example.demo.dao.KeyDao;
import com.example.demo.dao.UserDao;
import com.example.demo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class DemoApplicationTests {

	@Autowired
	private UserDao userDao;

	@Autowired
	private KeyDao keyDao;

	@Test
	void contextLoads() {
	
		// UserDao
		userDao.getUserById(1);
		userDao.deleteUserById(1);
		
		// KeyDao
		User user = keyDao.getUserById(1);
		System.out.println("user : " + user);
		
		User users = new User();
		users.setName("lisa");
		users.setInfo("我爱弹吉他");
		users.setId(1);
		userDao.updateUserById(users);
		User userById = userDao.getUserById(1);
		System.out.println("user : " + userById);
	}

}

打印输出如下:
在这里插入图片描述

发布了227 篇原创文章 · 获赞 1032 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/Woo_home/article/details/104210624