44 Star 409 Fork 116

lianjiatech / retrofit-spring-boot-starter

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
Apache-2.0

retrofit-spring-boot-starter

License Build Status Maven central GitHub release License License Author QQ-Group

English Document

适用于retrofit的spring-boot-starter,支持快速集成和功能增强

  1. Spring Boot 3.x 项目,请使用retrofit-spring-boot-starter 3.x
  2. Spring Boot 1.x/2.x 项目,请使用retrofit-spring-boot-starter 2.x

🚀项目持续优化迭代,欢迎大家提ISSUE和PR!麻烦大家能给一颗star✨,您的star是我们持续更新的动力!

github项目地址:https://github.com/LianjiaTech/retrofit-spring-boot-starter

gitee项目地址:https://gitee.com/lianjiatech/retrofit-spring-boot-starter

示例demo:https://github.com/ismart-yuxi/retrofit-spring-boot-demo

感谢@ismart-yuxi为本项目写的示例demo

功能特性

快速开始

引入依赖

<dependency>
    <groupId>com.github.lianjiatech</groupId>
   <artifactId>retrofit-spring-boot-starter</artifactId>
   <version>3.0.3</version>
</dependency>

如果启动失败,大概率是依赖冲突,烦请引入或者排除相关依赖

定义HTTP接口

接口必须使用@RetrofitClient注解标记!HTTP相关注解可参考官方文档:retrofit官方文档

@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface UserService {

   /**
    * 根据id查询用户姓名
    */
   @POST("getName")
   String getName(@Query("id") Long id);
}

注意:方法请求路径慎用/开头。对于Retrofit而言,如果baseUrl=http://localhost:8080/api/test/,方法请求路径如果是person,则该方法完整的请求路径是:http://localhost:8080/api/test/person。而方法请求路径如果是/person,则该方法完整的请求路径是:http://localhost:8080/person

注入使用

将接口注入到其它Service中即可使用!

@Service
public class BusinessService {

    @Autowired
    private UserService userService;

    public void doBusiness() {
       // call userService
    }
}

默认情况下,自动使用SpringBoot扫描路径进行RetrofitClient注册。你也可以在配置类加上@RetrofitScan手工指定扫描路径。

HTTP请求相关注解

HTTP请求相关注解,全部使用了Retrofit原生注解,以下是一个简单说明:

注解分类 支持的注解
请求方式 @GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP
请求头 @Header @HeaderMap @Headers
Query参数 @Query @QueryMap @QueryName
path参数 @Path
form-encoded参数 @Field @FieldMap @FormUrlEncoded
请求体 @Body
文件上传 @Multipart @Part @PartMap
url参数 @Url

详细信息可参考官方文档:retrofit官方文档

配置属性

组件支持了多个可配置的属性,用来应对不同的业务场景,具体可支持的配置属性及默认值如下:

注意:应用只需要配置要更改的配置项!

retrofit:
   # 全局转换器工厂
   global-converter-factories:
      - com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
      - retrofit2.converter.jackson.JacksonConverterFactory
   # 全局调用适配器工厂(组件扩展的调用适配器工厂已经内置,这里请勿重复配置)
   global-call-adapter-factories:

   # 全局日志打印配置
   global-log:
      # 启用日志打印
      enable: true
      # 全局日志打印级别
      log-level: info
      # 全局日志打印策略
      log-strategy: basic
      # 是否聚合打印请求日志
      aggregate: true

   # 全局重试配置
   global-retry:
      # 是否启用全局重试
      enable: false
      # 全局重试间隔时间
      interval-ms: 100
      # 全局最大重试次数
      max-retries: 2
      # 全局重试规则
      retry-rules:
         - response_status_not_2xx
         - occur_io_exception

   # 全局超时时间配置
   global-timeout:
      # 全局读取超时时间
      read-timeout-ms: 10000
      # 全局写入超时时间
      write-timeout-ms: 10000
      # 全局连接超时时间
      connect-timeout-ms: 10000
      # 全局完整调用超时时间
      call-timeout-ms: 0

   # 熔断降级配置
   degrade:
      # 熔断降级类型。默认none,表示不启用熔断降级
      degrade-type: none
      # 全局sentinel降级配置
      global-sentinel-degrade:
         # 是否开启
         enable: false
         # 各降级策略对应的阈值。平均响应时间(ms),异常比例(0-1),异常数量(1-N)
         count: 1000
         # 熔断时长,单位为 s
         time-window: 5
         # 降级策略(0:平均响应时间;1:异常比例;2:异常数量)
         grade: 0

      # 全局resilience4j降级配置
      global-resilience4j-degrade:
         # 是否开启
         enable: false
         # 根据该名称从#{@link CircuitBreakerConfigRegistry}获取CircuitBreakerConfig,作为全局熔断配置
         circuit-breaker-config-name: defaultCircuitBreakerConfig
   # 自动设置PathMathInterceptor的scope为prototype
   auto-set-prototype-scope-for-path-math-interceptor: true

