基于Redis的Spring Boot 幂等性插件模块封装

简介: [基于Redis支持集群、哨兵等模式]

幂等性注解定义

importjava.lang.annotation.*;
importjava.util.concurrent.TimeUnit;
/*** @author jeckxu*/@Inherited@Target(ElementType.METHOD)
@Retention(value=RetentionPolicy.RUNTIME)
public@interfaceIdempotent {
/*** 幂等操作的唯一标识,使用spring el表达式 用#来引用方法参数* @return Spring-EL expression*/Stringkey() default"";
/*** 有效期 默认:1 有效期要大于程序执行时间,否则请求还是可能会进来* @return expireTime*/intexpireTime() default1;
/*** 时间单位 默认:s* @return TimeUnit*/TimeUnittimeUnit() defaultTimeUnit.SECONDS;
/*** 提示信息,可自定义* @return String*/Stringinfo() default"ROOT.IDEMPOTENT.DO_NOT_REPEAT_THE_OPERATION";
/*** 是否在业务完成后删除key true:删除 false:不删除* @return boolean*/booleandelKey() defaulttrue;
}

幂等性配置类

importorg.springframework.boot.context.properties.ConfigurationProperties;
/*** @author Jeckxu*/@ConfigurationProperties(MagicalIdempotentProperties.PREFIX)
publicclassMagicalIdempotentProperties {
publicstaticfinalStringPREFIX="magical.idempotent";
/*** 是否开启:默认为:false,便于生成配置提示。*/privateBooleanenabled=Boolean.FALSE;
/*** 单机配置:redis 服务地址*/privateStringaddress="redis://127.0.0.1:6379";
/*** 密码配置*/privateStringpassword;
/*** db*/privateIntegerdatabase=0;
/*** 连接池大小*/privateIntegerpoolSize=20;
/*** 最小空闲连接数*/privateIntegeridleSize=5;
/*** 连接空闲超时,单位:毫秒*/privateIntegeridleTimeout=60000;
/*** 连接超时,单位:毫秒*/privateIntegerconnectionTimeout=3000;
/*** 命令等待超时,单位:毫秒*/privateIntegertimeout=10000;
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/privateModemode=Mode.single;
/*** 主从模式,主地址*/privateStringmasterAddress;
/*** 主从模式,从地址*/privateString[] slaveAddress;
/*** 哨兵模式:主名称*/privateStringmasterName;
/*** 哨兵模式地址*/privateString[] sentinelAddress;
/*** 集群模式节点地址*/privateString[] nodeAddress;
publicenumMode {
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/single, master, sentinel, cluster;
    }
/*** 是否开启:默认为:false,便于生成配置提示。*/publicBooleangetEnabled() {
returnthis.enabled;
    }
/*** 单机配置:redis 服务地址*/publicStringgetAddress() {
returnthis.address;
    }
/*** 密码配置*/publicStringgetPassword() {
returnthis.password;
    }
/*** db*/publicIntegergetDatabase() {
returnthis.database;
    }
/*** 连接池大小*/publicIntegergetPoolSize() {
returnthis.poolSize;
    }
/*** 最小空闲连接数*/publicIntegergetIdleSize() {
returnthis.idleSize;
    }
/*** 连接空闲超时,单位:毫秒*/publicIntegergetIdleTimeout() {
returnthis.idleTimeout;
    }
/*** 连接超时,单位:毫秒*/publicIntegergetConnectionTimeout() {
returnthis.connectionTimeout;
    }
/*** 命令等待超时,单位:毫秒*/publicIntegergetTimeout() {
returnthis.timeout;
    }
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/publicModegetMode() {
returnthis.mode;
    }
/*** 主从模式,主地址*/publicStringgetMasterAddress() {
returnthis.masterAddress;
    }
/*** 主从模式,从地址*/publicString[] getSlaveAddress() {
returnthis.slaveAddress;
    }
/*** 哨兵模式:主名称*/publicStringgetMasterName() {
returnthis.masterName;
    }
/*** 哨兵模式地址*/publicString[] getSentinelAddress() {
returnthis.sentinelAddress;
    }
/*** 集群模式节点地址*/publicString[] getNodeAddress() {
returnthis.nodeAddress;
    }
/*** 是否开启:默认为:false,便于生成配置提示。*/publicvoidsetEnabled(finalBooleanenabled) {
this.enabled=enabled;
    }
/*** 单机配置:redis 服务地址*/publicvoidsetAddress(finalStringaddress) {
this.address=address;
    }
/*** 密码配置*/publicvoidsetPassword(finalStringpassword) {
this.password=password;
    }
/*** db*/publicvoidsetDatabase(finalIntegerdatabase) {
this.database=database;
    }
/*** 连接池大小*/publicvoidsetPoolSize(finalIntegerpoolSize) {
this.poolSize=poolSize;
    }
/*** 最小空闲连接数*/publicvoidsetIdleSize(finalIntegeridleSize) {
this.idleSize=idleSize;
    }
/*** 连接空闲超时,单位:毫秒*/publicvoidsetIdleTimeout(finalIntegeridleTimeout) {
this.idleTimeout=idleTimeout;
    }
/*** 连接超时,单位:毫秒*/publicvoidsetConnectionTimeout(finalIntegerconnectionTimeout) {
this.connectionTimeout=connectionTimeout;
    }
/*** 命令等待超时,单位:毫秒*/publicvoidsetTimeout(finalIntegertimeout) {
this.timeout=timeout;
    }
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/publicvoidsetMode(finalModemode) {
this.mode=mode;
    }
/*** 主从模式,主地址*/publicvoidsetMasterAddress(finalStringmasterAddress) {
this.masterAddress=masterAddress;
    }
/*** 主从模式,从地址*/publicvoidsetSlaveAddress(finalString[] slaveAddress) {
this.slaveAddress=slaveAddress;
    }
/*** 哨兵模式:主名称*/publicvoidsetMasterName(finalStringmasterName) {
this.masterName=masterName;
    }
/*** 哨兵模式地址*/publicvoidsetSentinelAddress(finalString[] sentinelAddress) {
this.sentinelAddress=sentinelAddress;
    }
/*** 集群模式节点地址*/publicvoidsetNodeAddress(finalString[] nodeAddress) {
this.nodeAddress=nodeAddress;
    }
}

