Unlock Simple HTTP Calls in Spring Boot with retrofit-spring-boot-starter
This guide introduces retrofit-spring-boot-starter, a lightweight HTTP client for Spring Boot that simplifies request management, offers rich features such as custom OkHttp injection, annotation‑based interceptors, logging, retry, circuit‑breaker, and detailed configuration, with full code examples and usage patterns.
Introduction
Retrofit is a type‑safe HTTP client for Android and Java. Spring Boot does not provide native integration, so retrofit-spring-boot-starter was created to bridge the gap, offering a lightweight and feature‑rich HTTP client for Spring Boot projects.
Features
Custom OkHttpClient injection
Annotation‑based interceptors
Connection pool management
Logging with configurable levels and strategies
Request retry with customizable rules
Error decoder support
Global and network interceptors
Circuit‑breaker (Sentinel) with annotation‑based rules
Microservice HTTP calls via service discovery
Flexible call adapters and data converters
Quick Start
Add the starter as a Maven dependency:
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>Define a Retrofit interface and annotate it with @RetrofitClient:
@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}Inject the interface into a Spring service:
@Service
public class TestService {
@Autowired
private HttpApi httpApi;
public void test() {
// call httpApi methods
}
}HTTP Annotations
The starter uses the native Retrofit annotations such as @GET, @POST, @PUT, @DELETE, @Header, @Query, @Path, @Body, etc. Refer to the official Retrofit documentation for details.
Annotation‑Based Interceptors
Implement a class extending BasePathMatchInterceptor and annotate the Retrofit interface with @Intercept to apply the interceptor to specific URL patterns.
@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);
}
} @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);
@POST("savePerson")
Result<Person> savePerson(@Body Person person);
}Custom Intercept Annotations
Define a custom annotation marked with @InterceptMark (e.g., @Sign) and implement a corresponding interceptor to add dynamic headers such as access keys.
@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;
} @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);
}
} @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);
@POST("savePerson")
Result<Person> savePerson(@Body Person person);
}Connection Pool Management
Define multiple connection pools in application.yml and select a pool per interface using the poolName attribute.
retrofit:
pool:
test1:
max-idle-connections: 3
keep-alive-second: 100
test2:
max-idle-connections: 5
keep-alive-second: 50 @RetrofitClient(baseUrl = "${test.baseUrl}", poolName = "test1")
public interface HttpApi { ... }Logging
Global logging can be toggled with retrofit.enableLog. Per‑interface logging is controlled by enableLog, logLevel (ERROR, WARN, INFO, DEBUG, TRACE) and logStrategy (NONE, BASIC, HEADERS, BODY). The default interceptor is DefaultLoggingInterceptor based on OkHttp’s HttpLoggingInterceptor.
retrofit:
logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptorRetry
Apply @Retry on an interface or method to enable automatic retries. Configurable parameters include maxRetries, intervalMs, and retryRules (e.g., RESPONSE_STATUS_NOT_2XX, OCCUR_IO_EXCEPTION, OCCUR_EXCEPTION).
retrofit:
retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptorError Decoder
Implement ErrorDecoder and reference it via the errorDecoder() attribute of @RetrofitClient to translate HTTP errors into custom exceptions.
public interface ErrorDecoder {
default RuntimeException invalidRespDecode(Request request, Response response) { ... }
default RuntimeException ioExceptionDecode(Request request, IOException cause) { ... }
default RuntimeException exceptionDecode(Request request, Exception cause) { ... }
}Global Interceptors
Define beans extending BaseGlobalInterceptor or implementing NetworkInterceptor to apply logic to every HTTP request, such as adding a source header.
@Component
public class SourceInterceptor extends BaseGlobalInterceptor {
@Override
public Response doIntercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder().addHeader("source", "test").build();
return chain.proceed(newReq);
}
}Circuit Breaker
Enable circuit breaking with Sentinel by setting:
retrofit:
enable-degrade: true
degrade-type: sentinel
resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParserConfigure rules with @Degrade (count, timeWindow, strategy) on interfaces or methods, and optionally provide fallback or fallbackFactory beans for graceful degradation.
Microservice Calls
Integrate with service discovery by providing a ServiceInstanceChooser bean (e.g., Spring Cloud’s SpringCloudServiceInstanceChooser) and using @RetrofitClient(serviceId = "${service.id}", path = "/api") to call other services.
@Bean
public ServiceInstanceChooser serviceInstanceChooser(LoadBalancerClient loadBalancerClient) {
return new SpringCloudServiceInstanceChooser(loadBalancerClient);
}Call Adapters and Converters
The starter registers two call‑adapter factories by default: BodyCallAdapterFactory (maps response bodies to method return types) and ResponseCallAdapterFactory (returns Response<T>). These can be disabled via retrofit.enable-body-call-adapter or retrofit.enable-response-call-adapter. Global factories are configurable:
retrofit:
global-call-adapter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactoryData conversion uses Converter.Factory implementations (Jackson, Gson, Moshi, etc.). The default is Jackson, configurable globally:
retrofit:
global-converter-factories:
- retrofit2.converter.jackson.JacksonConverterFactoryInterface‑level converters can be set with the converterFactories() attribute.
Conclusion
retrofit-spring-boot-starterprovides a production‑ready, lightweight HTTP client for Spring Boot, covering request management, extensible interceptors, logging, retry, circuit breaking, and seamless microservice integration. It has been stable in production for over a year and is ready for adoption in new projects.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