高级功能

超时时间配置

如果仅仅需要修改OkHttpClient的超时时间,可以通过@RetrofitClient相关字段修改,或者全局超时配置修改。

自定义OkHttpClient

如果需要修改OkHttpClient其它配置,可以通过自定义OkHttpClient来实现,步骤如下:

实现SourceOkHttpClientRegistrar接口,调用SourceOkHttpClientRegistry#register()方法注册OkHttpClient

@Component
public class CustomOkHttpClientRegistrar implements SourceOkHttpClientRegistrar {

   @Override
   public void register(SourceOkHttpClientRegistry registry) {
      // 注册customOkHttpClient,超时时间设置为1s
      registry.register("customOkHttpClient", new OkHttpClient.Builder()
              .connectTimeout(Duration.ofSeconds(1))
              .writeTimeout(Duration.ofSeconds(1))
              .readTimeout(Duration.ofSeconds(1))
              .addInterceptor(chain -> chain.proceed(chain.request()))
              .build());
   }
}

通过@RetrofitClient.sourceOkHttpClient指定当前接口要使用的OkHttpClient

@RetrofitClient(baseUrl = "${test.baseUrl}", sourceOkHttpClient = "customOkHttpClient")
public interface CustomOkHttpUserService {

   /**
    * 根据id查询用户信息
    */
   @GET("getUser")
   User getUser(@Query("id") Long id);
}

注意:组件不会直接使用指定的OkHttpClient,而是基于该OkHttpClient创建一个新的。

注解式拦截器

组件提供了注解式拦截器,支持基于url路径匹配拦截,使用的步骤如下:

  1. 继承BasePathMatchInterceptor
  2. 使用@Intercept注解指定要使用的拦截器

如果需要使用多个拦截器,在接口上标注多个@Intercept注解即可。

继承BasePathMatchInterceptor编写拦截处理器

@Component
public class PathMatchInterceptor extends BasePathMatchInterceptor {
   @Override
   protected Response doIntercept(Chain chain) throws IOException {
      Response response = chain.proceed(chain.request());
      // response的Header加上path.match
      return response.newBuilder().header("path.match", "true").build();
   }
}

默认情况下,组件会自动将BasePathMatchInterceptorscope设置为prototype。 可通过retrofit.auto-set-prototype-scope-for-path-math-interceptor=false关闭该功能。关闭之后,需要手动将scope设置为prototype

@Component
@Scope("prototype")
public class PathMatchInterceptor extends BasePathMatchInterceptor {
    
}

接口上使用@Intercept进行标注

@RetrofitClient(baseUrl = "${test.baseUrl}")
@Intercept(handler = PathMatchInterceptor.class, include = {"/api/user/**"}, exclude = "/api/user/getUser")
// @Intercept() 如果需要使用多个路径匹配拦截器,继续添加@Intercept即可
public interface InterceptorUserService {

   /**
    * 根据id查询用户姓名
    */
   @POST("getName")
   Response<String> getName(@Query("id") Long id);

   /**
    * 根据id查询用户信息
    */
   @GET("getUser")
   Response<User> getUser(@Query("id") Long id);

}

