SpringBoot集成Forest后解决其异常堆栈打印无法取消问题

一、前言

由于项目中需要与多家单位进行接口对接,因此使用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堆栈信息。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容