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

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: [基于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}
目录
相关文章
|
16天前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
5月前
|
NoSQL 安全 Java
深入理解 RedisConnectionFactory:Spring Data Redis 的核心组件
在 Spring Data Redis 中,`RedisConnectionFactory` 是核心组件,负责创建和管理与 Redis 的连接。它支持单机、集群及哨兵等多种模式,为上层组件(如 `RedisTemplate`)提供连接抽象。Spring 提供了 Lettuce 和 Jedis 两种主要实现,其中 Lettuce 因其线程安全和高性能特性被广泛推荐。通过手动配置或 Spring Boot 自动化配置,开发者可轻松集成 Redis,提升应用性能与扩展性。本文深入解析其作用、实现方式及常见问题解决方法,助你高效使用 Redis。
562 4
|
6月前
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
271 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
2月前
|
NoSQL Java Redis
Redis基本数据类型及Spring Data Redis应用
Redis 是开源高性能键值对数据库,支持 String、Hash、List、Set、Sorted Set 等数据结构,适用于缓存、消息队列、排行榜等场景。具备高性能、原子操作及丰富功能,是分布式系统核心组件。
368 2
|
2月前
|
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等常用模块,简化了开发者对依赖的手动管理。
370 3
|
6月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
530 0
|
3月前
|
Java 数据库连接 数据库
Spring boot 使用mybatis generator 自动生成代码插件
本文介绍了在Spring Boot项目中使用MyBatis Generator插件自动生成代码的详细步骤。首先创建一个新的Spring Boot项目,接着引入MyBatis Generator插件并配置`pom.xml`文件。然后删除默认的`application.properties`文件,创建`application.yml`进行相关配置,如设置Mapper路径和实体类包名。重点在于配置`generatorConfig.xml`文件,包括数据库驱动、连接信息、生成模型、映射文件及DAO的包名和位置。最后通过IDE配置运行插件生成代码,并在主类添加`@MapperScan`注解完成整合
598 1
Spring boot 使用mybatis generator 自动生成代码插件
|
4月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
182 32
|
7月前
|
人工智能 Java API
支持 40+ 插件,Spring AI Alibaba 简化智能体私有数据集成
通过使用社区官方提供的超过 20 种 RAG 数据源和 20 种 Tool Calling 接口,开发者可以轻松接入多种外部数据源(如 GitHub、飞书、云 OSS 等)以及调用各种工具(如天气预报、地图导航、翻译服务等)。这些默认实现大大简化了智能体的开发过程,使得开发者无需从零开始,便可以快速构建功能强大的智能体系统。通过这种方式,智能体不仅能够高效处理复杂任务,还能适应各种应用场景,提供更加智能、精准的服务。
1112 101
|
4月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。