上面的@Intercept配置表示:拦截InterceptorUserService接口下/api/user/**路径下(排除/api/user/getUser)的请求,拦截处理器使用PathMatchInterceptor

自定义拦截注解

有的时候,我们需要在"拦截注解"动态传入一些参数,然后在拦截的时候使用这些参数。 这时候,我们可以使用"自定义拦截注解",步骤如下:

  1. 自定义注解。必须使用@InterceptMark标记,并且注解中必须包括include、exclude、handler字段。
  2. 继承BasePathMatchInterceptor编写拦截处理器
  3. 接口上使用自定义注解

例如,我们需要"在请求头里面动态加入accessKeyIdaccessKeySecret签名信息才能再发起HTTP请求",这时候可以自定义@Sign注解来实现。

自定义@Sign注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public @interface Sign {
    
    String accessKeyId();

    String accessKeySecret();

    String[] include() default {"/**"};

    String[] exclude() default {};

    Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}

@Sign注解中指定了使用的拦截器是SignInterceptor

实现SignInterceptor

@Component
@Setter
public class SignInterceptor extends BasePathMatchInterceptor {

   private String accessKeyId;

   private String accessKeySecret;

   @Override
   public Response doIntercept(Chain chain) throws IOException {
      Request request = chain.request();
      Request newReq = request.newBuilder()
              .addHeader("accessKeyId", accessKeyId)
              .addHeader("accessKeySecret", accessKeySecret)
              .build();
      Response response = chain.proceed(newReq);
      return response.newBuilder().addHeader("accessKeyId", accessKeyId)
              .addHeader("accessKeySecret", accessKeySecret).build();
   }
}

注意:accessKeyIdaccessKeySecret字段必须提供setter方法。

拦截器的accessKeyIdaccessKeySecret字段值会依据@Sign注解的accessKeyId()accessKeySecret()值自动注入,如果@Sign指定的是占位符形式的字符串,则会取配置属性值进行注入。

接口上使用@Sign

@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", include = "/api/user/getAll")
public interface InterceptorUserService {

   /**
    * 查询所有用户信息
    */
   @GET("getAll")
   Response<List<User>> getAll();

}

日志打印

组件支持支持全局日志打印和声明式日志打印。

全局日志打印

默认情况下,全局日志打印是开启的,默认配置如下:

retrofit:
   # 全局日志打印配置
   global-log:
      # 启用日志打印
      enable: true
      # 全局日志打印级别
      log-level: info
      # 全局日志打印策略
      log-strategy: basic
      # 是否聚合打印请求日志
      aggregate: true

四种日志打印策略含义如下:

  1. NONE:No logs.
  2. BASIC:Logs request and response lines.
  3. HEADERS:Logs request and response lines and their respective headers.
  4. BODY:Logs request and response lines and their respective headers and bodies (if present).

声明式日志打印

如果只需要部分请求才打印日志,可以在相关接口或者方法上使用@Logging注解。

日志打印自定义扩展

如果需要修改日志打印行为,可以继承LoggingInterceptor,并将其配置成Spring bean

请求重试

组件支持支持全局重试和声明式重试。

全局重试

全局重试默认关闭,默认配置项如下:

retrofit:
  # 全局重试配置
  global-retry:
     # 是否启用全局重试
     enable: false
     # 全局重试间隔时间
     interval-ms: 100
     # 全局最大重试次数
     max-retries: 2
     # 全局重试规则
     retry-rules:
        - response_status_not_2xx
        - occur_io_exception

重试规则支持三种配置:

  1. RESPONSE_STATUS_NOT_2XX:响应状态码不是2xx时执行重试
  2. OCCUR_IO_EXCEPTION:发生IO异常时执行重试
  3. OCCUR_EXCEPTION:发生任意异常时执行重试

声明式重试

如果只有一部分请求需要重试,可以在相应的接口或者方法上使用@Retry注解。

请求重试自定义扩展

如果需要修改请求重试行为,可以继承RetryInterceptor,并将其配置成Spring bean

熔断降级

熔断降级默认关闭,当前支持sentinelresilience4j两种实现。

retrofit:
   # 熔断降级配置
   degrade:
      # 熔断降级类型。默认none,表示不启用熔断降级
      degrade-type: sentinel

Sentinel

配置degrade-type=sentinel开启,然后在相关接口或者方法上声明@SentinelDegrade注解即可。

记得手动引入Sentinel依赖:


<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>sentinel-core</artifactId>
   <version>1.6.3</version>
</dependency>

此外,还支持全局Sentinel熔断降级:

retrofit:
  # 熔断降级配置
  degrade:
    # 熔断降级类型。默认none,表示不启用熔断降级
    degrade-type: sentinel
    # 全局sentinel降级配置
    global-sentinel-degrade:
      # 是否开启
      enable: true
      # ...其他sentinel全局配置

Resilience4j

配置degrade-type=resilience4j开启。然后在相关接口或者方法上声明@Resilience4jDegrade即可。

