mirror of
https://github.com/dromara/tianai-captcha.git
synced 2026-05-06 21:53:10 +08:00
修复资源泄露问题并优化资源管理
- 添加 AutoCloseable 接口实现,支持自动资源释放 - 修复 Graphics2D、ImageOutputStream 等资源未正确关闭的问题 - 为 Spring Bean 添加 destroyMethod 配置 - 添加定时任务线程池的正确关闭逻辑 - FontCache 添加缓存大小限制防止内存泄露 - 优化 StandardWordClickImageCaptchaGenerator 代码结构
This commit is contained in:
+20
-4
@@ -2,11 +2,15 @@ package cloud.tianai.captcha.spring.autoconfiguration;
|
||||
|
||||
import cloud.tianai.captcha.cache.CacheStore;
|
||||
import cloud.tianai.captcha.cache.impl.LocalCacheStore;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
|
||||
import cloud.tianai.captcha.spring.plugins.RedisResourceStore;
|
||||
import cloud.tianai.captcha.spring.store.impl.RedisCacheStore;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -17,8 +21,6 @@ import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
*
|
||||
* @author Hccake
|
||||
*/
|
||||
@AutoConfigureAfter(name = {"org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration",
|
||||
"org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration"})
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class CacheStoreAutoConfiguration {
|
||||
|
||||
@@ -31,15 +33,23 @@ public class CacheStoreAutoConfiguration {
|
||||
@Order(1)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(StringRedisTemplate.class)
|
||||
@AutoConfigureAfter({RedisAutoConfiguration.class})
|
||||
public static class RedisCacheStoreConfiguration {
|
||||
|
||||
@Bean
|
||||
@Bean(destroyMethod = "")
|
||||
@ConditionalOnBean(StringRedisTemplate.class)
|
||||
@ConditionalOnMissingBean(CacheStore.class)
|
||||
public CacheStore redis(StringRedisTemplate redisTemplate) {
|
||||
return new RedisCacheStore(redisTemplate);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(StringRedisTemplate.class)
|
||||
@ConditionalOnMissingBean(ResourceStore.class)
|
||||
public ResourceStore redisResourceStore(StringRedisTemplate redisTemplate) {
|
||||
return new RedisResourceStore(redisTemplate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,12 +62,18 @@ public class CacheStoreAutoConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public static class LocalCacheStoreConfiguration {
|
||||
|
||||
@Bean
|
||||
@Bean(destroyMethod = "close")
|
||||
@ConditionalOnMissingBean(CacheStore.class)
|
||||
public CacheStore local() {
|
||||
return new LocalCacheStore();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ResourceStore.class)
|
||||
public ResourceStore resourceStore() {
|
||||
return new LocalMemoryResourceStore();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+20
-23
@@ -4,33 +4,31 @@ package cloud.tianai.captcha.spring.autoconfiguration;
|
||||
import cloud.tianai.captcha.application.ImageCaptchaApplication;
|
||||
import cloud.tianai.captcha.application.TACBuilder;
|
||||
import cloud.tianai.captcha.cache.CacheStore;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.generator.ImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.generator.ImageTransform;
|
||||
import cloud.tianai.captcha.generator.impl.transform.Base64ImageTransform;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptor;
|
||||
import cloud.tianai.captcha.interceptor.CaptchaInterceptorGroup;
|
||||
import cloud.tianai.captcha.interceptor.impl.ParamCheckCaptchaInterceptor;
|
||||
import cloud.tianai.captcha.interceptor.EmptyCaptchaInterceptor;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.ResourceProviders;
|
||||
import cloud.tianai.captcha.resource.ResourceStore;
|
||||
import cloud.tianai.captcha.resource.impl.DefaultImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.impl.LocalMemoryResourceStore;
|
||||
import cloud.tianai.captcha.spring.common.util.URL;
|
||||
import cloud.tianai.captcha.spring.plugins.SpringMultiImageCaptchaGenerator;
|
||||
import cloud.tianai.captcha.spring.plugins.secondary.SecondaryVerificationApplication;
|
||||
import cloud.tianai.captcha.validator.ImageCaptchaValidator;
|
||||
import cloud.tianai.captcha.validator.impl.SimpleImageCaptchaValidator;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
@@ -44,17 +42,13 @@ import org.springframework.core.annotation.Order;
|
||||
@EnableConfigurationProperties({SpringImageCaptchaProperties.class})
|
||||
public class ImageCaptchaAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ResourceStore resourceStore() {
|
||||
return new LocalMemoryResourceStore();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(ResourceStore.class)
|
||||
public ImageCaptchaResourceManager imageCaptchaResourceManager(ResourceStore resourceStore) {
|
||||
ResourceProviders resourceProviders = new ResourceProviders();
|
||||
return new DefaultImageCaptchaResourceManager(resourceStore,resourceProviders);
|
||||
return new DefaultImageCaptchaResourceManager(resourceStore, resourceProviders);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -70,7 +64,9 @@ public class ImageCaptchaAutoConfiguration {
|
||||
ImageCaptchaResourceManager captchaResourceManager,
|
||||
ImageTransform imageTransform,
|
||||
BeanFactory beanFactory) {
|
||||
return new SpringMultiImageCaptchaGenerator(captchaResourceManager, imageTransform, beanFactory);
|
||||
// 构建多验证码生成器
|
||||
ImageCaptchaGenerator captchaGenerator = new SpringMultiImageCaptchaGenerator(captchaResourceManager, imageTransform, beanFactory);
|
||||
return captchaGenerator;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -80,26 +76,24 @@ public class ImageCaptchaAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@ConditionalOnMissingBean
|
||||
public CaptchaInterceptor captchaInterceptor() {
|
||||
CaptchaInterceptorGroup group = new CaptchaInterceptorGroup();
|
||||
group.addInterceptor(new ParamCheckCaptchaInterceptor());
|
||||
// group.addInterceptor(new BasicTrackCaptchaInterceptor());
|
||||
return group;
|
||||
return new EmptyCaptchaInterceptor();
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
@Bean(destroyMethod = "close")
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(CacheStore.class)
|
||||
public ImageCaptchaApplication imageCaptchaApplication(ImageCaptchaGenerator captchaGenerator,
|
||||
ImageCaptchaValidator imageCaptchaValidator,
|
||||
CacheStore cacheStore,
|
||||
ResourceStore resourceStore,
|
||||
SpringImageCaptchaProperties prop,
|
||||
CaptchaInterceptor captchaInterceptor,
|
||||
ApplicationContext applicationContext) {
|
||||
TACBuilder tacBuilder = TACBuilder.builder(resourceStore)
|
||||
ApplicationContext applicationContext
|
||||
) {
|
||||
TACBuilder tacBuilder = TACBuilder.builder()
|
||||
.setResourceStore(resourceStore)
|
||||
.setGenerator(captchaGenerator)
|
||||
.setValidator(imageCaptchaValidator)
|
||||
.setCacheStore(cacheStore)
|
||||
@@ -116,7 +110,10 @@ public class ImageCaptchaAutoConfiguration {
|
||||
String[] split = index > 0 ? new String[]{fontPath.substring(0, index), fontPath.substring(index + 1)} : new String[]{"", fontPath};
|
||||
String type = split[0];
|
||||
String path = split[1];
|
||||
tacBuilder.addFont(new cloud.tianai.captcha.resource.common.model.dto.Resource(type, path));
|
||||
|
||||
URL fontUrl = URL.valueOf(fontPath);
|
||||
String tag = fontUrl.getParam(URL.PARAM_TAG_KEY, null);
|
||||
tacBuilder.addFont(new cloud.tianai.captcha.resource.common.model.dto.Resource(type, path, tag));
|
||||
}
|
||||
}
|
||||
ImageCaptchaApplication target = tacBuilder.build();
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
package cloud.tianai.captcha.spring.common.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class URL {
|
||||
|
||||
public static final String PARAM_TAG_KEY = "tag";
|
||||
|
||||
private String protocol;
|
||||
private String path;
|
||||
private Map<String, String> params;
|
||||
|
||||
public String getParam(String key, String defaultValue) {
|
||||
return params.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
public static URL valueOf(String input) {
|
||||
// 分割协议和剩余部分
|
||||
String[] parts = input.split(":", 2);
|
||||
String protocol = parts[0];
|
||||
String remaining = parts[1];
|
||||
|
||||
// 分割路径和查询参数
|
||||
String path;
|
||||
String query = null;
|
||||
|
||||
if (remaining.contains("?")) {
|
||||
String[] pathQuerySplit = remaining.split("\\?", 2);
|
||||
path = pathQuerySplit[0];
|
||||
query = pathQuerySplit[1];
|
||||
} else {
|
||||
path = remaining;
|
||||
}
|
||||
if (path.startsWith("//")) {
|
||||
path = path.substring(2);
|
||||
}
|
||||
// 处理查询参数,提取键值对
|
||||
Map<String, String> queryParams = new HashMap<>();
|
||||
if (query != null) {
|
||||
for (String param : query.split("&")) {
|
||||
String[] keyValue = param.split("=", 2);
|
||||
String key = keyValue[0];
|
||||
String value = keyValue.length > 1 ? keyValue[1] : "";
|
||||
queryParams.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return new URL(protocol, path, queryParams);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package cloud.tianai.captcha.spring.exception;
|
||||
|
||||
import cloud.tianai.captcha.common.exception.ImageCaptchaException;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @Date 2020/6/19 16:36
|
||||
* @Description 验证码验证失败异常
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class CaptchaValidException extends ImageCaptchaException {
|
||||
|
||||
private String captchaType;
|
||||
private Integer code;
|
||||
public CaptchaValidException() {
|
||||
}
|
||||
|
||||
public CaptchaValidException(String captchaType,String message) {
|
||||
super(message);
|
||||
this.captchaType = captchaType;
|
||||
}
|
||||
public CaptchaValidException(String captchaType,Integer code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.captchaType = captchaType;
|
||||
}
|
||||
public CaptchaValidException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CaptchaValidException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CaptchaValidException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
package cloud.tianai.captcha.spring.plugins;
|
||||
|
||||
import cloud.tianai.captcha.common.constant.CommonConstant;
|
||||
import cloud.tianai.captcha.common.util.CollectionUtils;
|
||||
import cloud.tianai.captcha.resource.CrudResourceStore;
|
||||
import cloud.tianai.captcha.resource.ImageCaptchaResourceManager;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.Resource;
|
||||
import cloud.tianai.captcha.resource.common.model.dto.ResourceMap;
|
||||
import com.google.gson.Gson;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* @Author: 天爱有情
|
||||
* @date 2023/8/23 10:52
|
||||
* @Description 基于redis的store
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class RedisResourceStore implements CrudResourceStore {
|
||||
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String resourcePrefix = "captcha:config:resource:";
|
||||
@Getter
|
||||
@Setter
|
||||
private String templatePrefix = "captcha:config:template:";
|
||||
private Gson gson = new Gson();
|
||||
|
||||
public String joinResourceKey(String type, String tag) {
|
||||
if (tag == null) {
|
||||
tag = CommonConstant.DEFAULT_TAG;
|
||||
}
|
||||
type = type.toUpperCase();
|
||||
return resourcePrefix + tag + ":" + type;
|
||||
}
|
||||
|
||||
public String joinTemplateKey(String type, String tag) {
|
||||
if (tag == null) {
|
||||
tag = CommonConstant.DEFAULT_TAG;
|
||||
}
|
||||
type = type.toUpperCase();
|
||||
return templatePrefix + tag + ":" + type;
|
||||
}
|
||||
|
||||
public List<Resource> getResources(String type, String tag) {
|
||||
String key = joinResourceKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size < 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size);
|
||||
List<Resource> result = new ArrayList<>(range.size());
|
||||
for (String json : range) {
|
||||
result.add(gson.fromJson(json, Resource.class));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<ResourceMap> getTemplates(String type, String tag) {
|
||||
String key = joinTemplateKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size < 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size);
|
||||
List<ResourceMap> result = new ArrayList<>(range.size());
|
||||
for (String json : range) {
|
||||
result.add(gson.fromJson(json, ResourceMap.class));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public void setResources(String type, String tag, List<Resource> resources) {
|
||||
String key = joinResourceKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size != null && size > 0) {
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
for (Resource resource : resources) {
|
||||
addResource(type, resource);
|
||||
}
|
||||
}
|
||||
|
||||
public void setTemplates(String type, String tag, List<ResourceMap> templates) {
|
||||
String key = joinTemplateKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size != null && size > 0) {
|
||||
redisTemplate.delete(key);
|
||||
}
|
||||
for (ResourceMap template : templates) {
|
||||
addTemplate(type, template);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addResource(String type, Resource resource) {
|
||||
// 添加tag标签字典
|
||||
redisTemplate.opsForList().rightPush(joinResourceKey(type, resource.getTag()), gson.toJson(resource));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTemplate(String type, ResourceMap template) {
|
||||
// 添加tag标签字典
|
||||
redisTemplate.opsForList().rightPush(joinTemplateKey(type, template.getTag()), gson.toJson(template));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource deleteResource(String type, String id) {
|
||||
Set<String> keys = redisTemplate.keys(joinResourceKey(type, "*"));
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
for (String key : keys) {
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size < 1) {
|
||||
continue;
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size);
|
||||
if (range != null) {
|
||||
for (String json : range) {
|
||||
Resource resource = gson.fromJson(json, Resource.class);
|
||||
if (resource.getId().equals(id)) {
|
||||
redisTemplate.opsForList().remove(key, 1, json);
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceMap deleteTemplate(String type, String id) {
|
||||
Set<String> keys = redisTemplate.keys(joinTemplateKey(type, "*"));
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
for (String key : keys) {
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size < 1) {
|
||||
continue;
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size);
|
||||
if (range != null) {
|
||||
for (String json : range) {
|
||||
ResourceMap resourceMap = gson.fromJson(json, ResourceMap.class);
|
||||
if (resourceMap.getId().equals(id)) {
|
||||
redisTemplate.opsForList().remove(key, 1, json);
|
||||
return resourceMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> listResourcesByTypeAndTag(String type, String tag) {
|
||||
if (StringUtils.isNotBlank(tag)) {
|
||||
return getResources(type, tag);
|
||||
}
|
||||
Set<String> keys = redisTemplate.keys(joinResourceKey(type, "*"));
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
Long size1 = redisTemplate.opsForList().size(key);
|
||||
if (size1 == null || size1 < 1) {
|
||||
continue;
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size1);
|
||||
if (range != null) {
|
||||
for (String json : range) {
|
||||
Resource resource = gson.fromJson(json, Resource.class);
|
||||
resources.add(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
return resources;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> listTemplatesByTypeAndTag(String type, String tag) {
|
||||
if (StringUtils.isNotBlank(tag)) {
|
||||
return getTemplates(type, tag);
|
||||
}
|
||||
Set<String> keys = redisTemplate.keys(joinTemplateKey(type, "*"));
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
List<ResourceMap> templates = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
Long size1 = redisTemplate.opsForList().size(key);
|
||||
if (size1 == null || size1 < 1) {
|
||||
continue;
|
||||
}
|
||||
List<String> range = redisTemplate.opsForList().range(key, 0, size1);
|
||||
if (range != null) {
|
||||
for (String json : range) {
|
||||
ResourceMap template = gson.fromJson(json, ResourceMap.class);
|
||||
templates.add(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
return templates;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ImageCaptchaResourceManager resourceManager) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Resource> randomGetResourceByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
String key = joinResourceKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || quantity > size) {
|
||||
throw new IllegalArgumentException("请求的资源数量超过可用资源总数");
|
||||
}
|
||||
|
||||
Set<Long> indexes = new HashSet<>(quantity);
|
||||
while (indexes.size() < quantity) {
|
||||
indexes.add(ThreadLocalRandom.current().nextLong(size));
|
||||
}
|
||||
List<Resource> result = new ArrayList<>(quantity);
|
||||
for (Long index : indexes) {
|
||||
String resourceJson = redisTemplate.opsForList().index(key, index);
|
||||
result.add(gson.fromJson(resourceJson, Resource.class));
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceMap> randomGetTemplateByTypeAndTag(String type, String tag, Integer quantity) {
|
||||
String key = joinTemplateKey(type, tag);
|
||||
Long size = redisTemplate.opsForList().size(key);
|
||||
if (size == null || size < 1) {
|
||||
throw new IllegalStateException("随机获取模板错误,store中模板为空, type:" + type);
|
||||
}
|
||||
if (quantity > size) {
|
||||
throw new IllegalArgumentException("请求的模板数量超过可用模板总数");
|
||||
}
|
||||
|
||||
Set<Long> indexes = new HashSet<>(quantity);
|
||||
while (indexes.size() < quantity) {
|
||||
indexes.add(ThreadLocalRandom.current().nextLong(size));
|
||||
}
|
||||
|
||||
List<ResourceMap> result = new ArrayList<>(quantity);
|
||||
|
||||
for (Long index : indexes) {
|
||||
String resourceJson = redisTemplate.opsForList().index(key, index);
|
||||
result.add(gson.fromJson(resourceJson, ResourceMap.class));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllTemplates() {
|
||||
Set<String> keys = redisTemplate.keys(templatePrefix + "*");
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllResources() {
|
||||
Set<String> keys = redisTemplate.keys(resourcePrefix + "*");
|
||||
if (!CollectionUtils.isEmpty(keys)) {
|
||||
redisTemplate.delete(keys);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -68,4 +68,9 @@ public class RedisCacheStore implements CacheStore {
|
||||
}
|
||||
return Long.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user