Open Feign 使用、原理、源码(openfeign feign)
OpenFegin @FeignClient注解详解
@FeignClient 注解具有以下可配置属性:
- name:用于指定 Feign Client 的名称,同时也是 Spring Bean 的名称。默认情况下,使用注解的接口的简单类名作为名称。
- url:用于指定 Feign Client 请求的 URL 地址。如果指定了该属性,则 Feign 将不会使用 Eureka 进行服务发现。
- path:用于指定 Feign Client 调用的 URL 相对路径,默认为空。
- fallback:用于指定 Fallback 类的 Class 对象。该类需要实现当前注解标注的接口,并提供相应的容错逻辑。
- fallbackFactory:用于指定 Fallback 工厂类的 Class 对象。该工厂类需要实现 FallbackFactory 接口,并提供对应的容错逻辑。在创建 Fallback 实例时,会优先使用该工厂类。
- decode404:指示是否将 404 响应视为正常响应。默认情况下,Feign 客户端只有在收到非 404 响应时才会将响应体解码为 Java 对象。
- configuration:用于指定自定义的 Feign 配置类。这个自定义配置类必须是一个实现 FeignClientConfigurer 接口的 Bean。
- primary:用于指定 Feign Client 是否为首选 Bean。如果有多个 Feign Client 需要注入,且没有指定首选 Bean,那么 Spring 容器会抛出异常。
- qualifier:用于指定 Feign Client 的限定符,同样用于解决多个 Feign Client 需要注入的情况。
- contextId:该属性用于在同一应用程序中创建不同的 Feign Client 实例。例如,如果您需要为两个不同的服务使用相同的 URL,则可以通过将 contextId 属性设置为不同的值来实现此目的。
Open Feign 使用示例
OpenFeign是一个基于Java的HTTP客户端,它的设计目标是使得编写Http客户端变得更加简单。OpenFeign提供了类似于Spring MVC注解风格的API,并且能够与Ribbon和Eureka等组件集成,从而实现负载均衡以及服务发现等功能。
下面是一个简单的使用OpenFeign进行HTTP请求的示例:
首先,在Maven中添加OpenFeign的依赖:
org.springframework.cloud
spring-cloud-starter-openfeign
然后,定义一个Feign客户端接口:
@FeignClient(name = "github", url = "https://api.github.com")
public interface GithubClient {
@GetMapping("/users/{username}")
User getUser(@PathVariable("username") String username);
}
其中,@FeignClient注解用于指定该接口是一个Feign客户端,name属性用于指定客户端名称,url属性用于指定请求的URL。
接着,定义一个POJO类User:
public class User {
private String name;
private String blog;
// getters and setters
}
最后,在Spring Boot应用程序中使用该Feign客户端:
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
private GithubClient githubClient;
@GetMapping("/users/{username}")
public User getUser(@PathVariable String username) {
return githubClient.getUser(username);
}
}
@EnableFeignClients注解用于启用Feign客户端功能,@Autowired注解用于注入GithubClient接口的实例。
现在,我们就可以通过访问
http://localhost:8080/users/octocat来获取octocat用户的信息了。
Open Feign 原理
OpenFeign是一种基于接口的声明式Web服务客户端,它使得编写Web服务客户端变得更加简单。通过定义一个Java接口并使用注解来配置它,OpenFeign将自动地生成一个实现该接口的Web服务客户端。当我们调用该客户端时,它会发送HTTP请求到相应的Web服务端点,并将响应转换为我们所期望的类型。
OpenFeign的实现基于Netflix的Feign库。它支持多种HTTP请求方式和序列化协议,包括JSON和XML。在底层,OpenFeign使用了Ribbon和Hystrix来提供负载均衡和容错能力。
OpenFeign的工作原理可以大致分为以下几个步骤:
- 定义接口:首先需要定义一个 Java 接口,该接口中定义了需要访问的 REST API 方法,包括请求方式、请求路径、请求参数、请求头等信息。
- 注解配置:在接口方法上使用注解进行配置,包括请求方式、请求路径、请求参数、请求头等信息。OpenFeign 提供了很多注解,例如 @RequestMapping、@RequestParam、@RequestHeader 等。
- 生成代理类:在应用启动时,OpenFeign 会根据接口定义和注解配置生成一个代理类。这个代理类会通过 Java 反射机制调用底层的 HTTP 客户端进行请求。
- 发起请求:当调用接口方法时,实际上是调用了代理类的对应方法。代理类会根据注解配置生成 HTTP 请求,并将请求发送给服务端。
- 接收响应:服务端接收到请求后会返回一个 HTTP 响应。代理类会将响应解析成 Java 对象,并将其返回给调用方。
总之,OpenFeign通过在运行时生成Web服务客户端代码的方式,简化了Web服务客户端的开发。
Open Feign 自定义拦截器
请求拦截
OpenFeign 提供了 RequestInterceptor 接口,用于对 Feign 请求进行拦截和处理。通过实现该接口,可以在发送 Feign 请求前后进行一些自定义操作,例如添加公共请求头、签名等。
具体来说,RequestInterceptor 接口定义了两个方法:
- apply(RequestTemplate template):该方法会在发送 Feign 请求前被调用,传入的参数 template 是 HTTP 请求的模板,可以通过该模板设置请求头、请求体等信息。
- toString():该方法返回拦截器的名称,方便调试和排查问题。
下面是一个简单的示例,演示如何在 Feign 请求中添加公共请求头:
public class CustomHeaderInterceptor implements RequestInterceptor {
private final String headerName;
private final String headerValue;
public CustomHeaderInterceptor(String headerName, String headerValue) {
this.headerName = headerName;
this.headerValue = headerValue;
}
@Override
public void apply(RequestTemplate template) {
template.header(headerName, headerValue);
}
@Override
public String toString() {
return "CustomHeaderInterceptor{" +
"headerName='" + headerName + '\'' +
", headerValue='" + headerValue + '\'' +
'}';
}
}
在上述代码中,我们自定义了一个 CustomHeaderInterceptor 拦截器,它会在每次 Feign 请求中添加一个指定的请求头。使用该拦截器只需要在创建 Feign 客户端时将其加入即可:
MyClient client = Feign.builder()
.requestInterceptor(new CustomHeaderInterceptor("Authorization", "Bearer token"))
.target(MyClient.class, "http://localhost:8080");
这样,在每次调用 MyClient 接口的方法时,都会自动添加一个名为 "Authorization",值为 "Bearer token" 的请求头。
或者将CustomHeaderInterceptor注入到spring容器中。
响应拦截
在使用OpenFeign时,我们可以通过实现ResponseInterceptor接口来拦截响应。这个接口定义了一个方法void apply(Response response),该方法将在每次响应到达时被调用。
我们可以在这个方法中对响应进行处理,比如获取响应头、状态码、响应体等信息,并根据这些信息做出相应的处理。以下是一个简单的示例:
public class MyResponseInterceptor implements ResponseInterceptor {
@Override
public void apply(Response response) {
// 获取响应体
String responseBody = response.body().toString();
System.out.println("Response Body: " + responseBody);
// 获取响应状态码
int status = response.status();
System.out.println("Response Status: " + status);
// 获取响应头信息
Map> headers = response.headers();
for (Map.Entry> header : headers.entrySet()) {
System.out.println(header.getKey() + ": " + header.getValue());
}
}
}
在上面的代码中,我们实现了ResponseInterceptor接口,并重写了其中的apply()方法。在这个方法中,我们获取了响应体、状态码和响应头信息,并将其打印出来。当我们使用OpenFeign发起请求时,这个拦截器就会自动应用于响应中。
要使用自定义的响应拦截器,可以在使用@FeignClient注解的接口中指定configuration属性,例如:
@FeignClient(name = "example", configuration = MyFeignConfiguration.class)
public interface MyFeignClient {
// ...
}
在MyFeignConfiguration中可以注册自定义的响应拦截器,例如:
@Configuration
public class MyFeignConfiguration {
@Bean
public ResponseInterceptor myResponseInterceptor() {
return new MyResponseInterceptor();
}
}
这样,在使用MyFeignClient发起请求时,就会自动应用我们定义的响应拦截器了。
Open Feign 实现负载均衡策略
负载均衡策略本质:从注册中心获取对应的服务列表,根据服务的相关信息以及属性,选择一个服务进行请求。对标到框架上就是从OpenFeign中得到服务名称,Ribbon根据服务名称找到服务列表,并且根据策略选出服务,是由RestTemplate发送请求。
OpenFeign 实现负载均衡策略可以通过以下几种方式:
1.使用 Ribbon 进行负载均衡:可以通过在 Feign 配置类中配置 RibbonClient 对象,以便在远程调用时使用 Ribbon 进行负载均衡。同时需要在 Ribbon 配置类中设置负载均衡规则。
2.使用 Spring Cloud LoadBalancer 进行负载均衡:可以在 Feign 配置类中配置 LoadBalancerFeignClient 对象,以便在远程调用时使用 Spring Cloud LoadBalancer 进行负载均衡。
3.自定义负载均衡逻辑:可以通过实现 Feign 的 LoadBalancer 接口自定义负载均衡逻辑,并在 Feign 配置类中将该负载均衡器对象注入到 Feign.Builder 中。
示例代码如下:
// 使用 Ribbon 进行负载均衡
@FeignClient(name = "service-provider", configuration = RibbonConfig.class)
public interface ProviderService {
// ...
}
@Configuration
public class RibbonConfig {
@Bean
public IRule ribbonRule() {
return new RandomRule();
}
@Bean
public ServerList ribbonServerList(IClientConfig config) {
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
@Bean
public RibbonClient ribbonClient(IClientConfig config, ServerList serverList) {
return RibbonClient.builder()
.withServerList(serverList)
.withLoadBalancerRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))
.withClientConfig(config)
.build();
}
}
// 使用 Spring Cloud LoadBalancer 进行负载均衡
@FeignClient(name = "service-provider", configuration = LoadBalancerConfig.class)
public interface ProviderService {
// ...
}
@Configuration
public class LoadBalancerConfig {
@Bean
public ServiceInstanceListSupplier serviceInstanceListSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withApplicationContext(context)
.build();
}
@Bean
public LoadBalancerFeignClient loadBalancerFeignClient(
WebClient.Builder webClientBuilder, ReactiveLoadBalancer.Factory loadBalancerFactory) {
return new LoadBalancerFeignClient(new ReactiveLoadBalancerFeignClient(webClientBuilder, loadBalancerFactory));
}
}
// 自定义负载均衡逻辑
@FeignClient(name = "service-provider", configuration = MyLoadBalancerConfig.class)
public interface ProviderService {
// ...
}
@Configuration
public class MyLoadBalancerConfig {
@Bean
public LoadBalancer myLoadBalancer() {
return new MyLoadBalancer();
}
@Bean
public Feign.Builder feignBuilder(LoadBalancer loadBalancer) {
return Feign.builder().client(new CustomLoadBalancerFeignClient(loadBalancer));
}
}
在上述示例代码中,分别使用了 Ribbon、Spring Cloud LoadBalancer 和自定义负载均衡逻辑三种方式实现了 OpenFeign 的负载均衡策略。其中 Ribbon 和 Spring Cloud LoadBalancer 的配置需要分别在 RibbonConfig 和 LoadBalancerConfig 中进行。而自定义的负载均衡逻辑则需要实现 LoadBalancer 接口,并在 MyLoadBalancerConfig 中将其注入到 Feign.Builder 对象中。
OpenFegin 自定义负载均衡策略、服务检查、获取服务、客户端
OpenFeign 实现负载均衡策略可以通过配置 Ribbon 的相关组件来实现。下面是一些常用的方法:
1.配置 IRule 接口实现类
IRule 是 Ribbon 提供的负载均衡策略接口,可以自定义实现类来定制负载均衡算法。OpenFeign 中可以通过以下方式进行配置:
@Configuration
public class FeignConfiguration {
@Bean
public IRule ribbonRule() {
return new RandomRule(); // 配置为随机策略
}
}
上面的示例中使用的是随机策略,还可以使用其他策略如轮询、最小连接数等等。
- 配置 IPing 接口实现类
IPing 接口定义了检查服务是否可用的方法,通过配置 IPing 的实现类可以定制服务检查策略。OpenFeign 中可以通过以下方式进行配置:
@Configuration
public class FeignConfiguration {
@Bean
public IPing ribbonPing() {
return new PingUrl(); // 配置为 URL 检查策略
}
}
上面的示例中使用的是 URL 检查策略,还可以使用其他策略如 ICMP Ping 等等。
- 配置 ServerList 接口实现类
ServerList 接口用于获取服务实例列表。默认情况下,Ribbon 使用 Eureka 或 Consul 等服务注册中心来获取服务实例列表,但也可以自定义实现类来获取服务实例列表。OpenFeign 中可以通过以下方式进行配置:
@Configuration
public class FeignConfiguration {
@Bean
public ServerList ribbonServerList(IClientConfig config) {
return new ConfigurationBasedServerList(); // 配置为配置文件列表策略
}
}
上面的示例中使用的是配置文件列表策略,还可以使用其他策略如 DNS 解析等等。
- 配置 IClientConfig 对象
IClientConfig 用于传递 Ribbon 配置参数,可以通过以下方式进行配置:
@Configuration
public class FeignConfiguration {
@Bean
public IClientConfig ribbonClientConfig() {
return new DefaultClientConfigImpl();
}
}
上面的示例中使用的是默认实现类 DefaultClientConfigImpl,还可以自定义实现类来定制配置参数。
综上所述,通过配置以上组件即可实现 OpenFeign 的负载均衡策略,具体使用时需要根据实际情况进行配置调整。
OpenFeign 源码解析
OpenFeign 是一个基于 Netflix Feign 的 Java 库,它提供了简单易用的 HTTP 请求接口,并且集成了 Ribbon 负载均衡组件。下面是 OpenFeign 的源码解析:
1.Feign.builder() 方法
该方法是 OpenFeign 中创建 Feign 客户端实例的入口,返回一个 Feign.Builder 对象,用于设置 Feign 客户端的参数和配置选项。
- FeignClientFactoryBean 类
该类是 Spring Boot 中创建 Feign 客户端的工厂类,负责生成 Feign 客户端的代理对象。其中,getObject() 方法会根据配置信息、注解信息等来创建 Feign 客户端实例。
- ReflectiveFeign 类
该类是 OpenFeign 中使用反射机制调用 Feign 客户端方法的核心类,定义了一系列静态方法用于构建和执行 HTTP 请求。其中,newInstance() 方法会使用 JDK 动态代理机制来创建 Feign 客户端的代理实例。
- feign.Contract 接口
该接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。
- feign.Logger 接口
该接口定义了 Feign 客户端的日志输出格式和级别,包括 NONE、BASIC、HEADERS 和 FULL 等级别。默认情况下,OpenFeign 使用的是 SLF4J 日志框架输出日志。
- feign.Client 接口
该接口定义了 Feign 客户端发送 HTTP 请求和接收响应的行为,并提供了多个实现类,包括 Apache HttpClient 和 OkHttp 实现类等。
- feign.codec.Encoder 接口和 feign.codec.Decoder 接口
这两个接口分别定义了请求消息体和响应消息体的编码和解码逻辑,根据要求可以使用不同的实现类来支持不同的编解码格式,如 JSON、XML、Protobuf 等。
- feign.codec.ErrorDecoder 接口
该接口定义了在 Feign 客户端请求失败时,如何将服务端返回的错误信息转换成异常信息并抛出,以及如何处理超时、连接异常等问题。
- feign.Contract.Default() 类
该类是 Contract 接口的默认实现类,它将请求方法与 HTTP 请求之间的映射规则定义为注解方式,如 @RequestMapping、@GetMapping、@PostMapping 等。
- feign.RequestTemplate 类
该类是 OpenFeign 中表示 HTTP 请求的模板类,用于存储请求 URL、HTTP 方法、请求头、请求体等信息,并提供了一系列方法用于设置和获取请求参数、请求头等。
源码
以下是 Feign 类的源码:
public class Feign {
// 创建一个 Feign 客户端实例的入口,返回一个 Feign.Builder 对象
public static Builder builder() {
return new Builder();
}
// 使用指定的 InvocationHandler 和接口类型创建代理对象实例
public static T newInstance(Target target) {
return newInstance(target, new HardCodedTarget<>(target.type(), target.name()));
}
// 使用指定的 InvocationHandler 和接口类型创建代理对象实例,并绑定到指定的 URL 上
public static T newInstance(Target target, InvocationHandler handler) {
return (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class>[] { target.type() },
handler);
}
// 定义 Feign 客户端的构建器类,用于设置其参数和配置选项
public static final class Builder extends Feign.Builder {
// ...
}
}
重要说明:newInstance() 方法使用了 JDK 的动态代理机制来创建代理对象,并且可以绑定到指定的 InvocationHandler 处理器上。其中,Target 接口表示请求目标信息,比如服务名、IP 地址、端口号等;HardCodedTarget 类则是对 Target 接口的默认实现。
public interface Target {
Class type();
String name();
String url();
}
public final class HardCodedTarget implements Target {
private final Class type;
private final String name;
public HardCodedTarget(Class type, String name) {
this.type = checkNotNull(type, "type");
this.name = checkNotNull(name, "name");
}
@Override
public Class type() {
return type;
}
@Override
public String name() {
return name;
}
@Override
public String url() {
throw new UnsupportedOperationException();
}
}
重要说明:HardCodedTarget 类是 Target 接口的默认实现,它包含了请求目标的类型和名称等信息。
public interface InvocationHandlerFactory {
InvocationHandler create(Target> target, Map dispatch);
default InvocationHandler wrap(InvocationHandler handler) {
return handler;
}
}
public abstract class InvocationHandler implements java.lang.reflect.InvocationHandler {
// 调用 API 前预处理方法
protected void preprocess(Target> target, Method method, Object[] args) throws Throwable {}
// 调用 API 后后处理方法
protected void postprocess(Target> target, Method method, Object[] args, Object result,
Throwable throwable) throws Throwable {}
}
重要说明:InvocationHandler 抽象类定义了调用 API 前后的预处理和后处理方法,可以在这些方法中执行一些额外的业务逻辑操作。
public interface Target {
// ...
Request apply(RequestTemplate input);
}
public interface RequestTemplate {
void method(String method);
void append(String key, String value);
void header(String key, String value);
void body(byte[] data, Charset charset);
// ...
}
重要说明:Target 接口中的 apply() 方法用于将请求模板应用到 Feign 请求中,而 RequestTemplate 接口则用于设置请求模板的各种参数和选项。
public interface Encoder {
// 将请求对象编码成指定格式的字节数组
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
public interface Decoder {
// 将响应字节数组解码成指定类型的对象
Object decode(Response response, Type type) throws IOException, DecodeException;
}
重要说明:Encoder 和 Decoder 接口分别定义了请求消息体和响应消息体的编码和解码逻辑,可以使用不同的实现类来支持不同的编解码格式,如 JSON、XML、Protobuf 等。
public interface Client {
// 发送 HTTP 请求并获取响应
Response execute(Request request, Request.Options options) throws IOException;
}
public interface ErrorDecoder {
// 将服务端返回的错误信息转换成异常信息并抛出
Exception decode(String methodKey, Response response);
}
public interface Logger {
// 日志级别:NONE、BASIC、HEADERS 和 FULL 等级别
enum Level {
NONE, // 不输出日志
BASIC, // 只输出请求方法和 URL
HEADERS, // 输出请求和响应的头部信息
FULL // 输出完整的请求和响应信息
}
// 输出日志信息
void log(String configKey, String format, Object... args);
}
重要说明:Client 接口定义了发送 HTTP 请求的行为,并提供了多个实现类,包括 Apache HttpClient 和 OkHttp 实现类等;ErrorDecoder 接口定义了将服务端返回的错误信息转换成异常信息并抛出的逻辑,通常需要自定义实现类来处理自己的业务逻辑;Logger 接口定义了 Feign 客户端的日志输出格式和级别,可以使用不同实现类来支持不同的日志框架。
public interface Contract {
List parseAndValidatateMetadata(Class> targetType);
// 构建 HTTP 请求模板
MethodMetadata parseAndValidateMetadata(Method method);
class Default extends AbstractContract {
// ...
}
}
重要说明:Contract 接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。AbstractContract 类提供了一些公共的解析逻辑和模板构建方法,而 Default 内部类则是 AbstractContract 的默认实现。
public abstract class AbstractContract implements Contract {
protected interface AnnotatedParamMetadata {
// ...
}
protected interface AnnotatedMethodMetadata {
// ...
}
protected interface MethodMetadataBuilder {
// ...
}
// 解析参数上的注解信息
protected List requestParamMetadata(MethodMetadata data) {
// ...
}
// 解析方法上的注解信息
protected AnnotatedMethodMetadata getAnnotationMetadata(Method method) {
// ...
}
// 构建 HTTP 请求模板
protected MethodMetadata parseAndValidateMetadata(Class> targetType, Method method) {
// ...
}
}
重要说明:AbstractContract 抽象类提供了一些公共的解析逻辑和模板构建方法,可以被子类继承和扩展。
public interface RequestInterceptor {
// 处理请求前的预处理操作
void apply(RequestTemplate template);
}
public interface Retryer {
// 确定是否重试以及间隔时间
void continueOrPropagate(RetryableException e);
public static class Default implements Retryer {
// ...
}
}
public interface Target {
// ...
String name();
String url();
}
public final class Request {
// HTTP 请求方法:GET、POST、PUT、DELETE 等
public enum HttpMethod {
GET, POST, PUT, DELETE, HEAD, OPTIONS, PATCH, TRACE;
}
// 构造函数,用于创建一个新的 HTTP 请求对象
protected Request(String method, String url, Map> headers,
byte[] body, Charset charset) {
// ...
}
// 获取 HTTP 请求方法
public String method() {
return method;
}
// 获取 HTTP 请求 URL
public String url() {
return url;
}
// 获取 HTTP 请求头部信息
public Map> headers() {
return Collections.unmodifiableMap(headers);
}
// 获取 HTTP 请求消息体字节数组
public byte[] body() {
return body;
}
// 获取 HTTP 请求消息体编码方式
public Charset charset() {
return charset;
}
}
public final class Response {
// HTTP 响应状态码:200、404、500 等
public enum StatusType {
INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR, SERVER_ERROR, UNKNOWN;
}
// 构造函数,用于创建一个新的 HTTP 响应对象
protected Response(int status, String reason, Map> headers,
byte[] body, Charset charset) {
// ...
}
// 获取 HTTP 响应状态码
public int status() {
return status;
}
// 获取 HTTP 响应原因短语
public String reason() {
return reason;
}
// 获取 HTTP 响应头部信息
public Map> headers() {
return Collections.unmodifiableMap(headers);
}
// 获取 HTTP 响应消息体字节数组
public byte[] body() {
return body;
}
// 获取 HTTP 响应消息体编码方式
public Charset charset() {
return charset;
}
}
重要说明:RequestInterceptor 接口定义了请求前的预处理操作,可以在其中添加一些公共的请求头部信息、认证信息等;Retryer 接口定义了确定是否重试以及间隔时间的逻辑,可以使用不同实现类来实现自己的重试策略;Target 接口中新增了 name() 和 url() 方法用于获取请求目标的名称和 URL 地址信息;Request 类表示 HTTP 请求对象,包括请求方法、URL、请求头部信息、请求消息体等;Response 类表示 HTTP 响应对象,包括响应状态码、原因短语、响应头部信息、响应消息体等。
public interface ResponseMapper {
// 将 Feign 响应转换成服务端响应
Response map(Response response, Type type) throws IOException;
public static class Default implements ResponseMapper {
// ...
}
}
重要说明:ResponseMapper 接口定义了将 Feign 客户端的响应转换成服务端响应(即将 Feign 的 Response 对象转换成服务端的原生 Response 对象)的逻辑,通常需要自定义实现类来处理自己的业务逻辑。Default 内部类是 ResponseMapper 的默认实现,它直接返回 Feign 的 Response 对象。
public interface Contract {
List parseAndValidatateMetadata(Class> targetType);
// 构建 HTTP 请求模板
MethodMetadata parseAndValidateMetadata(Method method);
class Default extends AbstractContract {
// ...
}
}
public abstract class AbstractContract implements Contract {
protected interface AnnotatedParamMetadata {
// ...
}
protected interface AnnotatedMethodMetadata {
// ...
}
protected interface MethodMetadataBuilder {
// ...
}
// 解析参数上的注解信息
protected List requestParamMetadata(MethodMetadata data) {
// ...
}
// 解析方法上的注解信息
protected AnnotatedMethodMetadata getAnnotationMetadata(Method method) {
// ...
}
// 构建 HTTP 请求模板
protected MethodMetadata parseAndValidateMetadata(Class> targetType, Method method) {
// ...
}
}
重要说明:Contract 接口定义了 Feign 客户端接口和 HTTP 请求之间的映射规则,包括 URL、HTTP 方法、请求头、请求体等。默认情况下,OpenFeign 使用的是 Spring Cloud 中的 SpringMvcContract 实现类。AbstractContract 类提供了一些公共的解析逻辑和模板构建方法,而 Default 内部类则是 AbstractContract 的默认实现。
public interface Decoder {
// 将响应字节数组解码成指定类型的对象
Object decode(Response response, Type type) throws IOException, DecodeException;
}
public interface Encoder {
// 将请求对象编码成指定格式的字节数组
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
public interface Logger {
// 日志级别:NONE、BASIC、HEADERS 和 FULL 等级别
enum Level {
NONE, // 不输出日志
BASIC, // 只输出请求方法和 URL
HEADERS, // 输出请求和响应的头部信息
FULL // 输出完整的请求和响应信息
}
// 输出日志信息
void log(String configKey, String format, Object... args);
}
重要说明:Decoder 和 Encoder 接口分别定义了响应消息体和请求消息体的解码和编码逻辑,可以使用不同实现类来支持不同的编解码格式,比如 JSON、XML、Protobuf 等。Logger 接口定义了 Feign 客户端的日志输出格式和级别,可以使用不同实现类来支持不同的日志框架。
public interface Target {
// 获取请求目标类型
Class type();
// 获取请求目标名称
String name();
// 获取请求目标 URL
String url();
}
public final class HardCodedTarget implements Target {
private final Class type;
private final String name;
public HardCodedTarget(Class type, String name) {
this.type = checkNotNull(type, "type");
this.name = checkNotNull(name, "name");
}
@Override
public Class type() {
return type;
}
@Override
public String name() {
return name;
}
@Override
public String url() {
throw new UnsupportedOperationException();
}
}
重要说明:Target 接口表示请求目标信息,比如服务名、IP 地址、端口号等;而 HardCodedTarget 类则是对 Target 接口的默认实现。