记得手动引入Resilience4j依赖:


<dependency>
   <groupId>io.github.resilience4j</groupId>
   <artifactId>resilience4j-circuitbreaker</artifactId>
   <version>1.7.1</version>
</dependency>

通过以下配置可开启全局resilience4j熔断降级:

retrofit:
   # 熔断降级配置
   degrade:
      # 熔断降级类型。默认none,表示不启用熔断降级
      degrade-type: resilience4j
      # 全局resilience4j降级配置
      global-resilience4j-degrade:
         # 是否开启
         enable: true
         # 根据该名称从#{@link CircuitBreakerConfigRegistry}获取CircuitBreakerConfig,作为全局熔断配置
         circuit-breaker-config-name: defaultCircuitBreakerConfig

熔断配置管理:

实现CircuitBreakerConfigRegistrar接口,注册CircuitBreakerConfig

@Component
public class CustomCircuitBreakerConfigRegistrar implements CircuitBreakerConfigRegistrar {
   @Override
   public void register(CircuitBreakerConfigRegistry registry) {
   
         // 替换默认的CircuitBreakerConfig
         registry.register(Constants.DEFAULT_CIRCUIT_BREAKER_CONFIG, CircuitBreakerConfig.ofDefaults());
   
         // 注册其它的CircuitBreakerConfig
         registry.register("testCircuitBreakerConfig", CircuitBreakerConfig.custom()
                 .slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
                 .failureRateThreshold(20)
                 .minimumNumberOfCalls(5)
                 .permittedNumberOfCallsInHalfOpenState(5)
                 .build());
   }
}

通过circuitBreakerConfigName指定CircuitBreakerConfig。包括retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name或者@Resilience4jDegrade.circuitBreakerConfigName

扩展熔断降级

如果用户需要使用其他的熔断降级实现,继承BaseRetrofitDegrade,并将其配置Spring Bean

配置fallback或者fallbackFactory (可选)

如果@RetrofitClient不设置fallback或者fallbackFactory,当触发熔断时,会直接抛出RetrofitBlockException异常。 用户可以通过设置fallback或者fallbackFactory来定制熔断时的方法返回值。

注意:fallback类必须是当前接口的实现类,fallbackFactory必须是FallbackFactory<T> 实现类,泛型参数类型为当前接口类型。另外,fallbackfallbackFactory实例必须配置成Spring Bean

fallbackFactory相对于fallback,主要差别在于能够感知每次熔断的异常原因(cause),参考示例如下:

@Slf4j
@Service
public class HttpDegradeFallback implements HttpDegradeApi {

   @Override
   public Result<Integer> test() {
      Result<Integer> fallback = new Result<>();
      fallback.setCode(100)
              .setMsg("fallback")
              .setBody(1000000);
      return fallback;
   }
}
@Slf4j
@Service
public class HttpDegradeFallbackFactory implements FallbackFactory<HttpDegradeApi> {

   @Override
   public HttpDegradeApi create(Throwable cause) {
      log.error("触发熔断了! ", cause.getMessage(), cause);
      return new HttpDegradeApi() {
         @Override
         public Result<Integer> test() {
            Result<Integer> fallback = new Result<>();
            fallback.setCode(100)
                    .setMsg("fallback")
                    .setBody(1000000);
            return fallback;
         }
      };
   }
}

错误解码器

HTTP发生请求错误(包括发生异常或者响应数据不符合预期)的时候,错误解码器可将HTTP相关信息解码到自定义异常中。你可以在@RetrofitClient注解的errorDecoder() 指定当前接口的错误解码器,自定义错误解码器需要实现ErrorDecoder接口:

微服务之间的HTTP调用

继承ServiceInstanceChooser

用户可以自行实现ServiceInstanceChooser接口,完成服务实例的选取逻辑,并将其配置成Spring Bean。对于Spring Cloud 应用,可以使用如下实现。

@Service
public class SpringCloudServiceInstanceChooser implements ServiceInstanceChooser {
    
   private LoadBalancerClient loadBalancerClient;

   @Autowired
   public SpringCloudServiceInstanceChooser(LoadBalancerClient loadBalancerClient) {
      this.loadBalancerClient = loadBalancerClient;
   }