幂等性切面类

importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.annotation.After;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
importorg.jeckxu.magical.core.idempotent.exception.IdempotentException;
importorg.jeckxu.magical.core.idempotent.expression.KeyResolver;
importorg.redisson.api.RMapCache;
importorg.redisson.api.RedissonClient;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.util.CollectionUtils;
importorg.springframework.util.StringUtils;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;
importjavax.servlet.http.HttpServletRequest;
importjava.lang.reflect.Method;
importjava.time.LocalDateTime;
importjava.util.Arrays;
importjava.util.HashMap;
importjava.util.Map;
importjava.util.concurrent.TimeUnit;
/*** @author jeckxu*/@AspectpublicclassIdempotentAspect {
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(IdempotentAspect.class);
privateThreadLocal<Map<String, Object>>threadLocal=newThreadLocal();
privatestaticfinalStringRMAPCACHE_KEY="magical:idempotent";
privatestaticfinalStringKEY="key";
privatestaticfinalStringDELKEY="delKey";
privatefinalRedissonClientredissonClient;
privatefinalKeyResolverkeyResolver;
@Pointcut("@annotation(org.jeckxu.magical.core.idempotent.annotation.Idempotent)")
publicvoidpointCut() {
    }
@Before("pointCut()")
publicvoidbeforePointCut(JoinPointjoinPoint) throwsException {
ServletRequestAttributesrequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequestrequest=requestAttributes.getRequest();
MethodSignaturesignature= (MethodSignature) joinPoint.getSignature();
Methodmethod=signature.getMethod();
if (!method.isAnnotationPresent(Idempotent.class)) {
return;
        }
Idempotentidempotent=method.getAnnotation(Idempotent.class);
Stringkey;
// 若没有配置 幂等 标识编号,则使用 url + 参数列表作为区分if (StringUtils.isEmpty(idempotent.key())) {
Stringurl=request.getRequestURL().toString();
StringargString=Arrays.asList(joinPoint.getArgs()).toString();
key=url+argString;
        } else {
// 使用jstl 规则区分key=keyResolver.resolver(idempotent, joinPoint);
        }
longexpireTime=idempotent.expireTime();
Stringinfo=idempotent.info();
TimeUnittimeUnit=idempotent.timeUnit();
booleandelKey=idempotent.delKey();
// do not need check nullRMapCache<String, Object>rMapCache=redissonClient.getMapCache(RMAPCACHE_KEY);
Stringvalue=LocalDateTime.now().toString().replace("T", " ");
Objectv1;
if (null!=rMapCache.get(key)) {
// had storedthrownewIdempotentException(info);
        }
synchronized (this) {
v1=rMapCache.putIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
if (null!=v1) {
thrownewIdempotentException(info);
            } else {
LOGGER.info("[idempotent]:has stored key={},value={},expireTime={}{},now={}", key, value, expireTime, timeUnit, LocalDateTime.now().toString());
            }
        }
Map<String, Object>map=CollectionUtils.isEmpty(threadLocal.get()) ?newHashMap<>(4) : threadLocal.get();
map.put(KEY, key);
map.put(DELKEY, delKey);
threadLocal.set(map);
    }
