Backend Development 23 min read

Boost Your Spring Boot HTTP Calls with retrofit‑spring‑boot‑starter – A Complete Guide

This article introduces the lightweight retrofit‑spring‑boot‑starter for Spring Boot, explains its key features such as custom OkHttpClient, annotation‑based interceptors, logging, retry, circuit‑breaker support, and shows step‑by‑step configuration, dependency setup, and code examples for building robust HTTP client services.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Boost Your Spring Boot HTTP Calls with retrofit‑spring‑boot‑starter – A Complete Guide

Overview

In Spring Boot projects, using OkHttp, HttpClient, or RestTemplate for HTTP requests can be cumbersome and hard to manage uniformly. The retrofit‑spring‑boot‑starter provides a lightweight, feature‑rich HTTP client framework that integrates seamlessly with Spring Boot.

Key Features

Custom OkHttpClient

Annotation‑based interceptors

Logging

Retry

Circuit‑breaker (Sentinel, Resilience4j)

Error decoder

Microservice HTTP calls

Global interceptors

Call adapters

Data converters

Meta‑annotations

Other examples

Quick Start

Add Dependency

<code>&lt;dependency&gt;
  &lt;groupId&gt;com.github.lianjiatech&lt;/groupId&gt;
  &lt;artifactId&gt;retrofit‑spring‑boot‑starter&lt;/artifactId&gt;
  &lt;version&gt;3.0.2&lt;/version&gt;
&lt;/dependency&gt;</code>

If the application fails to start, it is likely due to dependency conflicts; exclude or include the relevant dependencies.

Define HTTP Interface

The interface must be annotated with @RetrofitClient :

<code>@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
    @GET("person")
    Result&lt;Person&gt; getPerson(@Query("id") Long id);
}</code>

Note: avoid starting the method path with a leading / when a base URL ends with a slash, otherwise the final URL may be incorrect.

Inject and Use

<code>@Service
public class TestService {
    @Autowired
    private HttpApi httpApi;
    public void test() {
        // use httpApi to make HTTP requests
    }
}</code>

By default, Spring Boot scans for @RetrofitClient annotations; you can also specify the scan path with @RetrofitScan .

HTTP Request Annotations

Retrofit’s native annotations are supported. Common categories include:

Request methods: @GET , @POST , @PUT , @DELETE , @HEAD , @OPTIONS , @HTTP

Headers: @Header , @HeaderMap , @Headers

Query parameters: @Query , @QueryMap , @QueryName

Path parameters: @Path

Form‑encoded: @Field , @FieldMap , @FormUrlEncoded

Request body: @Body

File upload: @Multipart , @Part , @PartMap

Dynamic URL: @Url

Advanced Features

Timeout Configuration

Modify OkHttpClient timeout via @RetrofitClient fields or global timeout settings.

Custom OkHttpClient

<code>@Component
public class CustomSourceOkHttpClientRegistrar implements SourceOkHttpClientRegistrar {
    @Override
    public void register(SourceOkHttpClientRegistry registry) {
        registry.register("testSourceOkHttpClient", new OkHttpClient.Builder()
            .connectTimeout(Duration.ofSeconds(3))
            .writeTimeout(Duration.ofSeconds(3))
            .readTimeout(Duration.ofSeconds(3))
            .addInterceptor(chain -> {
                log.info("============use testSourceOkHttpClient=============");
                return chain.proceed(chain.request());
            })
            .build());
    }
}</code>

Use it with @RetrofitClient(sourceOkHttpClient = "testSourceOkHttpClient") .

Annotation‑Based Interceptor

Implement BasePathMatchInterceptor and annotate the interface with @Intercept :

<code>@Component
public class TimeStampInterceptor extends BasePathMatchInterceptor {
    @Override
    public Response doIntercept(Chain chain) throws IOException {
        Request request = chain.request();
        HttpUrl url = request.url();
        long timestamp = System.currentTimeMillis();
        HttpUrl newUrl = url.newBuilder()
            .addQueryParameter("timestamp", String.valueOf(timestamp))
            .build();
        Request newRequest = request.newBuilder().url(newUrl).build();
        return chain.proceed(newRequest);
    }
}</code>
<code>@RetrofitClient(baseUrl = "${test.baseUrl}")
@Intercept(handler = TimeStampInterceptor.class, include = {"/api/**"}, exclude = "/api/test/savePerson")
public interface HttpApi {
    @GET("person")
    Result&lt;Person&gt; getPerson(@Query("id") Long id);
}</code>

Custom Intercept Annotation

<code>@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;
}</code>
<code>@Component
public class SignInterceptor extends BasePathMatchInterceptor {
    private String accessKeyId;
    private String accessKeySecret;
    public void setAccessKeyId(String accessKeyId) { this.accessKeyId = accessKeyId; }
    public void setAccessKeySecret(String accessKeySecret) { this.accessKeySecret = accessKeySecret; }
    @Override
    public Response doIntercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request newReq = request.newBuilder()
            .addHeader("accessKeyId", accessKeyId)
            .addHeader("accessKeySecret", accessKeySecret)
            .build();
        return chain.proceed(newReq);
    }
}</code>
<code>@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", exclude = {"/api/test/person"})
public interface HttpApi {
    @GET("person")
    Result&lt;Person&gt; getPerson(@Query("id") Long id);
}</code>

