前文
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);
}
}
打印输出如下:
转载:https://blog.csdn.net/Woo_home/article/details/104210624
查看评论