本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent
接下来,我们开始分析 OpenFeign 的生命周期,结合 OpenFeign 本身的源代码。首先是从接口定义创建 OpenFeign 代理开始。我们这里只关心同步客户端,因为异步客户端目前还在实现中,并且在我们的项目中,异步响应式的客户端不用 OpenFeign,而是用的官方的 WebClient
创建 OpenFeign 代理
创建 OpenFeign 代理,主要分为以下几步:
- 使用 Contract 解析接口的每一个方法,生成每一个方法的元数据列表:
List<MethodMetadata> metadata
- 根据每一个
MethodMetadata
,生成对应的请求模板工厂
RequestTemplate.Factory
,用于生成后面的请求。同时,使用这个模板工厂以及其他配置生成对应的方法处理器
MethodHandler
,对于同步的 OpenFeign,
MethodHandler
实现为
SynchronousMethodHandler
。将接口方法与
MethodHandler
一一对应建立映射,结果为
Map<Method, MethodHandler> methodToHandler
。对于 Java 8 引入的 interface default 方法,需要用不同
MethodHandler
,即
DefaultMethodHandler
,因为这种方法不用代理,不用生成对应的 http 调用,其实现为直接调用对应的 default 方法代码。
- 使用 InvocationHandlerFactory 这个工厂,创建
InvocationHandler
用于代理调用。
- 调用 JDK 动态代理生成类方法使用
InvocationHandler
创建代理类。
创建 OpenFeign 代理,主要基于 JDK 的动态代理实现。我们先举一个简单的例子,创建一个 JDK 动态代理,用来类比。
JDK 动态代理
使用 JDK 动态代理,需要如下几个步骤:
1. 编写接口以及对应的代理类。我们这里编写一个简单的接口和对应的实现类:
public interface TestService {void test();}
public class TestServiceImpl implements TestService {@Overridepublic void test() {System.out.println("TestServiceImpl#test is called");}}
2.创建代理类实现
java.lang.reflect.InvocationHandler
,并且,在核心方法中,调用实际的对象,这里即我们上面 TestService 的实现类 TestServiceImpl 的对象。
JDK 中有内置的动态代理 API,其核心是
java.lang.reflect.InvocationHandler
。我们先来创建一个简单的
InvocationHandler
实现类:
public class SimplePrintMethodInvocationHandler implements InvocationHandler {private final TestService testService;public SimplePrintMethodInvocationHandler(TestService testService) {this.testService = testService;}@Overridepublic Object invoke(//代理对象Object proxy,//调用的方法Method method,//使用的参数Object[] args)throws Throwable {System.out.println("Invoked method: " + method.getName());//进行实际的调用return method.invoke(testService, args);}}
3.创建代理对象,并使用代理对象调用。一般通过 Proxy 的静态方法去创建,例如:
//首先,创建要代理的对象TestServiceImpl testServiceImpl = new TestServiceImpl();//然后使用要代理的对象创建对应的 InvocationHandlerSimplePrintMethodInvocationHandler simplePrintMethodInvocationHandler = new SimplePrintMethodInvocationHandler(testServiceImpl);//创建代理类,因为一个类可能实现多个接口,所以这里返回的是 Object,用户根据自己需要强制转换成要用的接口Object proxyInstance = Proxy.newProxyInstance(TestService.class.getClassLoader(),testServiceImpl.getClass().getInterfaces(),simplePrintMethodInvocationHandler);//强制转换TestService proxied = (TestService) proxyInstance;//使用代理对象进行调用proxied.test();
这样,我们就使用了 JDK 的内置动态代理机制实现了一个简单的动态代理。在 OpenFeign 的使用中,和我们的示例有一点区别。首先,我们只需要定义要代理的接口,不用定义实现类。因为所有的 OpenFeign 接口要做的事情其实都是 HTTP 调用,其信息可以自动从接口定义中生成,我们可以使用统一的对象根据接口定义,承载 OpenFeign 接口定义的请求。在 OpenFeign 中,这个等同于实现对象的,就是根据接口生成的 MethodHandler,在同步的 OpenFeign 中,即
feign.SynchronousMethodHandler
。之后,OpenFeign 创建的 InvocationHandler,其实就是将调用转发到对应的
SynchronousMethodHandler
的对应方法。
创建 OpenFeign 代理对象的流程详解
我们使用前面的例子,来看一下创建代理的流程:
interface GitHub {/*** 定义get方法,包括路径参数,响应返回序列化类* @param owner* @param repository* @return*/@RequestLine("GET /repos/{owner}/{repo}/contributors")List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repository);/*** 响应体结构类*/class Contributor {String login;int contributions;public Contributor() {}public String getLogin() {return login;}public void setLogin(String login) {this.login = login;}public int getContributions() {return contributions;}public void setContributions(int contributions) {this.contributions = contributions;}}}/*** 基于 FastJson 的反序列化解码器*/static class FastJsonDecoder implements Decoder {@Overridepublic Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {//读取 bodybyte[] body = response.body().asInputStream().readAllBytes();return JSON.parseObject(body, type);}}
public static void main(String[] args) {//创建 Feign 代理的 HTTP 调用接口实现GitHub github = Feign.builder()//指定解码器为 FastJsonDecoder.decoder(new FastJsonDecoder())//指定代理类为 GitHub,基址为 https://api.github.com.target(GitHub.class, "https://api.github.com");List<GitHub.Contributor> contributors = github.contributors("OpenFeign", "feign");}
我们这里关心的其实就是创建 Feign 代理的 HTTP 调用接口实现这一步的内部流程。首先我们来看 Feign 的 Builder 的结构,当我们初始化一个 Feign 的 Builder 也就是调用
Feign.builder()
时,会创建如下组件(同时也说明以下组件都是可以配置的,如果一些配置之前没有提到,则可以):
//请求拦截器列表,默认为空private final List<RequestInterceptor> requestInterceptors = new ArrayList();//日志级别,默认不打印任何日志private Level logLevel = Level.NONE;//负责解析类元数据的 Contract,默认是支持 OpenFeign 内置注解的默认 Contractprivate Contract contract = new Contract.Default();//承载 HTTP 请求的 Client,默认是基于 Java HttpURLConnection 的 Default Clientprivate Client client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);//重试器,默认也是 Defaultprivate Retryer retryer = new feign.Retryer.Default();//默认的日志 Logger,默认不记录任何日志private Logger logger = new NoOpLogger();//编码器解码器也是默认的private Encoder encoder = new feign.codec.Encoder.Default();private Decoder decoder = new feign.codec.Decoder.Default();//查询参数编码,这个我们一般不会修改private QueryMapEncoder queryMapEncoder = new FieldQueryMapEncoder();//错误编码器,默认为 Defaultprivate ErrorDecoder errorDecoder = new feign.codec.ErrorDecoder.Default();//各种超时的 Options 走的默认配置private Options options = new Options();//用来生成 InvocationHandler 的 Factory 也是默认的private InvocationHandlerFactory invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();//是否特殊解析 404 错误,因为针对 404 我们可能不想抛出异常,默认是 falseprivate boolean decode404 = false;//是否在解码后立刻关闭 Response,默认为是private boolean closeAfterDecode = true;//异常传播规则,默认是不传播private ExceptionPropagationPolicy propagationPolicy = ExceptionPropagationPolicy.NONE;//是否强制解码,这个主要为了兼容异步 Feign 引入的配置,我们直接忽略,认为他就是 false 即可private boolean forceDecoding = false;private List<Capability> capabilities = new ArrayList();
我们的代码中指定了指定解码器为 FastJsonDecoder,所以 Decoder 就是 FastJsonDecoder 了。最后通过
target(GitHub.class, "https://api.github.com");
指定定代理类为 GitHub,基址为 https://api.github.com,这时候就会生成 Feign 代理类,其步骤是:
Feign
public <T> T target(Class<T> apiType, String url) {//使用代理接口类型,以及基址创建 HardCodedTarget,他的意思其实就是硬编码的 Targetreturn target(new HardCodedTarget<T>(apiType, url));}public <T> T target(Target<T> target) {return build().newInstance(target);}public Feign build() {//将所有组件经过所有的 Capability,从这里我们可以看出,我们可以实现 Capability 接口来在创建 Feign 代理的时候动态修改组件Client client = Capability.enrich(this.client, capabilities);Retryer retryer = Capability.enrich(this.retryer, capabilities);List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream().map(ri -> Capability.enrich(ri, capabilities)).collect(Collectors.toList());Logger logger = Capability.enrich(this.logger, capabilities);Contract contract = Capability.enrich(this.contract, capabilities);Options options = Capability.enrich(this.options, capabilities);Encoder encoder = Capability.enrich(this.encoder, capabilities);Decoder decoder = Capability.enrich(this.decoder, capabilities);InvocationHandlerFactory invocationHandlerFactory =Capability.enrich(this.invocationHandlerFactory, capabilities);QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);//创建 SynchronousMethodHandler 的 Factory,用于生成 SynchronousMethodHandler,SynchronousMethodHandler 是实际承载 Feign 代理请求的实现类SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);//通过方法名称来区分不同接口方法的元数据解析,用于生成并路由到对应的代理方法ParseHandlersByName handlersByName =new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,errorDecoder, synchronousMethodHandlerFactory);//创建 ReflectiveFeignreturn new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);}}
创建 ReflectiveFeign 之后,会调用其中的 newInstance 方法:
ReflectiveFeign
public <T> T newInstance(Target<T> target) {//使用前面提到的 ParseHandlersByName 解析元数据并生成所有需要代理的方法的 MethodHandler,我们这里分析的是同步 Feign,所以是 SynchronousMethodHandlerMap<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();//将方法与对应的 MethodHandler 一一对应for (Method method : target.type().getMethods()) {if (method.getDeclaringClass() == Object.class) {//对于 Object 的方法,直接跳过continue;} else if (Util.isDefault(method)) {//如果是 java 8 中接口的默认方法,就使用 DefaultMethodHandler 处理DefaultMethodHandler handler = new DefaultMethodHandler(method);defaultMethodHandlers.add(handler);methodToHandler.put(method, handler);} else {methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));}}//使用前面 Builder 中的 InvocationHandlerFactory 创建 InvocationHandlerInvocationHandler handler = factory.create(target, methodToHandler);//使用 InvocationHandler 创建 ProxyT proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),new Class<?>[] {target.type()}, handler);//将代理与 DefaultMethodHandler 关联for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {defaultMethodHandler.bindTo(proxy);}return proxy;}
对于使用前面提到的 ParseHandlersByName 解析元数据并生成所有需要代理的方法的 MethodHandler 这一步,主要就涉及到了使用 Contract 解析出方法的元数据,然后将这些元数据用对应的编码器绑定用于之后调用的编码:
ReflectiveFeign
public Map<String, MethodHandler> apply(Target target) {// 使用 Contract 解析出所有方法的元数据List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();// 对于每个解析出的方法元数据for (MethodMetadata md : metadata) {BuildTemplateByResolvingArgs buildTemplate;if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {//有表单的情况buildTemplate =new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);} else if (md.bodyIndex() != null) {//有 body 的情况buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);} else {//其他情况buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder, target);}if (md.isIgnored()) {result.put(md.configKey(), args -> {throw new IllegalStateException(md.configKey() + " is not a method handled by feign");});} else {// 使用 SynchronousMethodHandler 的 Factory 生成 SynchronousMethodHandlerresult.put(md.configKey(),factory.create(target, md, buildTemplate, options, decoder, errorDecoder));}}return result;}}
默认的 InvocationHandlerFactory 生成的 InvocationHandler 是 ReflectiveFeign.FeignInvocationHandler:
static final class Default implements InvocationHandlerFactory {@Overridepublic InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);}}
其中的内容是:
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 对于 equals,hashCode,toString 方法直接调用if ("equals".equals(method.getName())) {try {Object otherHandler =args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;return equals(otherHandler);} catch (IllegalArgumentException e) {return false;}} else if ("hashCode".equals(method.getName())) {return hashCode();} else if ("toString".equals(method.getName())) {return toString();}//对于其他方法,调用对应的 SynchronousMethodHandler 进行处理return dispatch.get(method).invoke(args);}
从这里我们就可以看出,我们生成的 Proxy,其实就是将请求代理到了 SynchronousMethodHandler 上。
我们这一节详细介绍了 OpenFeign 创建代理的详细流程,可以看出,对于同步 Feign 生成的 Proxy,其实就是将接口 HTTP 请求定义的方法请求代理到了 SynchronousMethodHandler 上。下一节我们会详细分析 SynchronousMethodHandler 做实际 HTTP 调用的流程,来搞清楚 Feign 所有组件是如何协调工作的。
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer: