Simplify Spring Boot HTTP Calls with retrofit-spring-boot-starter: Features, Usage, and Advanced Configuration
This article introduces retrofit-spring-boot-starter, a lightweight HTTP client framework for Spring Boot that integrates Retrofit, detailing its core features, quick start guide, custom interceptors, connection pool management, logging, retry mechanisms, error decoding, global interceptors, circuit breaking, microservice calls, call adapters, data converters, and best practices for production use.
Hello, I am Peng Lei.
2025 Java Architect: Video Course
In a SpringBoot project, using okhttp, httpClient, or RestTemplate to send HTTP requests is cumbersome and hard to manage uniformly. This article recommends the lightweight HTTP client framework retrofit-spring-boot-starter for SpringBoot projects, which is simple to use and provides many enhancements. The current version is 3.1.5 and will continue to be iterated.
GitHub: https://github.com/LianjiaTech/retrofit-spring-boot-starter
Preface
Retrofitis a type‑safe HTTP client for Android and Java that supports interface‑based requests. However, Retrofit is not officially supported for quick integration with SpringBoot, so retrofit-spring-boot-starter was developed to bridge the two.
retrofit-spring-boot-starter integrates Retrofit with SpringBoot and adds many functional enhancements, greatly simplifying development.
🚀 The project is continuously optimized. Please star the repository and submit issues or PRs!
Features
Custom OkHttpClient injection
Annotation‑based interceptors
Connection pool management
Log printing
Request retry
Error decoder
Global interceptors
Circuit breaking
HTTP calls between microservices
Call adapters
Data converters
Quick Start
Add Dependency
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>Define HTTP Interface
The interface must be annotated with @RetrofitClient .
@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}Inject and Use
@Service
public class TestService {
@Autowired
private HttpApi httpApi;
public void test() {
// invoke HTTP request via httpApi
}
}HTTP Request Annotations
Supported annotations include @GET, @POST, @PUT, @DELETE, @HEAD, @OPTIONS, @Header, @HeaderMap, @Headers, @Query, @QueryMap, @QueryName, @Path, @Field, @FieldMap, @FormUrlEncoded, @Multipart, @Part, @PartMap, @Url.
Configuration Items
retrofit:
enable-response-call-adapter: true
enable-log: true
pool:
test1:
max-idle-connections: 3
keep-alive-second: 100
test2:
max-idle-connections: 5
keep-alive-second: 50
disable-void-return-type: false
logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptor
retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptor
global-converter-factories:
- retrofit2.converter.jackson.JacksonConverterFactory
global-call-adapter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactory
enable-degrade: true
degrade-type: sentinel
resource-name-parser: com.github.lianjiatech.retrofit.spring.boot.degrade.DefaultResourceNameParserCustom OkHttpClient Injection
You can define a static method returning OkHttpClient.Builder in the interface to customize the client.
@RetrofitClient(baseUrl = "http://ke.com")
public interface HttpApi3 {
@OkHttpClientBuilder
static OkHttpClient.Builder okhttpClientBuilder() {
return new OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS);
}
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}Annotation‑Based Interceptor
Implement BasePathMatchInterceptor and annotate the interface with @Intercept to apply URL‑based interception.
@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 Annotation
Create a new annotation marked with @InterceptMark that includes include(), exclude(), and handler() attributes, then implement the corresponding interceptor.
@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 the configuration and select a pool for a specific interface via 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 {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}Log Printing
Global log control via retrofit.enableLog. Per‑interface control with enableLog, logLevel, and logStrategy. Supported levels: ERROR, WARN, INFO, DEBUG, TRACE (default INFO). Strategies: NONE, BASIC, HEADERS, BODY (default BASIC).
retrofit:
logging-interceptor: com.github.lianjiatech.retrofit.spring.boot.interceptor.DefaultLoggingInterceptorRequest Retry
Annotate methods or interfaces with @Retry and configure maxRetries, intervalMs, and retryRules (RESPONSE_STATUS_NOT_2XX, OCCUR_IO_EXCEPTION, OCCUR_EXCEPTION).
retrofit:
retry-interceptor: com.github.lianjiatech.retrofit.spring.boot.retry.DefaultRetryInterceptorError Decoder
Implement ErrorDecoder to convert HTTP errors or exceptions into custom runtime exceptions. Specify the decoder with errorDecoder() in @RetrofitClient.
public interface ErrorDecoder {
default RuntimeException invalidRespDecode(Request request, Response response) {
if (!response.isSuccessful()) {
throw RetrofitException.errorStatus(request, response);
}
return null;
}
default RuntimeException ioExceptionDecode(Request request, IOException cause) {
return RetrofitException.errorExecuting(request, cause);
}
default RuntimeException exceptionDecode(Request request, Exception cause) {
return RetrofitException.errorUnknown(request, cause);
}
}Global Interceptor
Application‑Level Interceptor
@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);
}
}Network Interceptor
Implement NetworkInterceptor and register it as a Spring bean to be woven into the OkHttp network chain.
Circuit Breaking
Enable circuit breaking with Sentinel by setting enable-degrade: true and degrade-type: sentinel. Define resource name parser if needed. Use @Degrade on interfaces or methods to configure rules, and optionally provide fallback or fallbackFactory for graceful degradation.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface Degrade {
double count();
int timeWindow() default 5;
DegradeStrategy degradeStrategy() default DegradeStrategy.AVERAGE_RT;
} @RetrofitClient(baseUrl = "${jy-helicarrier-api.serviceId}", path = "/m/count", errorDecoder = HelicarrierErrorDecoder.class)
@Retry
public interface ApiCountService { }Microservice HTTP Calls
Configure a ServiceInstanceChooser bean (e.g., SpringCloudServiceInstanceChooser) to resolve service instances. Use @RetrofitClient(serviceId = "...", path = "/m/count") for inter‑service calls.
@Bean
@Autowired
public ServiceInstanceChooser serviceInstanceChooser(LoadBalancerClient loadBalancerClient) {
return new SpringCloudServiceInstanceChooser(loadBalancerClient);
}Call Adapters
Two built‑in adapters: BodyCallAdapterFactory: synchronously executes the request and adapts the response body to the method return type (default enabled). ResponseCallAdapterFactory: synchronously executes the request and returns a Response<T> (default enabled).
Supported return types include Call<T>, CompletableFuture<T>, Void, Response<T>, and any other Java type (non‑2xx responses throw exceptions). Custom adapters can be added via retrofit.global-call-adapter-factories or the callAdapterFactories() attribute on @RetrofitClient.
retrofit:
global-call-adapter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BodyCallAdapterFactory
- com.github.lianjiatech.retrofit.spring.boot.core.ResponseCallAdapterFactoryData Converters
Retrofit uses Converter.Factory to serialize request bodies and deserialize responses. Supported factories include Gson, Jackson, Moshi, Protobuf, Wire, Simple XML, JAXB, etc. The starter defaults to JacksonConverterFactory and can be configured globally via retrofit.global-converter-factories or per‑interface via converterFactories().
retrofit:
global-converter-factories:
- retrofit2.converter.jackson.JacksonConverterFactoryConclusion
retrofit-spring-boot-starteris a lightweight HTTP client framework for SpringBoot projects that has been running stably in production for over a year and is adopted by multiple external companies. Feel free to try it, submit issues, or join the community for rapid support.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.
