Feign简介
一、简介
Feign是一个由Netflix开源的声明式HTTP客户端框架,旨在简化Web服务客户端的开发。通过抽象化模板代码,Feign让开发者能够以更简洁、更直观的方式编写REST API调用。
二、原生Feign样例
1、注解
-
@RequestLine
@RequestLine是原生Feign核心库中用于定义HTTP请求的关键注解,它提供了一种更接近于底层HTTP协议的、声明式的方式来指定请求方法(GET, POST 等)和路径;它的核心作用是将接口方法映射到具体的HTTP请求。
@RequestLine注解的值是一个字符串,标准格式为:”HTTP_METHOD URI_TEMPLATE”,其中:
-
HTTP_METHOD:请求方法,例如 GET, POST, PUT, DELETE, HEAD等。
-
URI_TEMPLATE:请求路径,可以包含占位符(如{id})来动态替换参数。
-
-
@Param
使用@Param注解显式标记方法参数,并指定在URI模板或头信息中对应的占位符名称。
举例:
简单的GET请求,无参数:
@RequestLine("GET /books")
List<Book> findAll();
路径中包含参数的GET请求:
@RequestLine("GET /books/{isbn}")
Book findByIsbn(@Param("isbn") String isbn);
路径和查询参数结合使用:
@RequestLine("GET /books?author={author}&title={title}")
List<Book> findBooksByAuthorAndTitle(@Param("author") String author, @Param("title") String title);
POST请求,需指定Content-Type头信息:
@RequestLine("POST /book")
@Headers("Content-Type: application/json")
void create(Book book);
2、样例
- 数据源
此处使用jsonplaceholder提供的todos数据:
https://jsonplaceholder.typicode.com/todos返回200个待办事项,结构如下:
[
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
},
{
"userId": 1,
"id": 2,
"title": "quis ut nam facilis et officia qui",
"completed": false
},
{
"userId": 1,
"id": 3,
"title": "fugiat veniam minus",
"completed": false
},
...
]
可以在请求中增加todo的id获取指定的待办事项:https://jsonplaceholder.typicode.com/todos/5
{
"userId": 1,
"id": 5,
"title": "laboriosam mollitia et enim quasi adipisci quia provident illum",
"completed": false
}
- 依赖
pom.xml中依赖如下:
<dependencies>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>13.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>13.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
<version>13.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
</dependencies>
- 日志配置
src/main/resource/logback.xml:
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG"> <!-- 根据需要调整根日志级别,如 INFO 或 WARN -->
<appender-ref ref="CONSOLE" />
</root>
</configuration>
- 模型类
Todo.java:
public class Todo {
private int id;
private int userId;
private String title;
private boolean completed;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public boolean isCompleted() {
return completed;
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
@Override
public String toString() {
return String.format("id: %s, userId: %s, title: %s, completed: %s", id, userId, title, completed);
}
}
- 服务类
TodoService.java:
import java.util.List;
import feign.Param;
import feign.RequestLine;
public interface TodoService {
@RequestLine("GET")
List<Todo> findAll();
@RequestLine("GET /{id}")
Todo findOne(@Param("id") int id);
}
- 主程序
public class App {
public static void main(String[] args) {
org.slf4j.Logger log = LoggerFactory.getLogger(App.class);
TodoService client = Feign.builder()
.client(new OkHttpClient())
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(new Slf4jLogger(App.class))
.logLevel(Logger.Level.FULL)
.target(TodoService.class, "https://jsonplaceholder.typicode.com/todos");
List<Todo> all = client.findAll();
if(all != null) {
log.info("Size: " + all.size());
}
Todo todo = (Todo) client.findOne(1);
log.info(String.valueOf(todo));
}
}
- 运行结果
输出:
[main] INFO demo.App - Size: 200
[main] INFO demo.App - id: 1, userId: 1, title: delectus aut autem, completed: false
由于样例中日志设置了DEBUG级别,在输出内容中可以看到详细的请求过程和返回结果。
三、Spring Boot样例
- 数据源
同上面样例中的数据源
- 模型类
同上面样例中的模型类
- 依赖
pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
- 服务类
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name = "todo-service", url = "https://jsonplaceholder.typicode.com")
public interface TodoService {
@RequestMapping(value = "/todos", method = RequestMethod.GET)
List<Todo> findAll();
@RequestMapping(value = "/todos/{id}", method = RequestMethod.GET)
Todo findOne(@PathVariable("id") int id);
}
- 控制器类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TodoController {
@Autowired
private TodoService todoService;
@GetMapping("/getTodos")
public ResponseEntity<?> getTodos(){
return ResponseEntity.ok(todoService.findAll());
}
@GetMapping("/getTodo/{id}")
public ResponseEntity<?> getTodo(@PathVariable int id){
return ResponseEntity.ok(todoService.findOne(id));
}
}
- 主程序
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
- 运行
访问http://localhost:8080/getTodos可以获取所有待办事项;
访问http://localhost:8080/getTodo/1可以获取id为1的待办事项。