upload,cache-redis,cache-ehcache
spring-boot-demo-upload
文件上传以及如何上传文件至七牛云平台。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>spring-boot-demo-upload</artifactId><version>1.0.0-SNAPSHOT</version><packaging>jar</packaging><parent><groupId>com.xkcoding</groupId><artifactId>spring-boot-demo</artifactId><version>1.0.0-SNAPSHOT</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><dependency><groupId>com.qiniu</groupId><artifactId>qiniu-java-sdk</artifactId><version>[7.2.0, 7.2.99]</version></dependency></dependencies><build><finalName>spring-boot-demo-upload</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
yml
server:port: 8080servlet:context-path: /demo
qiniu:## 此处填写你自己的七牛云 access keyaccessKey:## 此处填写你自己的七牛云 secret keysecretKey:## 此处填写你自己的七牛云 bucketbucket:## 此处填写你自己的七牛云 域名prefix:
spring:servlet:multipart:enabled: truelocation: /Users/yangkai.shen/Documents/code/back-end/spring-boot-demo/spring-boot-demo-upload/tmpfile-size-threshold: 5MBmax-file-size: 20MB
UploadConfig.java
@Configuration
@ConditionalOnClass({Servlet.class, StandardServletMultipartResolver.class, MultipartConfigElement.class})
@ConditionalOnProperty(prefix = "spring.http.multipart", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(MultipartProperties.class)
public class UploadConfig {@Value("${qiniu.accessKey}")private String accessKey;@Value("${qiniu.secretKey}")private String secretKey;@Autowiredprivate final MultipartProperties multipartProperties;/* 上传配置*/@Bean@ConditionalOnMissingBeanpublic MultipartConfigElement multipartConfigElement() {return this.multipartProperties.createMultipartConfig();}/* 注册解析器*/@Bean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)@ConditionalOnMissingBean(MultipartResolver.class)public StandardServletMultipartResolver multipartResolver() {StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());return multipartResolver;}/* 华东机房*/@Beanpublic com.qiniu.storage.Configuration qiniuConfig() {return new com.qiniu.storage.Configuration(Zone.zone0());}/* 构建一个七牛上传工具实例*/@Beanpublic UploadManager uploadManager() {return new UploadManager(qiniuConfig());}/* 认证信息实例*/@Beanpublic Auth auth() {return Auth.create(accessKey, secretKey);}/* 构建七牛空间管理实例*/@Beanpublic BucketManager bucketManager() {return new BucketManager(auth(), qiniuConfig());}
}
UploadController.java
@RestController
@Slf4j
@RequestMapping("/upload")
public class UploadController {@Value("${spring.servlet.multipart.location}")private String fileTempPath;@Value("${qiniu.prefix}")private String prefix;@Autowiredprivate final IQiNiuService qiNiuService;@PostMapping(value = "/local", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)public Dict local(@RequestParam("file") MultipartFile file) {if (file.isEmpty()) {return Dict.create().set("code", 400).set("message", "文件内容为空");}String fileName = file.getOriginalFilename();String rawFileName = StrUtil.subBefore(fileName, ".", true);String fileType = StrUtil.subAfter(fileName, ".", true);String localFilePath = StrUtil.appendIfMissing(fileTempPath, "/") + rawFileName + "-" + DateUtil.current(false) + "." + fileType;try {file.transferTo(new File(localFilePath));} catch (IOException e) {log.error("【文件上传至本地】失败,绝对路径:{}", localFilePath);return Dict.create().set("code", 500).set("message", "文件上传失败");}log.info("【文件上传至本地】绝对路径:{}", localFilePath);return Dict.create().set("code", 200).set("message", "上传成功").set("data", Dict.create().set("fileName", fileName).set("filePath", localFilePath));}@PostMapping(value = "/yun", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)public Dict yun(@RequestParam("file") MultipartFile file) {if (file.isEmpty()) {return Dict.create().set("code", 400).set("message", "文件内容为空");}String fileName = file.getOriginalFilename();String rawFileName = StrUtil.subBefore(fileName, ".", true);String fileType = StrUtil.subAfter(fileName, ".", true);String localFilePath = StrUtil.appendIfMissing(fileTempPath, "/") + rawFileName + "-" + DateUtil.current(false) + "." + fileType;try {file.transferTo(new File(localFilePath));Response response = qiNiuService.uploadFile(new File(localFilePath));if (response.isOK()) {JSONObject jsonObject = JSONUtil.parseObj(response.bodyString());String yunFileName = jsonObject.getStr("key");String yunFilePath = StrUtil.appendIfMissing(prefix, "/") + yunFileName;FileUtil.del(new File(localFilePath));log.info("【文件上传至七牛云】绝对路径:{}", yunFilePath);return Dict.create().set("code", 200).set("message", "上传成功").set("data", Dict.create().set("fileName", yunFileName).set("filePath", yunFilePath));} else {log.error("【文件上传至七牛云】失败,{}", JSONUtil.toJsonStr(response));FileUtil.del(new File(localFilePath));return Dict.create().set("code", 500).set("message", "文件上传失败");}} catch (IOException e) {log.error("【文件上传至七牛云】失败,绝对路径:{}", localFilePath);return Dict.create().set("code", 500).set("message", "文件上传失败");}}
}
QiNiuServiceImpl.java
@Service
@Slf4j
public class QiNiuServiceImpl implements IQiNiuService, InitializingBean {private final UploadManager uploadManager;private final Auth auth;@Value("${qiniu.bucket}")private String bucket;private StringMap putPolicy;@Autowiredpublic QiNiuServiceImpl(UploadManager uploadManager, Auth auth) {this.uploadManager = uploadManager;this.auth = auth;}/* 七牛云上传文件 @param file 文件* @return 七牛上传Response* @throws QiniuException 七牛异常*/@Overridepublic Response uploadFile(File file) throws QiniuException {Response response = this.uploadManager.put(file, file.getName(), getUploadToken());int retry = 0;while (response.needRetry() && retry < 3) {response = this.uploadManager.put(file, file.getName(), getUploadToken());retry++;}return response;}@Overridepublic void afterPropertiesSet() {this.putPolicy = new StringMap();putPolicy.put("returnBody", "{\\"key\\":\\"$(key)\\",\\"hash\\":\\"$(etag)\\",\\"bucket\\":\\"$(bucket)\\",\\"width\\":$(imageInfo.width), \\"height\\":${imageInfo.height}}");}/* 获取上传凭证 @return 上传凭证*/private String getUploadToken() {return this.auth.uploadToken(bucket, null, 3600, putPolicy);}
}
index.html
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>spring-boot-demo-upload</title><!-- import Vue.js --><script src="https://cdn.bootcss.com/vue/2.5.17/vue.min.js"></script><!-- import stylesheet --><link href="https://cdn.bootcss.com/iview/3.1.4/styles/iview.css" rel="stylesheet"><!-- import iView --><script src="https://cdn.bootcss.com/iview/3.1.4/iview.min.js"></script>
</head>
<body>
<div id="app"><Row :gutter="16" style="background:#eee;padding:10%"><i-col span="12"><Card style="height: 300px"><p slot="title"><Icon type="ios-cloud-upload"></Icon>本地上传</p><div style="text-align: center;"><Upload:before-upload="handleLocalUpload"action="/demo/upload/local"ref="localUploadRef":on-success="handleLocalSuccess":on-error="handleLocalError"><i-button icon="ios-cloud-upload-outline">选择文件</i-button></Upload><i-buttontype="primary"@click="localUpload":loading="local.loadingStatus":disabled="!local.file">{{ local.loadingStatus ? '本地文件上传中' : '本地上传' }}</i-button></div><div><div v-if="local.log.status != 0">状态:{{local.log.message}}</div><div v-if="local.log.status === 200">文件名:{{local.log.fileName}}</div><div v-if="local.log.status === 200">文件路径:{{local.log.filePath}}</div></div></Card></i-col><i-col span="12"><Card style="height: 300px;"><p slot="title"><Icon type="md-cloud-upload"></Icon>七牛云上传</p><div style="text-align: center;"><Upload:before-upload="handleYunUpload"action="/demo/upload/yun"ref="yunUploadRef":on-success="handleYunSuccess":on-error="handleYunError"><i-button icon="ios-cloud-upload-outline">选择文件</i-button></Upload><i-buttontype="primary"@click="yunUpload":loading="yun.loadingStatus":disabled="!yun.file">{{ yun.loadingStatus ? '七牛云文件上传中' : '七牛云上传' }}</i-button></div><div><div v-if="yun.log.status != 0">状态:{{yun.log.message}}</div><div v-if="yun.log.status === 200">文件名:{{yun.log.fileName}}</div><div v-if="yun.log.status === 200">文件路径:{{yun.log.filePath}}</div></div></Card></i-col></Row>
</div>
<script>new Vue({el: '#app',data: {local: {// 选择文件后,将 beforeUpload 返回的 file 保存在这里,后面会用到file: null,// 标记上传状态loadingStatus: false,log: {status: 0,message: "",fileName: "",filePath: ""}},yun: {// 选择文件后,将 beforeUpload 返回的 file 保存在这里,后面会用到file: null,// 标记上传状态loadingStatus: false,log: {status: 0,message: "",fileName: "",filePath: ""}}},methods: {// beforeUpload 在返回 false 或 Promise 时,会停止自动上传,这里我们将选择好的文件 file 保存在 data里,并 return falsehandleLocalUpload(file) {this.local.file = file;return false;},// 这里是手动上传,通过 $refs 获取到 Upload 实例,然后调用私有方法 .post(),把保存在 data 里的 file 上传。// iView 的 Upload 组件在调用 .post() 方法时,就会继续上传了。localUpload() {this.local.loadingStatus = true; // 标记上传状态this.$refs.localUploadRef.post(this.local.file);},// 上传成功后,清空 data 里的 file,并修改上传状态handleLocalSuccess(response) {this.local.file = null;this.local.loadingStatus = false;if (response.code === 200) {this.$Message.success(response.message);this.local.log.status = response.code;this.local.log.message = response.message;this.local.log.fileName = response.data.fileName;this.local.log.filePath = response.data.filePath;this.$refs.localUploadRef.clearFiles();} else {this.$Message.error(response.message);this.local.log.status = response.code;this.local.log.message = response.message;}},// 上传失败后,清空 data 里的 file,并修改上传状态handleLocalError() {this.local.file = null;this.local.loadingStatus = false;this.$Message.error('上传失败');},// beforeUpload 在返回 false 或 Promise 时,会停止自动上传,这里我们将选择好的文件 file 保存在 data里,并 return falsehandleYunUpload(file) {this.yun.file = file;return false;},// 这里是手动上传,通过 $refs 获取到 Upload 实例,然后调用私有方法 .post(),把保存在 data 里的 file 上传。// iView 的 Upload 组件在调用 .post() 方法时,就会继续上传了。yunUpload() {this.yun.loadingStatus = true; // 标记上传状态this.$refs.yunUploadRef.post(this.yun.file);},// 上传成功后,清空 data 里的 file,并修改上传状态handleYunSuccess(response) {this.yun.file = null;this.yun.loadingStatus = false;if (response.code === 200) {this.$Message.success(response.message);this.yun.log.status = response.code;this.yun.log.message = response.message;this.yun.log.fileName = response.data.fileName;this.yun.log.filePath = response.data.filePath;this.$refs.yunUploadRef.clearFiles();} else {this.$Message.error(response.message);this.yun.log.status = response.code;this.yun.log.message = response.message;}},// 上传失败后,清空 data 里的 file,并修改上传状态handleYunError() {this.yun.file = null;this.yun.loadingStatus = false;this.$Message.error('上传失败');}}})
</script>
</body>
</html>
参考
- Spring 官方文档:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#howto-multipart-file-upload-configuration
- 七牛云官方文档:https://developer.qiniu.com/kodo/sdk/1239/java#5
spring-boot-demo-cache-redis
整合redis,操作redis中的数据,并使用redis缓存数据。连接池使用 Lettuce。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>spring-boot-demo-cache-redis</artifactId><version>1.0.0-SNAPSHOT</version><packaging>jar</packaging><parent><groupId>com.xkcoding</groupId><artifactId>spring-boot-demo</artifactId><version>1.0.0-SNAPSHOT</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- 对象池,使用redis时必须引入 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- 引入 jackson 对象json转换 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-json</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><build><finalName>spring-boot-demo-cache-redis</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
application.yml
spring:redis:host: localhost# 连接超时时间(记得添加单位,Duration)timeout: 10000ms# Redis默认情况下有16个分片,这里配置具体使用的分片# database: 0lettuce:pool:# 连接池最大连接数(使用负值表示没有限制) 默认 8max-active: 8# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1max-wait: -1ms# 连接池中的最大空闲连接 默认 8max-idle: 8# 连接池中的最小空闲连接 默认 0min-idle: 0cache:# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配type: redis
logging:level:com.xkcoding: debug
RedisConfig.java
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@EnableCaching
public class RedisConfig {/* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化*/@Beanpublic RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {RedisTemplate<String, Serializable> template = new RedisTemplate<>();template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setConnectionFactory(redisConnectionFactory);return template;}/* 配置使用注解的时候缓存配置,默认是序列化反序列化的形式,加上此配置则为 json 形式*/@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {// 配置序列化RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();RedisCacheConfiguration redisCacheConfiguration = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(redisCacheConfiguration).build();}
}
UserServiceImpl.java
@Service
@Slf4j
public class UserServiceImpl implements UserService {/* 模拟数据库*/private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();/* 初始化数据*/static {DATABASES.put(1L, new User(1L, "user1"));DATABASES.put(2L, new User(2L, "user2"));DATABASES.put(3L, new User(3L, "user3"));}/* 保存或修改用户 @param user 用户对象* @return 操作结果*/@CachePut(value = "user", key = "#user.id")@Overridepublic User saveOrUpdate(User user) {DATABASES.put(user.getId(), user);log.info("保存用户【user】= {}", user);return user;}/* 获取用户 @param id key值* @return 返回结果*/@Cacheable(value = "user", key = "#id")@Overridepublic User get(Long id) {// 我们假设从数据库读取log.info("查询用户【id】= {}", id);return DATABASES.get(id);}/* 删除 @param id key值*/@CacheEvict(value = "user", key = "#id")@Overridepublic void delete(Long id) {DATABASES.remove(id);log.info("删除用户【id】= {}", id);}
}
RedisTest.java
主要测试使用
RedisTemplate
操作Redis
中的数据:
- opsForValue:对应 String(字符串)
- opsForZSet:对应 ZSet(有序集合)
- opsForHash:对应 Hash(哈希)
- opsForList:对应 List(列表)
- opsForSet:对应 Set(集合)
- opsForGeo: 对应 GEO(地理位置)
@Slf4j
public class RedisTest extends SpringBootDemoCacheRedisApplicationTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate RedisTemplate<String, Serializable> redisCacheTemplate;/* 测试 Redis 操作*/@Testpublic void get() {// 测试线程安全,程序结束查看redis中count的值是否为1000ExecutorService executorService = Executors.newFixedThreadPool(1000);IntStream.range(0, 1000).forEach(i -> executorService.execute(() -> stringRedisTemplate.opsForValue().increment("count", 1)));stringRedisTemplate.opsForValue().set("k1", "v1");String k1 = stringRedisTemplate.opsForValue().get("k1");log.debug("【k1】= {}", k1);// 以下演示整合,具体Redis命令可以参考官方文档String key = "xkcoding:user:1";redisCacheTemplate.opsForValue().set(key, new User(1L, "user1"));// 对应 String(字符串)User user = (User) redisCacheTemplate.opsForValue().get(key);log.debug("【user】= {}", user);}
}
UserServiceTest.java
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheRedisApplicationTests {@Autowiredprivate UserService userService;/* 获取两次,查看日志验证缓存*/@Testpublic void getTwice() {// 模拟查询id为1的用户User user1 = userService.get(1L);log.debug("【user1】= {}", user1);// 再次查询User user2 = userService.get(1L);log.debug("【user2】= {}", user2);// 查看日志,只打印一次日志,证明缓存生效}/* 先存,再查询,查看日志验证缓存*/@Testpublic void getAfterSave() {userService.saveOrUpdate(new User(4L, "测试中文"));User user = userService.get(4L);log.debug("【user】= {}", user);// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效}/* 测试删除,查看redis是否存在缓存数据*/@Testpublic void deleteUser() {// 查询一次,使redis中存在缓存数据userService.get(1L);// 删除,查看redis是否存在缓存数据userService.delete(1L);}}
参考
- spring-data-redis 官方文档:https://docs.spring.io/spring-data/redis/docs/2.0.1.RELEASE/reference/html/
- redis 文档:https://redis.io/documentation
- redis 中文文档:http://www.redis.cn/commands.html
spring-boot-demo-cache-ehcache
集成 ehcache 使用缓存。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>spring-boot-demo-cache-ehcache</artifactId><version>1.0.0-SNAPSHOT</version><packaging>jar</packaging><parent><groupId>com.xkcoding</groupId><artifactId>spring-boot-demo</artifactId><version>1.0.0-SNAPSHOT</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><finalName>spring-boot-demo-cache-ehcache</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
SpringBootDemoCacheEhcacheApplication.java
@SpringBootApplication
@EnableCaching
public class SpringBootDemoCacheEhcacheApplication {public static void main(String[] args) {SpringApplication.run(SpringBootDemoCacheEhcacheApplication.class, args);}
}
application.yml
spring:cache:type: ehcacheehcache:config: classpath:ehcache.xml
logging:level:com.xkcoding: debug
ehcache.xml
<!-- ehcache配置 -->
<ehcachexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="false"><!--缓存路径,用户目录下的base_ehcache目录--><diskStore path="user.home/base_ehcache"/><defaultCachemaxElementsInMemory="20000"eternal="false"timeToIdleSeconds="120"timeToLiveSeconds="120"overflowToDisk="true"maxElementsOnDisk="10000000"diskPersistent="false"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"/><!--缓存文件名:user,同样的可以配置多个缓存maxElementsInMemory:内存中最多存储eternal:外部存储overflowToDisk:超出缓存到磁盘diskPersistent:磁盘持久化timeToLiveSeconds:缓存时间diskExpiryThreadIntervalSeconds:磁盘过期时间--><cache name="user"maxElementsInMemory="20000"eternal="true"overflowToDisk="true"diskPersistent="false"timeToLiveSeconds="0"diskExpiryThreadIntervalSeconds="120"/>
</ehcache>
UserServiceImpl.java
@Service
@Slf4j
public class UserServiceImpl implements UserService {/* 模拟数据库*/private static final Map<Long, User> DATABASES = Maps.newConcurrentMap();/* 初始化数据*/static {DATABASES.put(1L, new User(1L, "user1"));DATABASES.put(2L, new User(2L, "user2"));DATABASES.put(3L, new User(3L, "user3"));}/* 保存或修改用户 @param user 用户对象* @return 操作结果*/@CachePut(value = "user", key = "#user.id")@Overridepublic User saveOrUpdate(User user) {DATABASES.put(user.getId(), user);log.info("保存用户【user】= {}", user);return user;}/* 获取用户 @param id key值* @return 返回结果*/@Cacheable(value = "user", key = "#id")@Overridepublic User get(Long id) {// 我们假设从数据库读取log.info("查询用户【id】= {}", id);return DATABASES.get(id);}/* 删除 @param id key值*/@CacheEvict(value = "user", key = "#id")@Overridepublic void delete(Long id) {DATABASES.remove(id);log.info("删除用户【id】= {}", id);}
}
UserServiceTest.java
@Slf4j
public class UserServiceTest extends SpringBootDemoCacheEhcacheApplicationTests {@Autowiredprivate UserService userService;/* 获取两次,查看日志验证缓存*/@Testpublic void getTwice() {// 模拟查询id为1的用户User user1 = userService.get(1L);log.debug("【user1】= {}", user1);// 再次查询User user2 = userService.get(1L);log.debug("【user2】= {}", user2);// 查看日志,只打印一次日志,证明缓存生效}/* 先存,再查询,查看日志验证缓存*/@Testpublic void getAfterSave() {userService.saveOrUpdate(new User(4L, "user4"));User user = userService.get(4L);log.debug("【user】= {}", user);// 查看日志,只打印保存用户的日志,查询是未触发查询日志,因此缓存生效}/* 测试删除,查看redis是否存在缓存数据*/@Testpublic void deleteUser() {// 查询一次,使ehcache中存在缓存数据userService.get(1L);// 删除,查看ehcache是否存在缓存数据userService.delete(1L);}
}
参考
- Ehcache 官网:http://www.ehcache.org/documentation/
- Spring Boot 官方文档:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-caching-provider-ehcache2
- 博客:https://juejin.im/post/5b308de9518825748b56ae1d