@After("pointCut()")
publicvoidafterPointCut(JoinPointjoinPoint) {
Map<String, Object>map=threadLocal.get();
if (CollectionUtils.isEmpty(map)) {
return;
        }
RMapCache<Object, Object>mapCache=redissonClient.getMapCache(RMAPCACHE_KEY);
if (mapCache.size() ==0) {
return;
        }
Stringkey=map.get(KEY).toString();
booleandelKey= (boolean) map.get(DELKEY);
if (delKey) {
mapCache.fastRemove(key);
LOGGER.info("[idempotent]:has removed key={}", key);
        }
threadLocal.remove();
    }
publicIdempotentAspect(finalRedissonClientredissonClient, finalKeyResolverkeyResolver) {
this.redissonClient=redissonClient;
this.keyResolver=keyResolver;
    }
}

幂等性异常定义

/*** Idempotent Exception If there is a custom global exception, you need to inherit the* custom global exception.** @author jeckxu*/publicclassIdempotentExceptionextendsException {
publicIdempotentException() {
super();
   }
publicIdempotentException(Stringmessage) {
super(message);
   }
publicIdempotentException(Stringmessage, Throwablecause) {
super(message, cause);
   }
publicIdempotentException(Throwablecause) {
super(cause);
   }
protectedIdempotentException(Stringmessage, Throwablecause, booleanenableSuppression,
booleanwritableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
   }
}


幂等性唯一标志处理器

