以下例子基於Spring Boot
第一步:添加Maven依賴
直接添加以下maven依賴即可
<dependency> <groupId>com.dtflys.forest</groupId> <artifactId>forest-spring-boot-starter</artifactId> <version>1.5.0</version> </dependency>
第二步:創建一個interface
就以高德地圖API為栗子吧
package com.yoursite.client; import com.dtflys.forest.annotation.Request; import com.dtflys.forest.annotation.DataParam; public interface AmapClient { /** * 聰明的你一定看出來了@Get注解代表該方法專做GET請求 * 在url中的${0}代表引用第一個參數,${1}引用第二個參數 */ @Get("http://ditu.amap.com/service/regeo?longitude=${0}&latitude=${1}") Map getLocation(String longitude, String latitude); }
第三步:掃描接口
在Spring Boot的配置類或者啟動類上加上@ForestScan
注解,並在basePackages
屬性里填上遠程接口的所在的包名
@SpringBootApplication @Configuration @ForestScan(basePackages = "com.yoursite.client") public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
第四步:調用接口
OK,我們可以愉快地調用接口了
// 注入接口實例 @Autowired private AmapClient amapClient; ... // 調用接口 Map result = amapClient.getLocation("121.475078", "31.223577"); System.out.println(result);
發送JSON數據
/** * 將對象參數解析為JSON字符串,並放在請求的Body進行傳輸 */ @Post("/register") String registerUser(@JSONBody MyUser user); /** * 將Map類型參數解析為JSON字符串,並放在請求的Body進行傳輸 */ @Post("/test/json") String postJsonMap(@JSONBody Map mapObj); /** * 直接傳入一個JSON字符串,並放在請求的Body進行傳輸 */ @Post("/test/json") String postJsonText(@JSONBody String jsonText);
發送XML數據
/** * 將一個通過JAXB注解修飾過的類型對象解析為XML字符串 * 並放在請求的Body進行傳輸 */ @Post("/message") String sendXmlMessage(@XMLBody MyMessage message); /** * 直接傳入一個XML字符串,並放在請求的Body進行傳輸 */ @Post("/test/xml") String postXmlBodyString(@XMLBody String xml);
文件上傳
/** * 用@DataFile注解修飾要上傳的參數對象 * OnProgress參數為監聽上傳進度的回調函數 */ @Post("/upload") Map upload(@DataFile("file") String filePath, OnProgress onProgress);
可以用一個方法加Lambda同時解決文件上傳和上傳的進度監聽
Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> { System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // 已上傳百分比 if (progress.isDone()) { // 是否上傳完成 System.out.println("-------- Upload Completed! --------"); } });
多文件批量上傳
/** * 上傳Map包裝的文件列表,其中 ${_key} 代表Map中每一次迭代中的鍵值 */ @Post("/upload") ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "${_key}") Map<String, byte[]> byteArrayMap); /** * 上傳List包裝的文件列表,其中 ${_index} 代表每次迭代List的循環計數(從零開始計) */ @Post("/upload") ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-${_index}.jpg") List<byte[]> byteArrayList);
文件下載
下載文件也是同樣的簡單
/** * 在方法上加上@DownloadFile注解 * dir屬性表示文件下載到哪個目錄 * OnProgress參數為監聽上傳進度的回調函數 * ${0}代表引用第一個參數 */ @Get("http://localhost:8080/images/xxx.jpg") @DownloadFile(dir = "${0}") File downloadFile(String dir, OnProgress onProgress);
調用下載接口以及監聽下載進度的代碼如下:
File file = myClient.downloadFile("D:\\TestDownload", progress -> { System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // 已下載百分比 if (progress.isDone()) { // 是否下載完成 System.out.println("-------- Download Completed! --------"); } });
基本簽名驗證
@Post("/hello/user?username=${username}") @BasicAuth(username = "${username}", password = "bar") String send(@DataVariable("username") String username);
OAuth 2.0
@OAuth2( tokenUri = "/auth/oauth/token", clientId = "password", clientSecret = "xxxxx-yyyyy-zzzzz", grantType = OAuth2.GrantType.PASSWORD, scope = "any", username = "root", password = "xxxxxx" ) @Get("/test/data") String getData();
自定義注解
Forest允許您根據需要自行定義注解,不但讓您可以簡單優雅得解決各種需求,而且極大得擴展了Forest的能力。
定義一個注解
/** * 用Forest自定義注解實現一個自定義的簽名加密注解 * 凡用此接口修飾的方法或接口,其對應的所有請求都會執行自定義的簽名加密過程 * 而自定義的簽名加密過程,由這里的@MethodLifeCycle注解指定的生命周期類進行處理 * 可以將此注解用在接口類和方法上 */ @Documented /** 重點: @MethodLifeCycle注解指定該注解的生命周期類*/ @MethodLifeCycle(MyAuthLifeCycle.class) @RequestAttributes @Retention(RetentionPolicy.RUNTIME) /** 指定該注解可用於類上或方法上 */ @Target({ElementType.TYPE, ElementType.METHOD}) public @interface MyAuth { /** * 自定義注解的屬性:用戶名 * 所有自定注解的屬性可以在生命周期類中被獲取到 */ String username(); /** * 自定義注解的屬性:密碼 * 所有自定注解的屬性可以在生命周期類中被獲取到 */ String password(); }
定義注解生命周期類
/** * MyAuthLifeCycle 為自定義的 @MyAuth 注解的生命周期類 * 因為 @MyAuth 是針對每個請求方法的,所以它實現自 MethodAnnotationLifeCycle 接口 * MethodAnnotationLifeCycle 接口帶有泛型參數 * 第一個泛型參數是該生命周期類綁定的注解類型 * 第二個泛型參數為請求方法返回的數據類型,為了盡可能適應多的不同方法的返回類型,這里使用 Object */ public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> { /** * 當方法調用時調用此方法,此時還沒有執行請求發送 * 次方法可以獲得請求對應的方法調用信息,以及動態傳入的方法調用參數列表 */ @Override public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) { System.out.println("Invoke Method '" + method.getMethodName() + "' Arguments: " + args); } /** * 發送請求前執行此方法,同攔截器中的一樣 */ @Override public boolean beforeExecute(ForestRequest request) { // 通過getAttribute方法獲取自定義注解中的屬性值 // getAttribute第一個參數為request對象,第二個參數為自定義注解中的屬性名 String username = (String) getAttribute(request, "username"); String password = (String) getAttribute(request, "password"); // 使用Base64進行加密 String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}"); // 調用addHeader方法將加密結構加到請求頭MyAuthorization中 request.addHeader("MyAuthorization", basic); return true; } /** * 此方法在請求方法初始化的時候被調用 */ @Override public void onMethodInitialized(ForestMethod method, BasicAuth annotation) { System.out.println("Method '" + method.getMethodName() + "' Initialized, Arguments: " + args); } }
使用自定義的注解
/** * 在請求接口上加上自定義的 @MyAuth 注解 * 注解的參數可以是字符串模板,通過方法調用的時候動態傳入 * 也可以是寫死的字符串 */ @Get("/hello/user?username=${username}") @MyAuth(username = "${username}", password = "bar") String send(@DataVariable("username") String username);