做银行流水网站大型购物网站建设方案
2026/3/30 23:14:08 网站建设 项目流程
做银行流水网站,大型购物网站建设方案,海南网络,青海网站建设的企业一、TokenRetryHelper 设计解析 1. 核心设计目标 TokenRetryHelper 是一个处理 Token 过期场景的工具类#xff0c;主要解决以下问题#xff1a; 当 API 调用返回 Token 过期错误时#xff0c;自动触发重新登录重登成功后#xff0c;自动重试原始请求多请求并发时遇到 Toke…一、TokenRetryHelper 设计解析1. 核心设计目标TokenRetryHelper 是一个处理 Token 过期场景的工具类主要解决以下问题当 API 调用返回 Token 过期错误时自动触发重新登录重登成功后自动重试原始请求多请求并发时遇到 Token 过期避免重复重登操作保证整个重登重试流程的线程安全2. 关键设计点详解a) 线程安全机制privatestaticfinalObjectRELOGIN_LOCKnewObject();privatestaticvolatilebooleanisReloggingfalse;privatestaticvolatilelonglastReloginSuccessTime0;privatestaticfinallongRELOGIN_COOLDOWN_MS3000;为什么这样设计多个 API 请求可能同时检测到 Token 过期需要防止并发重登使用volatile保证变量在多线程间的可见性RELOGIN_LOCK对象锁确保临界区操作的原子性冷却期机制 (3秒) 防止短时间内重复重登减轻服务器压力b) 等待队列设计privatestaticfinaljava.util.ListReloginCallbackpendingCallbacksjava.util.Collections.synchronizedList(newjava.util.ArrayList());为什么这样设计当一个线程正在重登时其他检测到 Token 过期的请求不重复触发重登将这些请求的回调加入等待队列待重登完成后批量通知使用synchronizedList保证队列操作的线程安全解决高并发场景下的资源竞争问题c) 双重 Token 过期检测privatestaticbooleanisTokenExpiredCode(intcode){returncodeCODE_TOKEN_EXPIRED||codeCODE_TOKEN_INVALID||codeCODE_TOKEN_ERROR;}privatestaticbooleanisTokenExpiredHttpCode(inthttpCode){returnhttpCode401||httpCode403;}为什么这样设计业务层错误码 (1005/4001/3002) 和 HTTP 状态码 (401/403) 都可能表示 Token 问题双重检测机制确保各种 Token 过期场景都能被正确捕获适应后端 API 设计的多样性d) 同步/异步双模式publicstaticvoidexecuteWithTokenRetry(...){/* 异步模式 */}publicstaticResponseJsonObjectexecuteWithTokenRetrySync(...)throwsException{/* 同步模式 */}为什么这样设计Android 应用中既有 UI 线程发起的异步请求也有后台任务需要的同步请求两种模式共享核心重登逻辑但处理方式不同异步模式使用回调链同步模式使用阻塞等待e) 令牌获取逻辑的健壮性StringnewTokennull;if(data!nulldata.has(token)){newTokendata.get(token).getAsString();}elseif(root.has(token)){newTokenroot.get(token).getAsString();}else{// 记录错误日志}为什么这样设计后端 API 可能将 token 放在不同的位置 (data 对象内或根级别)多层判断保证在 API 结构变化时仍能获取 token详细的日志记录便于排查 token 获取失败的问题f) 同步重登的等待机制while(isRelogging){try{RELOGIN_LOCK.wait(5000);// 最多等待5秒}catch(InterruptedExceptione){Thread.currentThread().interrupt();returnfalse;}// 检查是否在冷却期}为什么这样设计同步方法中当其他线程正在重登时当前线程需要等待使用 wait/notify 机制实现线程间协作5秒超时防止永久阻塞中断处理保证线程安全退出g) 详细的日志与监控try{ActivationLogger.getInstance(context).logCustomEvent(【TokenRetry】检测到HTTP response.code()准备重登);}catch(Exceptionignored){}为什么这样设计每个关键步骤都有日志记录便于问题追踪使用 try-catch 防止日志记录异常影响主流程自定义事件日志帮助分析 Token 过期频率和重登成功率二、Spring Boot 迁移方案1. 架构设计原则保持核心重登重试逻辑不变适应 Spring 生态的组件和设计模式利用 Spring 的 AOP 和拦截器机制使用响应式编程模型替代回调模式2. 组件设计a) Token 管理组件ComponentpublicclassTokenManager{privatestaticfinallongRELOGIN_COOLDOWN_MS3000;privatefinalAtomicBooleanisReloggingnewAtomicBoolean(false);privatefinalAtomicLonglastReloginSuccessTimenewAtomicLong(0);privatefinalMapString,TokenInfotokenCachenewConcurrentHashMap();privatefinalReentrantLockreloginLocknewReentrantLock();AutowiredprivateRestTemplaterestTemplate;AutowiredprivateDeviceServicedeviceService;publicsynchronizedbooleanshouldRelogin(){if(System.currentTimeMillis()-lastReloginSuccessTime.get()RELOGIN_COOLDOWN_MS){returnfalse;}returnisRelogging.compareAndSet(false,true);}publicvoidcompleteRelogin(booleansuccess,StringnewToken){try{if(successnewToken!null){// 保存新tokenlastReloginSuccessTime.set(System.currentTimeMillis());// 更新全局token配置SecurityContextHolder.getContext().setAuthentication(newUsernamePasswordAuthenticationToken(newToken,null));}}finally{isRelogging.set(false);}}// 其他token管理方法...}b) 拦截器设计 (处理 Token 过期)ComponentpublicclassTokenExpirationInterceptorimplementsClientHttpRequestInterceptor{AutowiredprivateTokenManagertokenManager;AutowiredprivateReloginServicereloginService;OverridepublicClientHttpResponseintercept(HttpRequestrequest,byte[]body,ClientHttpRequestExecutionexecution)throwsIOException{// 1. 添加当前token到请求头StringcurrentTokengetCurrentToken();request.getHeaders().setBearerAuth(currentToken);try{ClientHttpResponseresponseexecution.execute(request,body);// 2. 检查是否token过期if(isTokenExpired(response)){// 3. 触发重登if(reloginService.relogin()){// 4. 重登成功使用新token重试请求StringnewTokengetCurrentToken();request.getHeaders().setBearerAuth(newToken);returnexecution.execute(request,body);}}returnresponse;}catch(Exceptione){thrownewIOException(Request execution failed,e);}}privatebooleanisTokenExpired(ClientHttpResponseresponse)throwsIOException{intstatusCoderesponse.getStatusCode().value();if(statusCode401||statusCode403){returntrue;}// 检查业务错误码ObjectMappermappernewObjectMapper();JsonNodebodymapper.readTree(response.getBody());intcodebody.has(code)?body.get(code).asInt():-1;returncode1005||code4001||code3002;}privateStringgetCurrentToken(){AuthenticationauthSecurityContextHolder.getContext().getAuthentication();returnauth!null?auth.getCredentials().toString():;}}c) 重登服务ServicepublicclassReloginService{AutowiredprivateTokenManagertokenManager;AutowiredprivateRestTemplaterestTemplate;Value(${api.base-url})privateStringbaseUrl;Value(${api.login-endpoint})privateStringloginEndpoint;publicbooleanrelogin(){reloginLock.lock();try{// 检查冷却期if(!tokenManager.shouldRelogin()){returntrue;// 在冷却期内视为已登录}try{// 获取设备信息DevicedevicedeviceService.getLatestDevice();if(devicenull||device.getDeviceOriginalId()0){log.error(Relogin failed: invalid device information);returnfalse;}// 构建登录请求MapString,ObjectpayloadnewHashMap();payload.put(deviceOriginalId,device.getDeviceOriginalId());// 执行登录ResponseEntityJsonNoderesponserestTemplate.postForEntity(baseUrlloginEndpoint,payload,JsonNode.class);if(response.getStatusCode().is2xxSuccessful()response.getBody()!null){JsonNoderesponseBodyresponse.getBody();intapiCoderesponseBody.has(code)?getSafeIntValue(responseBody,code):-1;if(apiCode0||apiCode200){// 提取token支持多种结构StringnewTokenextractToken(responseBody);if(newToken!null!newToken.trim().isEmpty()){tokenManager.completeRelogin(true,newToken);log.info(Relogin successful, token updated);returntrue;}}}log.error(Relogin failed: invalid response from server);returnfalse;}catch(Exceptione){log.error(Relogin failed: exception occurred,e);returnfalse;}finally{if(!tokenManager.isRelogging().get()){tokenManager.completeRelogin(false,null);}}}finally{reloginLock.unlock();}}privateStringextractToken(JsonNoderoot){if(root.has(data)root.get(data).isObject()root.get(data).has(token)){returnroot.get(data).get(token).asText();}elseif(root.has(token)){returnroot.get(token).asText();}returnnull;}privateintgetSafeIntValue(JsonNodenode,Stringfield){try{returnnode.get(field).asInt();}catch(Exceptione){try{returnInteger.parseInt(node.get(field).asText());}catch(Exceptionignore){return-1;}}}}d) 配置 RestTemplate 使用拦截器ConfigurationpublicclassApiConfig{BeanpublicRestTemplaterestTemplate(TokenExpirationInterceptorinterceptor){RestTemplaterestTemplatenewRestTemplate();ListClientHttpRequestInterceptorinterceptorsnewArrayList();interceptors.add(interceptor);restTemplate.setInterceptors(interceptors);returnrestTemplate;}}e) 响应式版本 (WebFlux)ComponentpublicclassReactiveTokenRetryFilterimplementsWebFilter{AutowiredprivateReactiveTokenManagertokenManager;AutowiredprivateReactiveReloginServicereloginService;OverridepublicMonoVoidfilter(ServerWebExchangeexchange,WebFilterChainchain){// 1. 获取当前tokenStringcurrentTokentokenManager.getCurrentToken();// 2. 添加到请求头exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION,Bearer currentToken).build();// 3. 执行请求并处理token过期returnchain.filter(exchange).onErrorResume(WebClientResponseException.Unauthorized.class,e-handleTokenExpiration(exchange,chain)).onErrorResume(WebClientResponseException.Forbidden.class,e-handleTokenExpiration(exchange,chain));}privateMonoVoidhandleTokenExpiration(ServerWebExchangeexchange,WebFilterChainchain){returnreloginService.relogin().flatMap(success-{if(success){// 重登成功使用新token重试StringnewTokentokenManager.getCurrentToken();exchange.getRequest().mutate().header(HttpHeaders.AUTHORIZATION,Bearer newToken).build();returnchain.filter(exchange);}else{// 重登失败返回401exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);returnexchange.getResponse().setComplete();}});}}3. 与原 Android 实现的核心区别线程模型Android使用锁等待队列处理多线程Spring Boot使用 Spring 的线程池和 Async 注解或响应式流存储机制AndroidSharedPreferences 存储 tokenSpring BootSecurityContext 或内存缓存可能结合数据库网络请求AndroidRetrofit OkHttpSpring BootRestTemplate/WebClient 拦截器重试机制Android回调链模式Spring BootAOP 或响应式操作符 (retryWhen)上下文管理Android显式传递 ContextSpring Boot依赖注入和上下文管理4. 优化建议使用 Spring RetryRetryable(value{TokenExpiredException.class},maxAttempts2)publicResponseEntityJsonNodemakeApiCall(){// API调用逻辑}RecoverpublicResponseEntityJsonNoderecover(TokenExpiredExceptione){// 重登逻辑}令牌刷新优化实现在令牌真正过期前主动刷新避免请求失败使用定时任务提前刷新即将过期的令牌熔断机制集成 Resilience4j当重登连续失败时触发熔断防止服务器压力过大导致雪崩监控指标使用 Micrometer 记录重登频率、成功率集成 Prometheus Grafana 监控面板三、总结TokenRetryHelper 的设计充分考虑了移动端环境的特点网络不稳定、资源受限、多线程环境复杂。在迁移到 Spring Boot 时我们需要保留其核心思想——自动处理 Token 过期、避免重复重登、线程安全同时充分利用 Spring 生态的特性如 AOP、依赖注入、响应式编程等构建更简洁、可维护的解决方案。通过拦截器Token管理器的组合模式我们可以在保持业务代码干净的同时透明地处理 Token 过期问题这比 Android 实现更加优雅也更符合后端服务的设计理念。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询