news 2026/6/26 14:16:22

java 抓取对接第三方API平台数据 完整实现 | 先获取登录Token + Token鉴权查询 + 缓存重试机

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
java 抓取对接第三方API平台数据 完整实现 | 先获取登录Token + Token鉴权查询 + 缓存重试机

Markdown 教程:单类通用Token接口请求工具(无继承、纯独立类、大白话易懂)

一、功能说明

市面上大部分第三方接口流程固定:

  1. 调用登录接口,拿到鉴权Token
  2. 存下Token和过期时间,重复查询不用反复登录
  3. 查数据时带上Token请求头
  4. Token失效自动重新获取再重试一次
  5. 解析返回的列表数据

本代码单独一个类,不继承任何父类,所有配置集中在最顶部,换接口只改常量,无硬编码文字,新手直接复制就能用。

二、所需Maven依赖

<!-- http请求工具okhttp --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.0</version></dependency><!-- json解析工具fastjson --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.74</version></dependency>

三、完整独立工具类(无继承,全部逻辑写在一类)

importcom.alibaba.fastjson.JSONArray;importcom.alibaba.fastjson.JSONObject;importokhttp3.*;importjava.util.ArrayList;importjava.util.List;/** * 通用Token鉴权接口请求工具 * 执行流程:登录拿token → 缓存token复用 → 带token查询数据 → 失效自动重试 → 解析列表 * 全部配置写在顶部常量,更换接口只修改常量即可 * 不继承任何类,完全独立,可直接复制使用 */publicclassCommonTokenApiUtil{// ====================== 配置区(只修改这里,下方逻辑不用动) ======================// 获取token的登录接口地址privatestaticfinalStringLOGIN_URL="https://xxxx.com/api/IPLogin";// 分页查询数据的业务接口地址privatestaticfinalStringSEARCH_URL="https://xxxx.com/api/search";// 每页查询条数privatestaticfinalintPAGE_SIZE=50;// 提前5分钟刷新token,避免查询中途过期privatestaticfinallongREFRESH_TOKEN_TIME=5*60*1000;// 详情页面拼接前缀privatestaticfinalStringDETAIL_URL_PREFIX="https://xxxx.com/asset/searchingDetail?id=";// ==============================================================================// 缓存token,全局复用privatestaticStringcacheToken;// token过期时间戳(毫秒)privatestaticlongtokenExpireTime;// 全局统一http请求对象,复用节省资源privatestaticfinalOkHttpClientHTTP_CLIENT=newOkHttpClient();// json请求固定头部类型privatestaticfinalMediaTypeJSON_TYPE=MediaType.parse("application/json; charset=utf-8");/** * 对外暴露的统一查询入口 * @param keyword 搜索关键词 * @param pageNum 当前页码 * @return 解析后的列表数据 */publicList<ItemData>searchData(Stringkeyword,intpageNum){System.out.println("开始执行接口查询,关键词:"+keyword+",页码:"+pageNum);List<ItemData>resultList=newArrayList<>();try{// 1. 获取可用token,过期自动刷新Stringtoken=getAvailableToken();if(token==null){System.out.println("获取token失败,终止查询");returnresultList;}// 2. 构造查询接口请求参数JSONObjectsearchParam=buildSearchParam(keyword,pageNum);// 3. 第一次发起查询StringresponseJson=sendSearchRequest(token,searchParam);// 4. 返回为空说明token失效,刷新token重试一次if(responseJson==null||responseJson.isBlank()){System.out.println("token失效,重新刷新后重试查询");StringnewToken=getAvailableToken();if(newToken!=null){responseJson=sendSearchRequest(newToken,searchParam);}}// 5. 解析返回的json数据,封装成实体if(responseJson!=null&&!responseJson.isBlank()){resultList=parseResponseJson(responseJson);System.out.println("查询完成,本次返回数据条数:"+resultList.size());}}catch(Exceptione){System.out.println("查询接口出现异常:"+e.getMessage());e.printStackTrace();}returnresultList;}/** * 获取有效的token,线程安全,未过期直接复用,过期重新登录 */privatesynchronizedStringgetAvailableToken(){longnowTime=System.currentTimeMillis();// 判断token存在且未到刷新时间,直接返回缓存tokenif(cacheToken!=null&&nowTime<tokenExpireTime-REFRESH_TOKEN_TIME){returncacheToken;}// 重新调用登录接口获取新tokenreturnrefreshToken();}/** * 调用登录接口,刷新全新token */privateStringrefreshToken(){try{// 登录接口请求体,无参数传空json对象JSONObjectloginBody=newJSONObject();Requestrequest=newRequest.Builder().url(LOGIN_URL).post(RequestBody.create(JSON_TYPE,loginBody.toJSONString())).addHeader("Content-Type","application/json").build();// 执行请求try(Responseresponse=HTTP_CLIENT.newCall(request).execute()){if(!response.isSuccessful()||response.body()==null){System.out.println("登录接口请求失败,状态码:"+response.code());returnnull;}StringrespStr=response.body().string();JSONObjectrespJson=JSONObject.parseObject(respStr);// 判断登录是否成功if(!respJson.getBooleanValue("succeeded")){System.out.println("登录获取token失败,提示:"+respJson.getString("message"));returnnull;}JSONObjectdata=respJson.getJSONObject("data");// 缓存tokencacheToken=data.getString("token");// 计算过期时间戳intexpireSeconds=data.getIntValue("expiredOn");tokenExpireTime=System.currentTimeMillis()+expireSeconds*1000L;System.out.println("token刷新成功,有效时长:"+expireSeconds+"秒");returncacheToken;}}catch(Exceptione){System.out.println("刷新token发生异常:"+e.getMessage());e.printStackTrace();returnnull;}}/** * 构造查询接口需要的json参数 */privateJSONObjectbuildSearchParam(Stringkeyword,intpageNum){JSONObjectbody=newJSONObject();body.put("sort","");body.put("pageIndex",pageNum);body.put("pageSize",PAGE_SIZE);body.put("keywords",keyword);returnbody;}/** * 携带token发送查询POST请求 */privateStringsendSearchRequest(Stringtoken,JSONObjectparamJson)throwsException{Requestrequest=newRequest.Builder().url(SEARCH_URL).post(RequestBody.create(JSON_TYPE,paramJson.toJSONString())).addHeader("Content-Type","application/json").addHeader("Authorization","Bearer "+token).build();try(Responseresponse=HTTP_CLIENT.newCall(request).execute()){if(response.isSuccessful()&&response.body()!=null){returnresponse.body().string();}System.out.println("数据查询接口请求失败,状态码:"+response.code());returnnull;}}/** * 解析接口返回的json,封装成自定义实体列表 */privateList<ItemData>parseResponseJson(StringjsonStr){List<ItemData>list=newArrayList<>();JSONObjectrootJson=JSONObject.parseObject(jsonStr);// 判断接口返回成功if(!rootJson.getBooleanValue("succeeded")){System.out.println("查询接口业务失败,提示:"+rootJson.getString("message"));returnlist;}JSONObjectdataObj=rootJson.getJSONObject("data");JSONObjecthitsObj=dataObj.getJSONObject("hits");JSONArraysourceArr=hitsObj.getJSONArray("source");if(sourceArr==null||sourceArr.isEmpty()){System.out.println("当前页码无数据");returnlist;}// 循环每一条数据封装for(inti=0;i<sourceArr.size();i++){JSONObjectitem=sourceArr.getJSONObject(i);ItemDatadata=newItemData();data.setTitle(item.getString("title"));data.setAuthor(item.getString("author"));data.setSource(item.getString("journal_name"));data.setAbstractText(item.getString("abstract"));data.setPublishDate(item.getString("pub_date_display"));data.setKeyword(item.getString("keyword"));// 拼接详情链接Stringid=item.getString("id");if(id!=null){data.setDetailUrl(DETAIL_URL_PREFIX+id);}list.add(data);}returnlist;}// 内部实体:封装每条返回的数据publicstaticclassItemData{privateStringtitle;privateStringauthor;privateStringsource;privateStringabstractText;privateStringpublishDate;privateStringkeyword;privateStringdetailUrl;// getter setterpublicStringgetTitle(){returntitle;}publicvoidsetTitle(Stringtitle){this.title=title;}publicStringgetAuthor(){returnauthor;}publicvoidsetAuthor(Stringauthor){this.author=author;}publicStringgetSource(){returnsource;}publicvoidsetSource(Stringsource){this.source=source;}publicStringgetAbstractText(){returnabstractText;}publicvoidsetAbstractText(StringabstractText){this.abstractText=abstractText;}publicStringgetPublishDate(){returnpublishDate;}publicvoidsetPublishDate(StringpublishDate){this.publishDate=publishDate;}publicStringgetKeyword(){returnkeyword;}publicvoidsetKeyword(Stringkeyword){this.keyword=keyword;}publicStringgetDetailUrl(){returndetailUrl;}publicvoidsetDetailUrl(StringdetailUrl){this.detailUrl=detailUrl;}}// 测试main方法,直接运行就能测试publicstaticvoidmain(String[]args){CommonTokenApiUtilutil=newCommonTokenApiUtil();// 查询关键词,第一页List<ItemData>dataList=util.searchData("测试关键词",1);for(ItemDataitem:dataList){System.out.println("标题:"+item.getTitle()+" 作者:"+item.getAuthor());}}}

四、代码大白话讲解

1. 顶部常量配置区

所有接口地址、分页大小、刷新时间全部写在最上面,以后换别的第三方接口,只改这几行,不用动下面业务代码。

2. 核心变量说明

  • cacheToken:存登录拿到的令牌,不用每次搜索都登录一次
  • tokenExpireTime:记录token什么时候过期,提前5分钟自动换新
  • HTTP_CLIENT:全局只用一个请求工具,频繁调用不会创建大量连接

3. 方法功能拆解

  1. searchData:对外调用入口,传入关键词和页码直接拿结果
  2. getAvailableToken:判断token是否过期,过期自动刷新(加了同步锁,多线程不会同时刷新)
  3. refreshToken:调用登录接口获取新token,更新缓存和过期时间
  4. buildSearchParam:组装查询接口需要的一长串json参数,单独抽出来方便修改字段
  5. sendSearchRequest:带上token请求头发送查询
  6. parseResponseJson:把后端返回的复杂json,转换成简单实体对象,方便业务使用

4. 容错逻辑

  • 拿不到token直接返回空列表,打印日志
  • 查询返回空自动刷新token重试一次,解决token中途失效问题
  • 所有网络、解析异常全部捕获打印,不会直接崩溃程序

五、使用方法

  1. 复制整个类到项目,无需引入父类、无需实现接口
  2. 修改顶部常量里的接口地址
  3. 在业务代码调用:
CommonTokenApiUtilapi=newCommonTokenApiUtil();List<CommonTokenApiUtil.ItemData>data=api.searchData("人工智能",1);
  1. 自带main函数,右键运行可直接调试接口,快速验证是否能正常拿到数据
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 14:15:34

高速信号端接实战:LVPECL、LVDS、HCSL、LVCMOS配置与调试指南

1. 项目概述&#xff1a;为什么信号端接是高速设计的“必修课”在调试一块高速FPGA板卡或者一个多路时钟分发系统时&#xff0c;你可能遇到过这样的场景&#xff1a;示波器上本该干净利落的时钟边沿&#xff0c;却拖着一个长长的“尾巴”&#xff08;振铃&#xff09;&#xff…

作者头像 李华
网站建设 2026/6/26 14:13:15

终极指南:如何用dnSpyEx进行专业级代码审查与智能分析

终极指南&#xff1a;如何用dnSpyEx进行专业级代码审查与智能分析 【免费下载链接】dnSpy Unofficial revival of the well known .NET debugger and assembly editor, dnSpy 项目地址: https://gitcode.com/gh_mirrors/dns/dnSpy dnSpyEx作为.NET逆向工程领域的专业工具…

作者头像 李华
网站建设 2026/6/26 14:10:50

Linux下Spotify广告拦截实战:基于流量分析与LD_PRELOAD注入

1. 项目概述&#xff1a;为什么我们需要一个纯净的Spotify体验&#xff1f;作为一个在Linux桌面环境里泡了十多年的老用户&#xff0c;我几乎把所有主流发行版都当主力机用过一遍。从早期的Ubuntu、Fedora到现在的Arch、Manjaro&#xff0c;一个绕不开的痛点就是流媒体服务的“…

作者头像 李华
网站建设 2026/6/26 14:10:07

五款办公智能体实测:差异不在“能不能做”,而在“怎么做”

桌面端办公智能体3月爆发今年3月&#xff0c;桌面端办公智能体迎来一波爆发。易观分析数据显示&#xff0c;当月头部产品月访问量合计超过2000万次&#xff0c;腾讯WorkBuddy以885万排在第一。也是在这个月&#xff0c;腾讯云在上海城市峰会上发布了AI Agent产品全景图&#xf…

作者头像 李华
网站建设 2026/6/26 14:07:01

自媒体账号安全隔离工具完全指南:选型标准、避坑法则与落地实操

做自媒体矩阵、多账号运营&#xff0c;账号安全永远是第一底线。不少人踩过 “一死死一片” 的连坐封号坑&#xff0c;也试过换 IP、多开浏览器、买备用机等各种土办法&#xff0c;但要么隔离效果不达预期&#xff0c;要么管理成本高到抵消收益。很多人对账号安全隔离工具的认知…

作者头像 李华
网站建设 2026/6/26 14:06:57

打破苹果硬件限制:OpenCore Legacy Patcher让老旧Mac重获新生

打破苹果硬件限制&#xff1a;OpenCore Legacy Patcher让老旧Mac重获新生 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 你是否拥有2007-2018年间的Intel Ma…

作者头像 李华