Spring Boot developers reach for RestTemplate or WebClient out of habit, but they're both still imperative builders: you hand-code the URL, the headers, and the body every single time. JQuickCurl brings a different workflow into your Spring Boot project — you define HTTP calls as curl commands on an interface, and Spring treats that interface's proxy like any other bean you can @Autowired.
This tutorial walks through a real Spring Boot setup step by step:
- Adding the dependency.
- Declaring a curl-powered service interface.
- Exposing the proxy as a Spring bean.
- Loading global HTTP settings from a properties file.
- Calling everything from a
@RestController.
Note: JQuickCurl ships as a plain library (no Spring Boot starter yet). Wiring it as a
@Configurationbean is a few lines of code and keeps full control in your hands.
Step 1: Dependencies
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.github.paohaijiao</groupId>
<artifactId>jquick-curl</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
Step 2: Declare a curl-Driven Service Interface
We call the public JSONPlaceholder API to fetch a "todo" item. Note the signature convention: the method receives a JQuickCurlReq, and its return type is a domain object.
import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.domain.req.JQuickCurlReq;
public interface TodoApi {
@JCurlCommand("curl -X GET https://jsonplaceholder.typicode.com/todos/1")
TodoBean getTodo(JQuickCurlReq request);
@JCurlCommand("curl -X POST https://jsonplaceholder.typicode.com/todos "
+ "-H 'Content-Type: application/json' "
+ "-d '{\"title\":\"learn jquick-curl\",\"completed\":false}'")
TodoBean createTodo(JQuickCurlReq request);
}
A couple of Java-syntax tricks used above that will save you headaches:
- Concatenated string literals are compile-time constants, so they're perfectly valid inside annotations — long curl commands stay readable.
- Inner double quotes inside the JSON body must be escaped (
\"), exactly as in any Java string.
The domain bean is a plain POJO (the response converter maps JSON onto its fields):
public class TodoBean {
private long userId;
private long id;
private String title;
private boolean completed;
// getters and setters omitted for brevity
}
Step 3: Register the Proxy as a Spring Bean
JCurlInvoker.createProxy() returns a JDK proxy of your interface. We turn that into a singleton bean, so every controller or service can @Autowired TodoApi and the proxy — and its OkHttp transport — is created only once.
import com.github.paohaijiao.config.JQuickCurlConfig;
import com.github.paohaijiao.executor.JCurlInvoker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpApiConfig {
// Optional: apply global HTTP settings (timeouts, pool, retry) at startup.
public HttpApiConfig() throws Exception {
JQuickCurlConfig.getInstance()
.loadFromClasspathResource("quick-curl.properties");
}
@Bean
public TodoApi todoApi() {
return JCurlInvoker.createProxy(TodoApi.class);
}
}
JQuickCurlConfig is a process-wide singleton; the fluent builders below are also available if you prefer code over properties:
JQuickCurlConfig.getInstance()
.connectTimeout(3, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.maxRetryCount(2)
.followRedirects(true);
Step 4: Properties-Driven Tuning
Place src/main/resources/quick-curl.properties on the classpath and load it with loadFromClasspathResource(...) (keys shown here are the ones this API reads today):
quick.curl.connect.timeout=3000
quick.curl.read.timeout=10000
quick.curl.write.timeout=10000
quick.curl.pool.max.idle=20
quick.curl.pool.keep.alive=300000
quick.curl.max.retry.count=2
Now your non-functional requirements (timeouts, connection pool, retry budget) live in configuration while your request definitions live in code — no more hunting for magic numbers inside service classes.
Step 5: Consume the Bean from a Controller
import com.github.paohaijiao.domain.req.JQuickCurlReq;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TodoController {
private final TodoApi todoApi;
public TodoController(TodoApi todoApi) {
this.todoApi = todoApi;
}
@GetMapping("/api/todos/1")
public TodoBean getTodo() {
return todoApi.getTodo(new JQuickCurlReq());
}
@PostMapping("/api/todos")
public TodoBean createTodo() {
return todoApi.createTodo(new JQuickCurlReq());
}
}
Start the application and test it:
mvn spring-boot:run
curl http://localhost:8080/api/todos/1
Response:
{"userId":1,"id":1,"title":"delectus aut autem","completed":false}
The controller stays clean: no RestTemplate, no URL constants, no header maps — the HTTP knowledge is encapsulated in @JCurlCommand annotations.
Summary
Integrating JQuickCurl into Spring Boot only requires three small pieces: a curl-annotated interface, one @Configuration class that exports its proxy as a bean, and an optional properties load for global tuning. From there, @Autowired works exactly as it does with any Spring-managed HTTP abstraction.
For a complete series context, all examples build on the dromara/jquick-curl project (Apache-2.0). In Post 3 we compare JQuickCurl against OkHttp, RestTemplate, and OpenFeign so you can decide, with evidence, when it deserves a place in your architecture.
Top comments (0)