   /**
    * Chooses a ServiceInstance URI from the LoadBalancer for the specified service.
    *
    * @param serviceId The service ID to look up the LoadBalancer.
    * @return Return the uri of ServiceInstance
    */
   @Override
   public URI choose(String serviceId) {
      ServiceInstance serviceInstance = loadBalancerClient.choose(serviceId);
      Assert.notNull(serviceInstance, "can not found service instance! serviceId=" + serviceId);
      return serviceInstance.getUri();
   }
}

指定serviceIdpath

@RetrofitClient(serviceId = "user", path = "/api/user")
public interface ChooserOkHttpUserService {

   /**
    * 根据id查询用户信息
    */
   @GET("getUser")
   User getUser(@Query("id") Long id);
}

全局拦截器

全局应用拦截器

如果我们需要对整个系统的的HTTP请求执行统一的拦截处理,可以实现全局拦截器GlobalInterceptor, 并配置成spring Bean

@Component
public class MyGlobalInterceptor implements GlobalInterceptor {
   @Override
   public Response intercept(Chain chain) throws IOException {
      Response response = chain.proceed(chain.request());
      // response的Header加上global
      return response.newBuilder().header("global", "true").build();
   }
}

全局网络拦截器

实现NetworkInterceptor接口,并配置成spring Bean

调用适配器

Retrofit可以通过CallAdapterFactoryCall<T>对象适配成接口方法的返回值类型。组件扩展了一些CallAdapterFactory实现:

  1. BodyCallAdapterFactory
    • 同步执行HTTP请求,将响应体内容适配成方法的返回值类型。
    • 任意方法返回值类型都可以使用BodyCallAdapterFactory,优先级最低。
  2. ResponseCallAdapterFactory
    • 同步执行HTTP请求,将响应体内容适配成Retrofit.Response<T>返回。
    • 只有方法返回值类型为Retrofit.Response<T>,才可以使用ResponseCallAdapterFactory
  3. 响应式编程相关CallAdapterFactory

