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.
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><dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit‑spring‑boot‑starter</artifactId>
<version>3.0.2</version>
</dependency></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<Person> 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<Person> 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<Person> 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<T> , Void , Response<T> , Call<T> , Mono<T> , Single<T> , 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<Person> getPerson(@Query("id") Long id);
CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id);
Void savePersonVoid(@Body Person person);
Response<Result<Person>> getPersonResponse(@Query("id") Long id);
Call<Result<Person>> getPersonCall(@Query("id") Long id);
Mono<Result<Person>> monoPerson(@Query("id") Long id);
Single<Result<Person>> 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<ResponseBody> download(@Path("fileKey") String fileKey);
}
</code>Dynamic URL
<code>@GET
Map<String, Object> 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<ResponseBody> getWithBody(@Body RequestBody body);
</code>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
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.