一、前言
由于项目中需要与多家单位进行接口对接,因此使用Forest进行统一配置,进行接口调取。Forest默认使用的是okhttp作为http请求客户端(也可以改为httpclient),本着不重复造轮子,既然已经有很好用的轮子,那就当个拿来主义。
事实证明,Forest也确实足够令人惊艳!
Forest采用注解式方式对URL、传参均可进行外部输入,很是灵活!
但是,凡是均有但是,使用Forest注解式开发,在调用时使用Exception进行捕获异常时,无论使用官方方式捕获ForestRuntimeException异常,还是在yml中配置取消Forest日志打印(logging.level.com.dtflys.forest: OFF)、亦或是使用OnError或ForestResponse作为返回值,试用,均无效。
以下方式涉及修改源码,其实非常简单,但是思路不容易想到,在此进行记录,以飨后来者。(可直接看第三章)
二、集成与使用
2.1 项目信息
- SpringBoot3.3.0
- JDK21
2.2 引入Forest依赖
<dependencies>
...
<!-- Forest:声明式与编程式双修,让天下没有难以发送的 HTTP 请求 -->
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot3-starter</artifactId>
<version>1.7.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.51</version>
</dependency>
...
</dependencies>
根据Forest要求,Fastjson2依赖:版本 >= 2.0.46
2.3 注解式接口示例
具体可见官网
package cn.keyidea.interfacegather.forest.client;
import cn.keyidea.common.bean.BaseRes;
import cn.keyidea.second.vo.ClockVo;
import com.alibaba.fastjson2.JSONObject;
import com.dtflys.forest.annotation.*;
import com.dtflys.forest.http.ForestResponse;
/**
* [二期]
* Forest接口,与xx之间接口
*
* @link <pre>Forest https://forest.dtflyx.com/<pre/>
*
* @author qyd
* @date 2025-05-13
*/
@Address(host = "192.168.1.11", port = "8085")
public interface MyForestClientForBupt {
/**
* 路由计算接口
* --------------------------------------------------------------------
* contentType属性设置为 application/x-www-form-urlencoded 即为表单格式,
* 当然不设置的时候默认值也为 application/x-www-form-urlencoded, 也同样是表单格式。
* 在 @Body 注解的 value 属性中设置的名称为表单项的 key 名,
* 而注解所修饰的参数值即为表单项的值,它可以为任何类型,不过最终都会转换为字符串进行传输。
*
* @param sourceUuid 源卫星
* @param destUuid 目的卫星
* @param calcPolicy 默认值:hop
* @param snapshotTime 时间戳
* @return 返回结果如下:
* <pre>
* {
* "pathResults": [
* {
* "pathKey": {
* "sourceUuid": "string", # 源卫星
* "destUuid": "string", # 目的卫星
* "snapshotTime": 0 # 时间戳
* },
* "primaryPath": {
* "path": [
* "string" # 经过的路径信息
* ]
* }
* }
* ]
* }
* </>
*/
@LogEnabled(false)
@Post(url = "pce/route/path/calc", contentType = "application/json")
public JSONObject pceRoutePathCalc(@Body("sourceUuid") String sourceUuid,
@Body("destUuid") String destUuid,
@Body("calcPolicy") String calcPolicy,
@Body("snapshotTime") Long snapshotTime);
// 异常处理 | Forest https://forest.dtflyx.com/pages/1.6.x/exception/
@Post(url = "pce/route/path/calc", contentType = "application/json")
public ForestResponse<JSONObject> pceRoutePathCalcForRes(@Body("sourceUuid") String sourceUuid,
@Body("destUuid") String destUuid,
@Body("calcPolicy") String calcPolicy,
@Body("snapshotTime") Long snapshotTime);
/**
* 路由计算接口(传参地址)
*
* @param url 请求的地址
* @param sourceUuid 源卫星
* @param destUuid 目的卫星
* @param calcPolicy 默认值:hop
* @param snapshotTime 时间戳
* @return 返回结果
*/
@LogEnabled(false)
@Post(url = "http://{url}/pce/route/path/calc", contentType = "application/json")
public JSONObject pceRoutePathCalcByUrl(@Var("url") String url,
@Body("sourceUuid") String sourceUuid,
@Body("destUuid") String destUuid,
@Body("calcPolicy") String calcPolicy,
@Body("snapshotTime") Long snapshotTime);
/**
* 拓扑接口:查询过载链路
*/
@LogEnabled(false)
@Post(url = "topo/links/overload/query", contentType = "application/json")
public JSONObject linksOverloadQuery();
/**
* 拓扑接口:查询过载链路(传参地址)
*
* @param url 请求的地址
*/
@LogEnabled(false)
@Post(url = "http://{url}/topo/links/overload/query", contentType = "application/json")
public JSONObject linksOverloadQueryByUrl(@Var("url") String url);
/**
* 拓扑接口:查询卫星接口
*
* @param brief
*/
@LogEnabled(false)
@Post(url = "topo/satellites/query", contentType = "application/json")
public JSONObject satellitesQuery(@Body(value = "brief", defaultValue = "true") boolean brief);
/**
* 拓扑接口:查询卫星接口(传参地址)
*
* @param url 请求的地址
* @param brief
* @return 返回结果
*/
@LogEnabled(false)
@Post(url = "http://{url}/topo/satellites/query", contentType = "application/json")
public JSONObject satellitesQueryByUrl(@Var("url") String url,
@Body(value = "brief", defaultValue = "true") boolean brief);
/**
* 拓扑接口:根据uuid查询链路接口
*
* @param jsonReq 请求参数
* @return 返回结果
* 请求参数
* <pre>
* {
* "uuids":
* [
* "string" //链路UUID
* ]
* }
* </pre>
*/
@LogEnabled(false)
@Post(url = "topo/links/query", contentType = "application/json")
public JSONObject linksQuery(@Body JSONObject jsonReq);
/**
* 拓扑接口:根据uuid查询链路接口(传参地址)
*
* @param url 请求的地址
* @param jsonReq 请求参数
* @return 返回结果
* 请求参数
* <pre>
* {
* "uuids":
* [
* "string" //链路UUID
* ]
* }
* </pre>
*/
@LogEnabled(false)
@Post(url = "http://{url}/topo/links/query", contentType = "application/json")
public JSONObject linksQueryByUrl(@Var("url") String url,
@Body JSONObject jsonReq);
/**
* 拓扑接口:查询所有链路接口
*
* @param jsonReq 请求参数
* @return 返回结果
* 请求参数
* <pre>
* {
* "times":
* [
* 1719902560 // 查询链路的时间
* ]
* }
* </pre>
*/
@LogEnabled(false)
@Post(url = "topo/links/querysnapshotbytime", contentType = "application/json")
public JSONObject linksQuerysnapshotbytime(@Body JSONObject jsonReq);
/**
* 拓扑接口:查询所有链路接口(传参地址)
*
* @param url 请求的地址
* @param jsonReq 请求参数
* @return 返回结果
* 请求参数
* <pre>
* {
* "times":
* [
* 1719902560 // 查询链路的时间
* ]
* }
* </pre>
*/
@LogEnabled(false)
@Post(url = "http://{url}/topo/links/querysnapshotbytime", contentType = "application/json")
public JSONObject linksQuerysnapshotbytimeByUrl(@Var("url") String url,
@Body JSONObject jsonReq);
/**
* 仿真时间同步接口
*
* @param vo 仿真同步对象
* @return 返回结果
*/
@LogEnabled(false)
@Post(url = "{url}/scene/start", contentType = "application/json")
public BaseRes sceneStart(@Body ClockVo vo);
}
接口上的
@LogEnabled(false)注解,是为了不打印请求地址等信息。
三、解决控制台Forest打印异常堆栈问题
主要来源于com.dtflys.forest.reflection.MethodLifeCycleHandler.java这个类中的L109的th.printStackTrace();
只需要将此行注释掉,然后在src/main/java下新建同级目录com/dtflys/forest/reflection,将修改后的MethodLifeCycleHandler.java文件丢到src\main\java\com\dtflys\forest\reflection目录下,重启服务,即可发现控制台再也不打印Forest堆栈信息。