Retrofit会根据方法返回值类型选择对应的CallAdapterFactory执行适配处理,目前支持的返回值类型如下:

  • String:将Response Body适配成String返回。
  • 基础类型(Long/Integer/Boolean/Float/Double):将Response Body适配成上述基础类型
  • 任意Java类型: 将Response Body适配成对应的Java对象返回
  • CompletableFuture<T>: 将Response Body适配成CompletableFuture<T>对象返回
  • Void: 不关注返回类型可以使用Void
  • Response<T>: 将Response适配成Response<T>对象返回
  • Call<T>: 不执行适配处理,直接返回Call<T>对象
  • Mono<T>: Project Reactor响应式返回类型
  • Single<T>Rxjava响应式返回类型(支持Rxjava2/Rxjava3
  • CompletableRxjava响应式返回类型,HTTP请求没有响应体(支持Rxjava2/Rxjava3

可以通过继承CallAdapter.Factory扩展CallAdapter

组件支持通过retrofit.global-call-adapter-factories配置全局调用适配器工厂:

retrofit:
  # 全局转换器工厂(组件扩展的`CallAdaptorFactory`工厂已经内置,这里请勿重复配置)
  global-call-adapter-factories:
    # ...

针对每个Java接口,还可以通过@RetrofitClient.callAdapterFactories指定当前接口采用的CallAdapter.Factory

建议:将CallAdapter.Factory配置成Spring Bean

数据转码器

Retrofit使用Converter@Body注解的对象转换成Request Body,将Response Body转换成一个Java对象,可以选用以下几种Converter

  • Gson: com.squareup.Retrofit:converter-gson
  • Jackson: com.squareup.Retrofit:converter-jackson
  • Moshi: com.squareup.Retrofit:converter-moshi
  • Protobuf: com.squareup.Retrofit:converter-protobuf
  • Wire: com.squareup.Retrofit:converter-wire
  • Simple XML: com.squareup.Retrofit:converter-simplexml
  • JAXB: com.squareup.retrofit2:converter-jaxb
  • fastJson:com.alibaba.fastjson.support.retrofit.Retrofit2ConverterFactory

组件支持通过retrofit.global-converter-factories配置全局Converter.Factory,默认的是retrofit2.converter.jackson.JacksonConverterFactory

如果需要修改Jackson配置,自行覆盖JacksonConverterFactorybean配置即可。

retrofit:
   # 全局转换器工厂
   global-converter-factories:
      - com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
      - retrofit2.converter.jackson.JacksonConverterFactory

针对每个Java接口,还可以通过@RetrofitClient.converterFactories指定当前接口采用的Converter.Factory

建议:将Converter.Factory配置成Spring Bean

元注解

@RetrofitClient@Retry@Logging@Resilience4jDegrade等注解支持元注解、继承以及@AliasFor


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Logging(logLevel = LogLevel.WARN)
@Retry(intervalMs = 200)
public @interface MyRetrofitClient {

   @AliasFor(annotation = RetrofitClient.class, attribute = "converterFactories")
   Class<? extends Converter.Factory>[] converterFactories() default {GsonConverterFactory.class};

   @AliasFor(annotation = Logging.class, attribute = "logStrategy")
   LogStrategy logStrategy() default LogStrategy.BODY;
}

其他功能示例

form参数

@FormUrlEncoded
@POST("token/verify")
Object tokenVerify(@Field("source") String source,@Field("signature") String signature,@Field("token") String token);


@FormUrlEncoded
@POST("message")
CompletableFuture<Object> sendMessage(@FieldMap Map<String, Object> param);

文件上传

创建MultipartBody.Part

// 对文件名使用URLEncoder进行编码
public ResponseEntity importTerminology(MultipartFile file){
     String fileName=URLEncoder.encode(Objects.requireNonNull(file.getOriginalFilename()),"utf-8");
     okhttp3.RequestBody requestBody=okhttp3.RequestBody.create(MediaType.parse("multipart/form-data"),file.getBytes());
     MultipartBody.Part part=MultipartBody.Part.createFormData("file",fileName,requestBody);
     apiService.upload(part);
     return ok().build();
}

HTTP上传接口

@POST("upload")
@Multipart
Void upload(@Part MultipartBody.Part file);

文件下载

HTTP下载接口

@RetrofitClient(baseUrl = "https://img.ljcdn.com/hc-picture/")
public interface DownloadApi {

    @GET("{fileKey}")
    Response<ResponseBody> download(@Path("fileKey") String fileKey);
}

HTTP下载使用

@SpringBootTest(classes = {RetrofitBootApplication.class})
@RunWith(SpringRunner.class)
public class DownloadTest {
    @Autowired
    DownloadApi downLoadApi;

    @Test
    public void download() throws Exception {
        String fileKey = "6302d742-ebc8-4649-95cf-62ccf57a1add";
        Response<ResponseBody> response = downLoadApi.download(fileKey);
        ResponseBody responseBody = response.body();
        // 二进制流
        InputStream is = responseBody.byteStream();

        // 具体如何处理二进制流,由业务自行控制。这里以写入文件为例
        File tempDirectory = new File("temp");
        if (!tempDirectory.exists()) {
            tempDirectory.mkdir();
        }
        File file = new File(tempDirectory, UUID.randomUUID().toString());
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(file);
        byte[] b = new byte[1024];
        int length;
        while ((length = is.read(b)) > 0) {
            fos.write(b, 0, length);
        }
        is.close();
        fos.close();
    }
}

动态URL

使用@url注解可实现动态URL。此时,baseUrl配置任意合法url即可。例如: http://github.com/ 。运行时只会根据@Url地址发起请求。

注意:@url必须放在方法参数的第一个位置,另外,@GET@POST等注解上,不需要定义端点路径。

 @GET
 Map<String, Object> test3(@Url String url,@Query("name") String name);

DELETE请求添加请求体

@HTTP(method = "DELETE", path = "/user/delete", hasBody = true)

GET请求添加请求体

okhttp3自身不支持GET请求添加请求体,源码如下:

image

image

作者给出了具体原因,可以参考: issue

但是,如果实在需要这么做,可以使用:@HTTP(method = "get", path = "/user/get", hasBody = true),使用小写get绕过上述限制。

反馈建议

如有任何问题,欢迎提issue或者加QQ群反馈。

群号:806714302

QQ群图片

Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright(c) 2020 Lianjia, Inc. All Rights Reserved Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

简介

A spring-boot starter for retrofit, supports rapid integration and feature enhancements.(适用于retrofit的spring-boot启动器,支持快速集成和功能增强) 展开 收起
Java
Apache-2.0
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/lianjiatech/retrofit-spring-boot-starter.git
git@gitee.com:lianjiatech/retrofit-spring-boot-starter.git
lianjiatech
retrofit-spring-boot-starter
retrofit-spring-boot-starter
master

搜索帮助