importorg.aspectj.lang.JoinPoint;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
/*** @author jeckxu* @date 2020/11/10* <p>* 唯一标志处理器*/publicinterfaceKeyResolver {
/*** 解析处理 key* @param idempotent 接口注解标识* @param point 接口切点信息* @return 处理结果*/Stringresolver(Idempotentidempotent, JoinPointpoint);
}
importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
importorg.springframework.core.LocalVariableTableParameterNameDiscoverer;
importorg.springframework.expression.Expression;
importorg.springframework.expression.spel.standard.SpelExpressionParser;
importorg.springframework.expression.spel.support.StandardEvaluationContext;
importjava.lang.reflect.Method;
/*** @author jeckxu*/publicclassExpressionResolverimplementsKeyResolver {
privatestaticfinalSpelExpressionParserPARSER=newSpelExpressionParser();
privatestaticfinalLocalVariableTableParameterNameDiscovererDISCOVERER=newLocalVariableTableParameterNameDiscoverer();
@OverridepublicStringresolver(Idempotentidempotent, JoinPointpoint) {
Object[] arguments=point.getArgs();
String[] params=DISCOVERER.getParameterNames(getMethod(point));
StandardEvaluationContextcontext=newStandardEvaluationContext();
for (intlen=0; len<params.length; len++) {
context.setVariable(params[len], arguments[len]);
      }
Expressionexpression=PARSER.parseExpression(idempotent.key());
returnexpression.getValue(context, String.class);
   }
/*** 根据切点解析方法信息* @param joinPoint 切点信息* @return Method 原信息*/privateMethodgetMethod(JoinPointjoinPoint) {
MethodSignaturesignature= (MethodSignature) joinPoint.getSignature();
Methodmethod=signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method=joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(),
method.getParameterTypes());
         }
catch (SecurityException|NoSuchMethodExceptione) {
thrownewRuntimeException(e);
         }
      }
returnmethod;
   }
}

幂等性异常统一处理

