{"id":13458695,"url":"https://github.com/dromara/forest","last_synced_at":"2025-05-13T20:22:25.147Z","repository":{"id":37416985,"uuid":"88974574","full_name":"dromara/forest","owner":"dromara","description":"A high-level and lightweight declarative HTTP client framework for Java. it makes sending HTTP requests in Java easier.","archived":false,"fork":false,"pushed_at":"2025-04-27T03:04:44.000Z","size":8372,"stargazers_count":1798,"open_issues_count":74,"forks_count":228,"subscribers_count":29,"default_branch":"master","last_synced_at":"2025-04-28T11:57:36.656Z","etag":null,"topics":["declarative-http-client","fegin","http","http-client","https","java-http-client","request","rest","restful","retrofit","socks"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dromara.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-21T10:53:29.000Z","updated_at":"2025-04-27T03:04:48.000Z","dependencies_parsed_at":"2024-04-10T04:23:26.388Z","dependency_job_id":"11848dfe-f1c0-4e78-9d91-3d89f06be3e6","html_url":"https://github.com/dromara/forest","commit_stats":{"total_commits":2480,"total_committers":33,"mean_commits":75.15151515151516,"dds":"0.44999999999999996","last_synced_commit":"05bf17881ca489c1c877f27ffa1af5fa9f1b6f3e"},"previous_names":["mysinglelive/forest"],"tags_count":83,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Fforest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Fforest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Fforest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dromara%2Fforest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dromara","download_url":"https://codeload.github.com/dromara/forest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251311332,"owners_count":21569008,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["declarative-http-client","fegin","http","http-client","https","java-http-client","request","rest","restful","retrofit","socks"],"created_at":"2024-07-31T09:00:55.257Z","updated_at":"2025-04-28T11:57:58.115Z","avatar_url":"https://github.com/dromara.png","language":"Java","readme":"\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://forest.dtflyx.com/\"\u003e\n    \u003cimg width=\"300\" src=\"site/media/logo3.png\" alt=\"logo\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/JDK-1.8+-yellow\" alt=\"JDK\"\u003e\n\u003c/a\u003e\n\n\u003ca href=\"https://opensource.org/licenses/mit-license.php\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/license-MIT-blue.svg\" alt=\"License\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://forest.dtflyx.com/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/document-1.x-e96.svg\" alt=\"Documentation\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://forest.dtflyx.com/pages/author/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/author-%E5%85%AC%E5%AD%90%E9%AA%8F-7af\" alt=\"Author\"\u003e\n\u003c/a\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eForest - 声明式HTTP客户端框架\u003c/h1\u003e\n\n项目介绍：\n-------------------------------------\n\nForest是一个高层的、极简的声明式HTTP调用API框架\u003cbr\u003e\n相比于直接使用Httpclient您不再用写一大堆重复的代码了，而是像调用本地方法一样去发送HTTP请求\n\n\n#### 获得荣誉\n\n- **2021 年度 OSC 中国开源项目评选「最受欢迎项目」**\n- **2022 年度 OSC 中国开源项目评选「最火热中国开源项目社区」**\n\n文档和示例：\n-------------------------------------\n* [项目主页](https://forest.dtflyx.com/) \n\n* [中文文档](https://forest.dtflyx.com/pages/1.6.x/install_guide/) \n\n* [示例工程](forest-examples)\n\nForest有哪些特性？\n-----\n* 同时支持编程式与声明式的请求发送方式\n* 以Httpclient和OkHttp为后端框架\n* 通过调用本地方法的方式去发送Http请求, 实现了业务逻辑与Http协议之间的解耦\n* 因为针对第三方接口，所以不需要依赖Spring Cloud和任何注册中心\n* 支持所有请求方法：GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH\n* 支持文件上传和下载\n* 支持灵活的模板表达式\n* 支持拦截器处理请求的各个生命周期\n* 支持自定义注解\n* 支持OAuth2验证\n* 支持过滤器来过滤传入的数据\n* 基于注解、配置化的方式定义Http请求\n* 支持Spring和Springboot集成\n* JSON格式数据序列化和反序列化\n* XML格式数据序列化和反序列化\n* Protobuf格式数据序列化和反序列化\n* JSON、XML或其他类型转换器可以随意扩展和替换\n* 支持JSON转换框架: Fastjson2, Fastjson1, Jackson, Gson\n* 支持JAXB形式的XML转换\n* 可以通过OnSuccess和OnError接口参数实现请求结果的回调\n* 配置简单，一般只需要@Request一个注解就能完成绝大多数请求的定义\n* 支持异步请求调用\n* 支持SSE\n\n极速开始\n-------------------------------------\n以下例子基于Spring Boot\n\n### 第一步：添加Maven依赖\n\n直接添加以下maven依赖即可\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.dtflys.forest\u003c/groupId\u003e\n    \u003cartifactId\u003eforest-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e1.6.4\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### 第二步：创建一个`interface`\n\n就以高德地图API为栗子吧\n\n```java\n\npackage com.yoursite.client;\n\nimport com.dtflys.forest.annotation.Request;\nimport com.dtflys.forest.annotation.DataParam;\n\npublic interface AmapClient {\n\n    /**\n     * 聪明的你一定看出来了@Get注解代表该方法专做GET请求\n     * 在url中的{0}代表引用第一个参数，{1}引用第二个参数\n     */\n    @Get(\"http://ditu.amap.com/service/regeo?longitude={0}\u0026latitude={1}\")\n    Map getLocation(String longitude, String latitude);\n}\n\n```\n\n### 第三步：扫描接口\n\n在Spring Boot的配置类或者启动类上加上`@ForestScan`注解，并在`basePackages`属性里填上远程接口的所在的包名\n\n```java\n@SpringBootApplication\n@Configuration\n@ForestScan(basePackages = \"com.yoursite.client\")\npublic class MyApplication {\n  public static void main(String[] args) {\n      SpringApplication.run(MyApplication.class, args);\n   }\n}\n```\n\n### 第四步：调用接口\n\nOK，我们可以愉快地调用接口了\n\n```java\n// 注入接口实例\n@Autowired\nprivate AmapClient amapClient;\n...\n// 调用接口\nMap result = amapClient.getLocation(\"121.475078\", \"31.223577\");\nSystem.out.println(result);\n```\n\n## 发送JSON数据\n\n```java\n/**\n * 将对象参数解析为JSON字符串，并放在请求的Body进行传输\n */\n@Post(\"/register\")\nString registerUser(@JSONBody MyUser user);\n\n/**\n * 将Map类型参数解析为JSON字符串，并放在请求的Body进行传输\n */\n@Post(\"/test/json\")\nString postJsonMap(@JSONBody Map mapObj);\n\n/**\n * 直接传入一个JSON字符串，并放在请求的Body进行传输\n */\n@Post(\"/test/json\")\nString postJsonText(@JSONBody String jsonText);\n```\n\n## 发送XML数据\n\n```java\n/**\n * 将一个通过JAXB注解修饰过的类型对象解析为XML字符串\n * 并放在请求的Body进行传输\n */\n@Post(\"/message\")\nString sendXmlMessage(@XMLBody MyMessage message);\n\n/**\n * 直接传入一个XML字符串，并放在请求的Body进行传输\n */\n@Post(\"/test/xml\")\nString postXmlBodyString(@XMLBody String xml);\n```\n\n## 发送Protobuf数据\n\n```java\n/**\n * ProtobufProto.MyMessage 为 Protobuf 生成的数据类\n * 将 Protobuf 生成的数据对象转换为 Protobuf 格式的字节流\n * 并放在请求的Body进行传输\n * \n * 注: 需要引入 google protobuf 依赖\n */\n@Post(url = \"/message\", contentType = \"application/octet-stream\")\nString sendProtobufMessage(@ProtobufBody ProtobufProto.MyMessage message);\n```\n\n\n## 文件上传\n\n```java\n/**\n * 用@DataFile注解修饰要上传的参数对象\n * OnProgress参数为监听上传进度的回调函数\n */\n@Post(\"/upload\")\nMap upload(@DataFile(\"file\") String filePath, OnProgress onProgress);\n```\n\n可以用一个方法加Lambda同时解决文件上传和上传的进度监听\n\n```java\nMap result = myClient.upload(\"D:\\\\TestUpload\\\\xxx.jpg\", progress -\u003e {\n    System.out.println(\"progress: \" + Math.round(progress.getRate() * 100) + \"%\");  // 已上传百分比\n    if (progress.isDone()) {   // 是否上传完成\n        System.out.println(\"--------   Upload Completed!   --------\");\n    }\n});\n```\n\n## 多文件批量上传\n\n```java\n/**\n * 上传Map包装的文件列表，其中 {_key} 代表Map中每一次迭代中的键值\n */\n@Post(\"/upload\")\nForestRequest\u003cMap\u003e uploadByteArrayMap(@DataFile(value = \"file\", fileName = \"{_key}\") Map\u003cString, byte[]\u003e byteArrayMap);\n\n/**\n * 上传List包装的文件列表，其中 {_index} 代表每次迭代List的循环计数（从零开始计）\n */\n@Post(\"/upload\")\nForestRequest\u003cMap\u003e uploadByteArrayList(@DataFile(value = \"file\", fileName = \"test-img-{_index}.jpg\") List\u003cbyte[]\u003e byteArrayList);\n```\n\n## 文件下载\n\n下载文件也是同样的简单\n\n```java\n/**\n * 在方法上加上@DownloadFile注解\n * dir属性表示文件下载到哪个目录\n * OnProgress参数为监听上传进度的回调函数\n * {0}代表引用第一个参数\n */\n@Get(\"http://localhost:8080/images/xxx.jpg\")\n@DownloadFile(dir = \"{0}\")\nFile downloadFile(String dir, OnProgress onProgress);\n```\n\n\n调用下载接口以及监听下载进度的代码如下：\n\n```java\nFile file = myClient.downloadFile(\"D:\\\\TestDownload\", progress -\u003e {\n    System.out.println(\"progress: \" + Math.round(progress.getRate() * 100) + \"%\");  // 已下载百分比\n    if (progress.isDone()) {   // 是否下载完成\n        System.out.println(\"--------   Download Completed!   --------\");\n    }\n});\n```\n\n## 基本签名验证\n\n```java\n@Post(\"/hello/user?username={username}\")\n@BasicAuth(username = \"{username}\", password = \"bar\")\nString send(@DataVariable(\"username\") String username);\n```\n\n## OAuth 2.0\n\n```java\n@OAuth2(\n        tokenUri = \"/auth/oauth/token\",\n        clientId = \"password\",\n        clientSecret = \"xxxxx-yyyyy-zzzzz\",\n        grantType = OAuth2.GrantType.PASSWORD,\n        scope = \"any\",\n        username = \"root\",\n        password = \"xxxxxx\"\n)\n@Get(\"/test/data\")\nString getData();\n```\n\n## 自定义注解\n\nForest允许您根据需要自行定义注解，不但让您可以简单优雅得解决各种需求，而且极大得扩展了Forest的能力。\n\n### 定义一个注解\n\n```java\n/**\n * 用Forest自定义注解实现一个自定义的签名加密注解\n * 凡用此接口修饰的方法或接口，其对应的所有请求都会执行自定义的签名加密过程\n * 而自定义的签名加密过程，由这里的@MethodLifeCycle注解指定的生命周期类进行处理\n * 可以将此注解用在接口类和方法上\n */\n@Documented\n/** 重点： @MethodLifeCycle注解指定该注解的生命周期类*/\n@MethodLifeCycle(MyAuthLifeCycle.class)\n@RequestAttributes\n@Retention(RetentionPolicy.RUNTIME)\n/** 指定该注解可用于类上或方法上 */\n@Target({ElementType.TYPE, ElementType.METHOD})\npublic @interface MyAuth {\n\n    /** \n     * 自定义注解的属性：用户名\n     * 所有自定注解的属性可以在生命周期类中被获取到\n     */\n    String username();\n\n    /** \n     * 自定义注解的属性：密码\n     * 所有自定注解的属性可以在生命周期类中被获取到\n     */\n    String password();\n}\n```\n\n### 定义注解生命周期类\n\n```java\n/**\n *  MyAuthLifeCycle 为自定义的 @MyAuth 注解的生命周期类\n * 因为 @MyAuth 是针对每个请求方法的，所以它实现自 MethodAnnotationLifeCycle 接口\n * MethodAnnotationLifeCycle 接口带有泛型参数\n * 第一个泛型参数是该生命周期类绑定的注解类型\n * 第二个泛型参数为请求方法返回的数据类型，为了尽可能适应多的不同方法的返回类型，这里使用 Object\n */\npublic class MyAuthLifeCycle implements MethodAnnotationLifeCycle\u003cMyAuth, Object\u003e {\n\n \n    /**\n     * 当方法调用时调用此方法，此时还没有执行请求发送\n     * 次方法可以获得请求对应的方法调用信息，以及动态传入的方法调用参数列表\n     */\n    @Override\n    public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {\n        System.out.println(\"Invoke Method '\" + method.getMethodName() + \"' Arguments: \" + args);\n    }\n\n    /**\n     * 发送请求前执行此方法，同拦截器中的一样\n     */\n    @Override\n    public boolean beforeExecute(ForestRequest request) {\n        // 通过getAttribute方法获取自定义注解中的属性值\n        // getAttribute第一个参数为request对象，第二个参数为自定义注解中的属性名\n        String username = (String) getAttribute(request, \"username\");\n        String password = (String) getAttribute(request, \"password\");\n        // 使用Base64进行加密\n        String basic = \"MyAuth \" + Base64Utils.encode(\"{\" + username + \":\" + password + \"}\");\n        // 调用addHeader方法将加密结构加到请求头MyAuthorization中\n        request.addHeader(\"MyAuthorization\", basic);\n        return true;\n    }\n\n    /**\n     * 此方法在请求方法初始化的时候被调用\n     */\n    @Override\n    public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {\n        System.out.println(\"Method '\" + method.getMethodName() + \"' Initialized, Arguments: \" + args);\n    }\n}\n```\n\n### 使用自定义的注解\n\n```java\n/**\n * 在请求接口上加上自定义的 @MyAuth 注解\n * 注解的参数可以是字符串模板，通过方法调用的时候动态传入\n * 也可以是写死的字符串\n */\n@Get(\"/hello/user?username={username}\")\n@MyAuth(username = \"{username}\", password = \"bar\")\nString send(@DataVariable(\"username\") String username);\n```\n\n### 编程式请求\n\nForest 的编程式请求支持链式调用，极为方便、高效、简洁\n\n```java\n// GET 请求访问百度\nString baidu = Forest.get(\"http://www.baidu.com\").execute(String.class);\n\n// POST 请求注册用户信息\nString result = Forest.post(\"/user/register\")\n        .contentType(\"application/json\")\n        .addBody(\"username\", \"公子骏\")\n        .addBody(\"password\", \"12345678\")\n        .execute(String.class);\n```\n\n#### 详细文档请看：[官方文档](https://forest.dtflyx.com/)\n\n\n## 获得奖项\n\n\u003cimg src=\"doc/images/osc-2021.jpg\" width=\"377px\"\u003e\n\n\u003e 2021 年度 OSC 中国开源项目评选「最受欢迎项目」\n\n\n## 联系作者\n\n亲，进群前记得先star一下哦~\n\n扫描二维码关注公众号，点击菜单中的 `进群` 按钮即可进群\n\n![avatar](doc/images/qr_code.jpg)\n\n他们在用\n-----------------------------------\n已在使用Forest的公司列表（排名不分先后）\n\u003cdiv\u003e\n    \u003ctable\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.huawei.com/cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_huawei.png\" class=\"no-zoom\" alt=\"华为\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.thebeastshop.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_thebeastshop.jpg\" class=\"no-zoom\" alt=\"野兽派花店\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://zgh.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_geely.png\" class=\"no-zoom\" alt=\"吉利集团\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.ictbda.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_ictbda.png\" class=\"no-zoom\" alt=\"中科院计算所大数据研究院\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.woshipm.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 58px; padding: 1px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #4470f5\"\u003e\u003cimg height=\"56px\" src=\"/doc/images/logo/logo_woshipm.webp\" class=\"no-zoom\" alt=\"人人都是产品经理\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://tldt.net/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #1590d6;\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_tldt.png\" class=\"no-zoom\" alt=\"神州通立电梯\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://weidubim.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #222222;\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_weidubim.png\" class=\"no-zoom\" alt=\"万智维度\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.yiring.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"60px\" src=\"/doc/images/logo/logo_yiring.png\" class=\"no-zoom\" alt=\"壹润\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://gzsunrun.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_gzsunrun.jpg\" class=\"no-zoom\" alt=\"尚融网络科技\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.huafang-aiot.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_huafangzhilian.png\" class=\"no-zoom\" alt=\"华方智联\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.hyperchain.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_hyperchain.png\" class=\"no-zoom\" alt=\"趣链科技\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.byai.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #2b58fa;\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_byai.png\" class=\"no-zoom\" alt=\"百应\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://www.datapps.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;\"\u003e\u003cimg height=\"60px\" src=\"/doc/images/logo/logo_datapps.png\" class=\"no-zoom\" alt=\"聚云位智\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://m.hibobi.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_hibobi.png\" class=\"no-zoom\" alt=\"嗨宝贝\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://hzqianqi.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_hzqianqi.png\" class=\"no-zoom\" alt=\"仟奇\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.swifthealth.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff;\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_swifthealth.png\" class=\"no-zoom\" alt=\"朝前智能\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.manyibar.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_manyibar.png\" class=\"no-zoom\" alt=\"满意吧\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://www.ue-one.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;\"\u003e\u003cimg height=\"60px\" src=\"/doc/images/logo/logo_ue-one.png\" class=\"no-zoom\" alt=\"源一科技\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n        \u003ctr\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"https://www.xwsoft.com.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #333333;\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_xwsoft.png\" class=\"no-zoom\" alt=\"欣网视讯\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://www.ynjzh.com/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_ynjzh.png\" class=\"no-zoom\" alt=\"嘉之会科技\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n            \u003ctd style=\"width: 500px\"\u003e\u003ca style=\"cursor: pointer;\" href=\"http://www.xingsnb.cn/\" target=\"_blank\"\u003e\u003cdiv style=\"height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff\"\u003e\u003cimg height=\"40px\" src=\"/doc/images/logo/logo_xingsnb.jpg\" class=\"no-zoom\" alt=\"星晟工程\"\u003e\u003c/div\u003e\u003c/a\u003e\u003c/td\u003e\n        \u003c/tr\u003e\n    \u003c/table\u003e\n\u003c/div\u003e\n\n\n参与贡献\n-----------------------------------\n\n1. 进群讨论，可以在群里抛出您遇到的问题，或许已经有人解决了您的问题。\n2. 提issue，如果在issue中已经有您想解决的问题，可以直接将该issue分配给您自己。如若没有，可以自己创建一个issue。\n3. Fork 本项目的仓库\n4. 新建分支，如果是加新特性，分支名格式为`feat_${issue的ID号}`，如果是修改bug，则命名为`fix_${issue的ID号}`。\n5. 本地自测，提交前请通过所有的已经单元测试，以及为您要解决的问题新增单元测试。\n6. 提交代码\n7. 新建 Pull Request\n8. 我会对您的PR进行验证和测试，如通过测试，我会合到`dev`分支上随新版本发布时再合到`master`分支上。\n\n欢迎小伙伴们多提issue和PR，被接纳PR的小伙伴我会列在`README`上的贡献者列表中:）\n\n项目协议\n--------------------------\nThe MIT License (MIT)\n\nCopyright (c) 2016 Jun Gong\n\n\n","funding_links":[],"categories":["Java"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdromara%2Fforest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdromara%2Fforest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdromara%2Fforest/lists"}