Logging

Global logging is enabled by default (level: info, strategy: basic). Four strategies are available: NONE, BASIC, HEADERS, BODY. Use @Logging on specific methods for declarative logging.

Retry

Global retry is disabled by default. Configuration includes enable flag, interval, max retries, and rules (e.g., response status not 2xx, IO exception). Use @Retry on specific methods for declarative retry.

Circuit‑Breaker (Degrade)

Degrade type can be sentinel or resilience4j . Annotate interfaces or methods with @SentinelDegrade or @Resilience4jDegrade . Fallback classes must implement the target interface or FallbackFactory .

Error Decoder

Specify a custom error decoder via @RetrofitClient(errorDecoder = MyErrorDecoder.class) to translate HTTP errors into custom exceptions.

Microservice Calls

Implement ServiceInstanceChooser to select service instances. Example for Spring Cloud:

<code>@Service
public class SpringCloudServiceInstanceChooser implements ServiceInstanceChooser {
    @Autowired
    private LoadBalancerClient loadBalancerClient;
    @Override
    public URI choose(String serviceId) {
        ServiceInstance instance = loadBalancerClient.choose(serviceId);
        Assert.notNull(instance, "can not found service instance! serviceId=" + serviceId);
        return instance.getUri();
    }
}</code>
<code>@RetrofitClient(serviceId = "${jy-helicarrier-api.serviceId}", path = "/m/count")
public interface ApiCountService {}</code>

Global Interceptor

<code>@Component
public class SourceGlobalInterceptor implements GlobalInterceptor {
    @Autowired
    private TestService testService;
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request newReq = request.newBuilder()
            .addHeader("source", "test")
            .build();
        testService.test();
        return chain.proceed(newReq);
    }
}</code>

Call Adapter

Supported return types include String , primitive types, any Java object, CompletableFuture&lt;T&gt; , Void , Response&lt;T&gt; , Call&lt;T&gt; , Mono&lt;T&gt; , Single&lt;T&gt; , and Completable . Custom adapters can be added via retrofit.global-call-adapter-factories or @RetrofitClient.callAdapterFactories .

<code>@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
    String getString(@Body Person person);
    Result&lt;Person&gt; getPerson(@Query("id") Long id);
    CompletableFuture&lt;Result&lt;Person&gt;&gt; getPersonCompletableFuture(@Query("id") Long id);
    Void savePersonVoid(@Body Person person);
    Response&lt;Result&lt;Person&gt;&gt; getPersonResponse(@Query("id") Long id);
    Call&lt;Result&lt;Person&gt;&gt; getPersonCall(@Query("id") Long id);
    Mono&lt;Result&lt;Person&gt;&gt; monoPerson(@Query("id") Long id);
    Single&lt;Result&lt;Person&gt;&gt; singlePerson(@Query("id") Long id);
    Completable ping();
}</code>

Data Converter

Retrofit uses Converter.Factory to serialize request bodies and deserialize responses. Supported factories include Gson, Jackson, Moshi, Protobuf, Wire, Simple XML, JAXB, and FastJSON. Global converters are configured via retrofit.global-converter-factories ; per‑interface converters via @RetrofitClient.converterFactories .

<code>retrofit:
  global-converter-factories:
    - com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
    - retrofit2.converter.jackson.JacksonConverterFactory</code>

File Upload & Download

<code>// Build MultipartBody.Part
MultipartBody.Part part = MultipartBody.Part.createFormData(
    "file",
    URLEncoder.encode(file.getOriginalFilename(), "utf-8"),
    RequestBody.create(MediaType.parse("multipart/form-data"), file.getBytes())
);
apiService.upload(part);
</code>
<code>@POST("upload")
@Multipart
Void upload(@Part MultipartBody.Part file);
</code>
<code>@RetrofitClient(baseUrl = "https://example.com/files/")
public interface DownloadApi {
    @GET("{fileKey}")
    Response&lt;ResponseBody&gt; download(@Path("fileKey") String fileKey);
}
</code>

Dynamic URL

<code>@GET
Map&lt;String, Object&gt; test3(@Url String url, @Query("name") String name);
</code>

Note: @Url must be the first method parameter and the HTTP method annotation should not specify a path.

GET/DELETE with Body

OkHttp does not support a request body for GET . To send a body, use the generic @HTTP annotation with method="GET" (lower‑case) and hasBody=true .

<code>@HTTP(method = "GET", path = "/user/get", hasBody = true)
Call&lt;ResponseBody&gt; getWithBody(@Body RequestBody body);
</code>
JavaMicroservicesSpring BootInterceptorRetrofitHTTP Client
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.