基于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}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
4月前
|
编解码 NoSQL Java
使用Spring Boot + Redis 队列实现视频文件上传及FFmpeg转码的技术分享
【8月更文挑战第30天】在当前的互联网应用中,视频内容的处理与分发已成为不可或缺的一部分。对于视频平台而言,高效、稳定地处理用户上传的视频文件,并对其进行转码以适应不同设备的播放需求,是提升用户体验的关键。本文将围绕使用Spring Boot结合Redis队列技术来实现视频文件上传及FFmpeg转码的过程,分享一系列技术干货。
252 3
|
7天前
|
前端开发 Java 开发者
这款免费 IDEA 插件让你开发 Spring 程序更简单
Feign-Helper 是一款支持 Spring 框架的 IDEA 免费插件,提供 URL 快速搜索、Spring Web Controller 路径一键复制及 Feign 与 Controller 接口互相导航等功能,极大提升了开发效率。
|
22天前
|
存储 NoSQL Java
使用lock4j-redis-template-spring-boot-starter实现redis分布式锁
通过使用 `lock4j-redis-template-spring-boot-starter`,我们可以轻松实现 Redis 分布式锁,从而解决分布式系统中多个实例并发访问共享资源的问题。合理配置和使用分布式锁,可以有效提高系统的稳定性和数据的一致性。希望本文对你在实际项目中使用 Redis 分布式锁有所帮助。
65 5
|
19小时前
|
Java Maven Spring
【Spring工具插件】lombok使用和EditStarter插件
本文第一个板块主要介绍了SpringMVC中lombok依赖的引入,和相应的使用方法,以及浅显的原理解释,第二个板块主要介绍EditStarter插件的安装与使用
|
1月前
|
Java 数据库连接 数据库
不可不知道的Spring 框架七大模块
Spring框架是一个全面的Java企业级应用开发框架,其核心容器模块为其他模块提供基础支持,包括Beans、Core、Context和SpEL四大子模块;数据访问及集成模块支持数据库操作,涵盖JDBC、ORM、OXM、JMS和Transactions;Web模块则专注于Web应用,提供Servlet、WebSocket等功能;此外,还包括AOP、Aspects、Instrumentation、Messaging和Test等辅助模块,共同构建强大的企业级应用解决方案。
70 2
|
1月前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
44 2
|
1月前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
58 2
|
2月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
78 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
2月前
|
NoSQL Java Redis
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
这篇文章介绍了Redis的基本命令,并展示了如何使用Netty框架直接与Redis服务器进行通信,包括设置Netty客户端、编写处理程序以及初始化Channel的完整示例代码。
72 1
redis的基本命令,并用netty操作redis(不使用springboot或者spring框架)就单纯的用netty搞。
|
2月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
144 2