importorg.jeckxu.magical.core.idempotent.exception.IdempotentException;
importorg.jeckxu.magical.core.launch.destroy.MagicalDestroying;
importorg.jeckxu.magical.core.launch.utils.I18nMessageUtils;
importorg.jeckxu.magical.core.tool.api.R;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.core.annotation.Order;
importorg.springframework.http.HttpStatus;
importorg.springframework.web.bind.annotation.ExceptionHandler;
importorg.springframework.web.bind.annotation.ResponseStatus;
importorg.springframework.web.bind.annotation.RestControllerAdvice;
importorg.springframework.web.servlet.DispatcherServlet;
importjavax.servlet.Servlet;
@Order(3)
@Configuration(proxyBeanMethods=false)
@ConditionalOnWebApplication(type=ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@RestControllerAdvicepublicclassMagicalIdempotentExceptionTranslatorimplementsMagicalDestroying {
privatestaticfinalorg.slf4j.Loggerlog=org.slf4j.LoggerFactory.getLogger(MagicalIdempotentExceptionTranslator.class);
@ExceptionHandler(IdempotentException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
publicR<Object>handleError(IdempotentExceptione) {
e.printStackTrace();
returnR.fail(1999, I18nMessageUtils.get(e.getMessage()));
    }
}

幂等插件自动配置【初始化】

importorg.jeckxu.magical.core.idempotent.aspect.IdempotentAspect;
importorg.jeckxu.magical.core.idempotent.expression.ExpressionResolver;
importorg.jeckxu.magical.core.idempotent.expression.KeyResolver;
importorg.jeckxu.magical.core.idempotent.prop.MagicalIdempotentProperties;
importorg.jeckxu.magical.core.launch.destroy.MagicalDestroying;
importorg.jeckxu.magical.core.tool.utils.StringUtil;
importorg.redisson.Redisson;
importorg.redisson.api.RedissonClient;
importorg.redisson.config.*;
importorg.springframework.boot.autoconfigure.AutoConfigureAfter;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
importorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
importorg.springframework.boot.context.properties.EnableConfigurationProperties;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
/*** 幂等插件初始化* @author jeckxu*/@Configuration(proxyBeanMethods=false)
@ConditionalOnClass(RedissonClient.class)
@EnableConfigurationProperties(MagicalIdempotentProperties.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnProperty(value="magical.idempotent.enabled", havingValue="true")
publicclassIdempotentAutoConfigurationimplementsMagicalDestroying {
privatestaticConfigsingleConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
SingleServerConfigserversConfig=config.useSingleServer();
serversConfig.setAddress(properties.getAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setConnectionPoolSize(properties.getPoolSize());
serversConfig.setConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigmasterSlaveConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
MasterSlaveServersConfigserversConfig=config.useMasterSlaveServers();
serversConfig.setMasterAddress(properties.getMasterAddress());
serversConfig.addSlaveAddress(properties.getSlaveAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigsentinelConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
SentinelServersConfigserversConfig=config.useSentinelServers();
serversConfig.setMasterName(properties.getMasterName());
serversConfig.addSentinelAddress(properties.getSentinelAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigclusterConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
ClusterServersConfigserversConfig=config.useClusterServers();
serversConfig.addNodeAddress(properties.getNodeAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
/*** 切面 拦截处理所有 @Idempotent* @return Aspect*/@BeanpublicIdempotentAspectidempotentAspect(MagicalIdempotentPropertiesproperties) {
returnnewIdempotentAspect(redissonClient(properties), newExpressionResolver());
   }
/*** key 解析器* @return KeyResolver*/@Bean@ConditionalOnMissingBean(KeyResolver.class)
publicKeyResolverkeyResolver() {
returnnewExpressionResolver();
   }
privatestaticRedissonClientredissonClient(MagicalIdempotentPropertiesproperties) {
MagicalIdempotentProperties.Modemode=properties.getMode();
Configconfig;
switch (mode) {
casesentinel:
config=sentinelConfig(properties);
break;
casecluster:
config=clusterConfig(properties);
break;
casemaster:
config=masterSlaveConfig(properties);
break;
casesingle:
config=singleConfig(properties);
break;
default:
config=newConfig();
break;
      }
returnRedisson.create(config);
   }
}

yml配置项

magical:  idempotent:    enabled: false    address: redis://192.168.0.1:26379    password: ${YOUR_PASSWORD}
目录
相关文章
|
NoSQL 安全 Java
深入理解 RedisConnectionFactory:Spring Data Redis 的核心组件
在 Spring Data Redis 中,`RedisConnectionFactory` 是核心组件,负责创建和管理与 Redis 的连接。它支持单机、集群及哨兵等多种模式,为上层组件(如 `RedisTemplate`)提供连接抽象。Spring 提供了 Lettuce 和 Jedis 两种主要实现,其中 Lettuce 因其线程安全和高性能特性被广泛推荐。通过手动配置或 Spring Boot 自动化配置,开发者可轻松集成 Redis,提升应用性能与扩展性。本文深入解析其作用、实现方式及常见问题解决方法,助你高效使用 Redis。
1391 4
|
8月前
|
NoSQL Java 网络安全
SpringBoot启动时连接Redis报错:ERR This instance has cluster support disabled - 如何解决?
通过以上步骤一般可以解决由于配置不匹配造成的连接错误。在调试问题时,一定要确保服务端和客户端的Redis配置保持同步一致。这能够确保SpringBoot应用顺利连接到正确配置的Redis服务,无论是单机模式还是集群模式。
702 5
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
489 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
9月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
821 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
11月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
869 3
|
11月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
811 2
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
388 32
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
855 0
|
NoSQL Java API
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Spring Boot 集成 Redis
本文介绍了在Spring Boot中集成Redis的方法,包括依赖导入、Redis配置及常用API的使用。通过导入`spring-boot-starter-data-redis`依赖和配置`application.yml`文件,可轻松实现Redis集成。文中详细讲解了StringRedisTemplate的使用,适用于字符串操作,并结合FastJSON将实体类转换为JSON存储。还展示了Redis的string、hash和list类型的操作示例。最后总结了Redis在缓存和高并发场景中的应用价值,并提供课程源代码下载链接。
2785 0
|
NoSQL Java Redis
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 安装
本教程介绍在 VMware 虚拟机(CentOS 7)或阿里云服务器中安装 Redis 的过程,包括安装 gcc 编译环境、下载 Redis(官网或 wget)、解压安装、修改配置文件(如 bind、daemonize、requirepass 等设置)、启动 Redis 服务及测试客户端连接。通过 set 和 get 命令验证安装是否成功。适用于初学者快速上手 Redis 部署。
447 0