feign介绍 Feign 是一款java的Restful客户端组件,Feign使得 Java HTTP 客户端编写更方便。Feign 灵感来源于Retrofit, JAXRS-2.0和WebSocket。feign在github上有近5K个star,是一款相当优秀的开源组件,虽然相比Retrofit的近30K个star,逊色了太多,但是spring cloud集成了feign,使得feign在java生态中比Retrofit使用的更加广泛。
feign的基本原理是在接口方法上加注解,定义rest请求,构造出接口的动态代理对象,然后通过调用接口方法就可以发送http请求,并且自动解析http响应为方法返回值,极大的简化了客户端调用rest api的代码。官网的示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 interface GitHub { @RequestLine("GET /repos/{owner}/{repo}/contributors") List contributors(@Param("owner") String owner, @Param("repo") String repo); } static class Contributor { String login; int contributions; } public static void main(String... args) { GitHub github = Feign.builder() .decoder(new GsonDecoder()) .target(GitHub.class, "https://api.github.com"); // Fetch and print a list of the contributors to this library. List contributors = github.contributors("OpenFeign", "feign"); for (Contributor contributor : contributors) { System.out.println(contributor.login + " (" + contributor.contributions + ")"); } }
feign使用教程请参考官网https://github.com/OpenFeign/feign/
本文主要是对feign源码进行分析,根据源码来理解feign的设计架构和内部实现技术。
Feign.build构建接口动态代理 我们先来看看接口的动态代理是如何构建出来的,下图是主要接口和类的类图:
从上文中的示例可以看到,构建的接口动态代理对象是通过Feign.builder()生成Feign.Builder的构造者对象,然后设置相关的参数,再调用target方法构造的。Feign.Builder的参数包括:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 private final List requestInterceptors = new ArrayList(); private Logger.Level logLevel = Logger.Level.NONE;private Contract contract = new Contract.Default();private Client client = new Client.Default(null , null );private Retryer retryer = new Retryer.Default();private Logger logger = new NoOpLogger();private Encoder encoder = new Encoder.Default();private Decoder decoder = new Decoder.Default();private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();private ErrorDecoder errorDecoder = new ErrorDecoder.Default();private Options options = new Options();private InvocationHandlerFactory invocationHandlerFactory = new InvocationHandlerFactory.Default();private boolean decode404;private boolean closeAfterDecode = true ;private ExceptionPropagationPolicy propagationPolicy = NONE;
这块是一个典型的构造者模式,target
方法内部先调用build
方法新建一个ReflectFeign
对象,然后调用ReflectFeign
的newInstance
方法创建动态代理,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public T target (Class apiType, String url) { return target(new HardCodedTarget(apiType, url)); } public T target (Target target) { return build().newInstance(target); } public Feign build () { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory( client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
ReflectiveFeign
构造函数有三个参数:
ParseHandlersByName
将builder所有参数进行封装,并提供解析接口方法的逻辑
InvocationHandlerFactory
java动态代理的InvocationHandler的工厂类,默认值是InvocationHandlerFactory.Default
QueryMapEncoder
接口参数注解@QueryMap
时,参数的编码器
ReflectiveFeign.newInstance
方法创建接口动态代理对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public T newInstance (Target target) { Map nameToHandler = targetToHandlersByName.apply(target); Map methodToHandler = new LinkedHashMap(); List defaultMethodHandlers = new LinkedList(); for (Method method : target.type().getMethods()) { if (method.getDeclaringClass() == Object.class ) { continue ; } else if (Util.isDefault(method)) { DefaultMethodHandler handler = new DefaultMethodHandler(method); defaultMethodHandlers.add(handler); methodToHandler.put(method, handler); } else { methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } InvocationHandler handler = factory.create(target, methodToHandler); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }
这段代码主要的逻辑是:
创建MethodHandler的映射,这里创建的是实现类SynchronousMethodHandler
通过InvocationHandlerFatory创建InvocationHandler
绑定接口的default方法,通过DefaultMethodHandler绑定
类图中已经画出,SynchronousMethodHandler
和DefaultMethodHandler
实现了InvocationHandlerFactory.MethodHandler
接口,动态代理对象调用方法时,如果是default方法,会直接调用接口方法,因为这里将接口的default方法绑定到动态代理对象上了,其他方法根据方法签名找到SynchronousMethodHandler
对象,调用其invoke方法。
Feign.configKey(Class targetType, Method method)
的实现源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static String configKey (Class targetType, Method method) { StringBuilder builder = new StringBuilder(); builder.append(targetType.getSimpleName()); builder.append('#' ).append(method.getName()).append('(' ); for (Type param : method.getGenericParameterTypes()) { param = Types.resolve(targetType, targetType, param); builder.append(Types.getRawType(param).getSimpleName()).append(',' ); } if (method.getParameterTypes().length > 0 ) { builder.deleteCharAt(builder.length() - 1 ); } return builder.append(')' ).toString(); }
Target Target
源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public interface Target <T > { Class type () ; String name () ; String url () ; public Request apply (RequestTemplate input) ; public static class HardCodedTarget <T > implements Target <T > { private final Class type; private final String name; private final String url; public HardCodedTarget (Class type, String url) { this (type, url, url); } public HardCodedTarget (Class type, String name, String url) { this .type = checkNotNull(type, "type" ); this .name = checkNotNull(emptyToNull(name), "name" ); this .url = checkNotNull(emptyToNull(url), "url" ); } @Override public Request apply (RequestTemplate input) { if (input.url().indexOf("http" ) != 0 ) { input.target(url()); } return input.request(); } } }
创建MethodHandler方法处理器 SynchronousMethodHandler是feign组件的核心,接口方法调用转换为http请求和解析http响应都是通过SynchronousMethodHandler来执行的,相关类图如下:
创建MethodHandler实现类ParseHandlersByName
的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 static final class ParseHandlersByName { private final Contract contract; private final Options options; private final Encoder encoder; private final Decoder decoder; private final ErrorDecoder errorDecoder; private final QueryMapEncoder queryMapEncoder; private final SynchronousMethodHandler.Factory factory; ParseHandlersByName( Contract contract, Options options, Encoder encoder, Decoder decoder, QueryMapEncoder queryMapEncoder, ErrorDecoder errorDecoder, SynchronousMethodHandler.Factory factory) { ...省略 } public Map apply (Target key) { List metadata = contract.parseAndValidatateMetadata(key.type()); Map result = new LinkedHashMap(); for (MethodMetadata md : metadata) { BuildTemplateByResolvingArgs buildTemplate; if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null ) { buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else if (md.bodyIndex() != null ) { buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder); } else { buildTemplate = new BuildTemplateByResolvingArgs(md, queryMapEncoder); } result.put(md.configKey(), factory.create(key, md, buildTemplate, options, decoder, errorDecoder)); } return result; } }
这段代码的逻辑是:
通过Contract
解析接口方法,生成MethodMetadata
,默认的Contract
解析Feign
自定义的http注解
根据MethodMetadata
方法元数据生成特定的RequestTemplate
的工厂
使用SynchronousMethodHandler.Factory
工厂创建SynchronousMethodHandler
这里有两个工厂不要搞混淆了,SynchronousMethodHandler
工厂和RequestTemplate```工厂,
SynchronousMethodHandler的属性包含
RequestTemplate`工厂
feign默认的解析器是Contract.Default
继承了Contract.BaseContract
,解析生成MethodMetadata
方法入口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 public interface Contract { List parseAndValidatateMetadata (Class targetType) ; abstract class BaseContract implements Contract { @Override public List parseAndValidatateMetadata (Class targetType) { checkState(targetType.getTypeParameters().length == 0 , "Parameterized types unsupported: %s" , targetType.getSimpleName()); checkState(targetType.getInterfaces().length <= 1 , "Only single inheritance supported: %s" , targetType.getSimpleName()); if (targetType.getInterfaces().length == 1 ) checkState(targetType.getInterfaces()[0 ].getInterfaces().length == 0 , "Only single-level inheritance supported: %s" , targetType.getSimpleName()); } Map result = new LinkedHashMap(); for (Method method : targetType.getMethods()) { if (method.getDeclaringClass() == Object.class || // Object 类中的方法、 (method .getModifiers () & Modifier .STATIC ) ! = 0 || Util.isDefault(method)) { continue ; } MethodMetadata metadata = parseAndValidateMetadata(targetType, method); checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s" , metadata.configKey()); result.put(metadata.configKey(), metadata); } return new ArrayList<>(result.values()); } protected MethodMetadata parseAndValidateMetadata (Class targetType, Method method) { MethodMetadata data = new MethodMetadata(); data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType())); data.configKey(Feign.configKey(targetType, method)); if (targetType.getInterfaces().length == 1 ) { processAnnotationOnClass(data, targetType.getInterfaces()[0 ]); } processAnnotationOnClass(data, targetType); for (Annotation methodAnnotation : method.getAnnotations()) { processAnnotationOnMethod(data, methodAnnotation, method); } checkState(data.template().method() != null , "Method %s not annotated with HTTP method type (ex. GET, POST)" , method.getName()); Class[] parameterTypes = method.getParameterTypes(); Type[] genericParameterTypes = method.getGenericParameterTypes(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); int count = parameterAnnotations.length; for (int i = 0 ; i < count; i++) { boolean isHttpAnnotation = false ; if (parameterAnnotations[i] != null ) { isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i); } if (parameterTypes[i] == URI.class ) { data.urlIndex(i); } else if (!isHttpAnnotation && parameterTypes[i] != Request.Options.class ) { checkState(data.formParams().isEmpty(), "Body parameters cannot be used with form parameters." ); checkState(data.bodyIndex() == null , "Method has too many Body parameters: %s" , method); data.bodyIndex(i); data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i])); } } if (data.headerMapIndex() != null ) { checkMapString("HeaderMap" , parameterTypes[data.headerMapIndex()], genericParameterTypes[data.headerMapIndex()]); } if (data.queryMapIndex() != null ) { if (Map.class.isAssignableFrom(parameterTypes[data.queryMapIndex()])) { checkMapKeys("QueryMap" , genericParameterTypes[data.queryMapIndex()]); } } return data; } protected abstract void processAnnotationOnMethod (MethodMetadata data, Annotation annotation, Method method) ; protected abstract boolean processAnnotationsOnParameter (MethodMetadata data, Annotation[] annotations, int paramIndex) ; protected void nameParam (MethodMetadata data, String name, int i) { Collection names = data.indexToName().containsKey(i) ? data.indexToName().get(i) : new ArrayList(); names.add(name); data.indexToName().put(i, names); } } class Default extends BaseContract { @Override protected void processAnnotationOnClass (MethodMetadata data, Class targetType) { if (targetType.isAnnotationPresent(Headers.class )) { String[] headersOnType = targetType.getAnnotation(Headers.class ).value () ; checkState(headersOnType.length > 0 , "Headers annotation was empty on type %s." , targetType.getName()); Map> headers = toMap(headersOnType); headers.putAll(data.template().headers()); data.template().headers(null ); data.template().headers(headers); } } @Override protected void processAnnotationOnMethod (MethodMetadata data, Annotation methodAnnotation, Method method) { Class annotationType = methodAnnotation.annotationType(); if (annotationType == RequestLine.class ) { String requestLine = RequestLine.class .cast (methodAnnotation ).value () ; checkState(emptyToNull(requestLine) != null , "RequestLine annotation was empty on method %s." , method.getName()); Matcher requestLineMatcher = REQUEST_LINE_PATTERN.matcher(requestLine); if (!requestLineMatcher.find()) { throw new IllegalStateException(String.format( "RequestLine annotation didn't start with an HTTP verb on method %s" , method.getName())); } else { data.template().method(HttpMethod.valueOf(requestLineMatcher.group(1 ))); data.template().uri(requestLineMatcher.group(2 )); } data.template().decodeSlash(RequestLine.class .cast (methodAnnotation ).decodeSlash ()) ; data.template().collectionFormat(RequestLine.class .cast (methodAnnotation ).collectionFormat ()) ; } else if (annotationType == Body.class ) { String body = Body.class .cast (methodAnnotation ).value () ; checkState(emptyToNull(body) != null , "Body annotation was empty on method %s." , method.getName()); if (body.indexOf('{' ) == -1 ) { data.template().body(body); } else { data.template().bodyTemplate(body); } } else if (annotationType == Headers.class ) { String[] headersOnMethod = Headers.class .cast (methodAnnotation ).value () ; checkState(headersOnMethod.length > 0 , "Headers annotation was empty on method %s." , method.getName()); data.template().headers(toMap(headersOnMethod)); } } @Override protected boolean processAnnotationsOnParameter (MethodMetadata data, Annotation[] annotations, int paramIndex) { boolean isHttpAnnotation = false ; for (Annotation annotation : annotations) { Class annotationType = annotation.annotationType(); if (annotationType == Param.class ) { Param paramAnnotation = (Param) annotation; String name = paramAnnotation.value(); checkState(emptyToNull(name) != null , "Param annotation was empty on param %s." , paramIndex); nameParam(data, name, paramIndex); Class expander = paramAnnotation.expander(); if (expander != Param.ToStringExpander.class ) { data.indexToExpanderClass().put(paramIndex, expander); } data.indexToEncoded().put(paramIndex, paramAnnotation.encoded()); isHttpAnnotation = true ; if (!data.template().hasRequestVariable(name)) { data.formParams().add(name); } } else if (annotationType == QueryMap.class ) { checkState(data.queryMapIndex() == null , "QueryMap annotation was present on multiple parameters." ); data.queryMapIndex(paramIndex); data.queryMapEncoded(QueryMap.class .cast (annotation ).encoded ()) ; isHttpAnnotation = true ; } else if (annotationType == HeaderMap.class ) { checkState(data.headerMapIndex() == null , "HeaderMap annotation was present on multiple parameters." ); data.headerMapIndex(paramIndex); isHttpAnnotation = true ; } } return isHttpAnnotation; } private static Map> toMap(String[] input) { Map> result = new LinkedHashMap>(input.length); for (String header : input) { int colon = header.indexOf(':' ); String name = header.substring(0 , colon); if (!result.containsKey(name)) { result.put(name, new ArrayList(1 )); } result.get(name).add(header.substring(colon + 1 ).trim()); } return result; } } }
代码稍微有点多,但是逻辑很清晰,先处理类上的注解,再处理方法上注解,最后处理方法参数注解,把所有注解的情况都处理到就可以了。
生成的MethodMetadata的结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public final class MethodMetadata implements Serializable { private String configKey; private transient Type returnType; private Integer urlIndex; private Integer bodyIndex; private Integer headerMapIndex; private Integer queryMapIndex; private boolean queryMapEncoded; private transient Type bodyType; private RequestTemplate template = new RequestTemplate(); private List formParams = new ArrayList(); private Map> indexToName ; private Map> indexToExpanderClass ; private Map indexToEncoded ; private transient Map indexToExpander; ...省略部分代码 }
Contract
也是feign的一个扩展点,一个优秀组件的架构通常是具有很强的扩展性,feign的架构本身很简单,设计的扩展点也很简单方便,所以受到spring的青睐,将其集成到spring cloud中。spring cloud就是通过Contract
的扩展(org.springframework.cloud.openfeign.support.SpringMvcContract
),实现使用springMVC的注解接入feign。feign自己还实现了使用jaxrs注解接入feign。
初始化总结 上文已经完成了feign初始化结构为动态代理的整个过程,简单的捋一遍:
初始化Feign.Builder
传入参数,构造ReflectiveFeign
ReflectiveFeign
通过内部类ParseHandlersByName
的Contract
属性,解析接口生成MethodMetadata
ParseHandlersByName
根据MethodMetadata
生成RequestTemplate
工厂
ParseHandlersByName
创建SynchronousMethodHandler
,传入MethodMetadata
、RequestTemplate
工厂和Feign.Builder
相关参数
ReflectiveFeign
创建FeignInvocationHandler
,传入参数SynchronousMethodHandler
,绑定DefaultMethodHandler
ReflectiveFeign
根据FeignInvocationHandler
创建Proxy
关键的几个类是:
ReflectiveFeign
初始化入口
FeignInvocationHandler
实现动态代理的InvocHandler
SynchronousMethodHandler
方法处理器,方法调用处理器
MethodMetadata
方法元数据
Contract.Default
契约解析默认实现
接口调用 为方便理解,分析完feign源码后,我将feign执行过程分成三层,如下图:
三层分别为:
代理层 jdk动态代理调用层
转换层 方法转http请求,解码http响应
网络层 http请求发送
java动态代理接口方法调用,会调用到InvocaHandler的invoke方法,feign里面实现类是FeignInvocationHandler,invoke代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ReflectiveFeign extends Feign { ...省略部分代码 static class FeignInvocationHandler implements InvocationHandler { private final Target target; private final Map dispatch; FeignInvocationHandler(Target target, Map dispatch) { this .target = checkNotNull(target, "target" ); this .dispatch = checkNotNull(dispatch, "dispatch for %s" , target); } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { ...省略 部分代码 return dispatch.get(method).invoke(args); } } }
根据方法找到MethodHandler
,除接口的default
方法外(default
方法直接调用,无需feign处理),找到的是SynchronousMethodHandler
对象,然后调用SynchronousMethodHandlerd.invoke
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 final class SynchronousMethodHandler implements MethodHandler { private final MethodMetadata metadata; private final Target target; private final Client client; private final Retryer retryer; private final List requestInterceptors; private final Logger logger; private final Logger.Level logLevel; private final RequestTemplate.Factory buildTemplateFromArgs; private final Options options; private final Decoder decoder; private final ErrorDecoder errorDecoder; private final boolean decode404; private final boolean closeAfterDecode; private final ExceptionPropagationPolicy propagationPolicy; @Override public Object invoke (Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); Options options = findOptions(argv); Retryer retryer = this .retryer.clone(); while (true ) { try { return executeAndDecode(template, options); } catch (RetryableException e) { try { retryer.continueOrPropagate(e); } catch (RetryableException th) { Throwable cause = th.getCause(); if (propagationPolicy == UNWRAP && cause != null ) { throw cause; } else { throw th; } } if (logLevel != Logger.Level.NONE) { logger.logRetry(metadata.configKey(), logLevel); } continue ; } } } Object executeAndDecode (RequestTemplate template, Options options) throws Throwable { Request request = targetRequest(template); ...省略部分代码 Response response; long start = System.nanoTime(); try { response = client.execute(request, options); } catch (IOException e) { ...省略部分代码 throw errorExecuting(request, e); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); boolean shouldClose = true ; try { ...省略部分代码 if (Response.class == metadata.returnType()) { if (response.body() == null ) { return response; } if (response.body().length() == null || response.body().length() > MAX_RESPONSE_BUFFER_SIZE) { shouldClose = false ; return response; } byte [] bodyData = Util.toByteArray(response.body().asInputStream()); return response.toBuilder().body(bodyData).build(); } if (response.status() >= 200 && response.status() < 300 ) { if (void .class == metadata.returnType()) { return null ; } else { Object result = decode(response); shouldClose = closeAfterDecode; return result; } } else if (decode404 && response.status() == 404 && void .class ! = metadata.returnType()) { Object result = decode(response); shouldClose = closeAfterDecode; return result; } else { throw errorDecoder.decode(metadata.configKey(), response); } } catch (IOException e) { ...省略部分代码 throw errorReading(request, response, e); } finally { if (shouldClose) { ensureClosed(response.body()); } } } Request targetRequest (RequestTemplate template) { for (RequestInterceptor interceptor : requestInterceptors) { interceptor.apply(template); } return target.apply(template); } }
过程比较简单,生成RquestTemplate -> 转换为Request -> client发请求 -> Decoder解析Response
RquestTemplate构建过程 先看看RequestTemplate的结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public final class RequestTemplate implements Serializable { private static final Pattern QUERY_STRING_PATTERN = Pattern.compile("(?); private final Map queries = new LinkedHashMap<>(); private final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String target; private String fragment; private boolean resolved = false ; private UriTemplate uriTemplate; private HttpMethod method; private transient Charset charset = Util.UTF_8; private Request.Body body = Request.Body.empty(); private boolean decodeSlash = true ; private CollectionFormat collectionFormat = CollectionFormat.EXPLODED; }
在SynchronousMethodHandler.invoke
方法中生成RequestTemplate
1 2 RequestTemplate template = buildTemplateFromArgs.create(argv);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 - ```BuildTemplateByResolvingArgs``` ```RequestTemplate```工厂 - ```BuildEncodedTemplateFromArgs``` ```BuildTemplateByResolvingArgs```的子类 重载```resolve```方法,解析form表单请求 - ```BuildFormEncodedTemplateFromArgs``` ```BuildTemplateByResolvingArgs```的子类,重载```resolve```方法,解析body请求 ---- - **BuildTemplateByResolvingArgs的实现** 默认的RequestTemplate的工厂,没有请求体,不需要编码器 ```java private static class BuildTemplateByResolvingArgs implements RequestTemplate.Factory { // @QueryMap注解的参数的编码器 private final QueryMapEncoder queryMapEncoder; // 方法的元数据 protected final MethodMetadata metadata; // @Param注解的expander属性,用于参数转换 private final Map indexToExpander = new LinkedHashMap(); /** * @params argv 调用方法时传递的参数列表 */ @Override public RequestTemplate create(Object[] argv) { RequestTemplate mutable = RequestTemplate.from(metadata.template()); // 方法中有java.net.URI类型的参数, 例如void list(String username, URI url, String queryKey) if (metadata.urlIndex() != null) { // 获取URI类型的参数在参数列表中的位置 int urlIndex = metadata.urlIndex(); // URI类型的参数的实际值不为null checkArgument(argv[urlIndex] != null, "URI parameter %s was null", urlIndex); // 使用指定的参数的地址作为请求的基础地址 //(不使用Feign.builder().target(Api.class, 'http://localhost:8080'))中的url地址 mutable.target(String.valueOf(argv[urlIndex])); } // 存储 @Param注解value值 --> 参数实际值 的映射关系,用于后续表达式{expression}的求值 Map varBuilder = new LinkedHashMap(); // indexToName属性是解析@Param注解产生的, // key为@Param注解的参数在参数列表中的索引 // value为@Param注解的value属性 for (Entry> entry : metadata.indexToName().entrySet()) { // 获得@Param主键的参数索引 int i = entry.getKey(); // 该位置参数的实际调用时传递的值 Object value = argv[entry.getKey()]; if (value != null) { // 跳过null值 // @Param注解是否有expander属性,即参数值是否需要转换处理(如Date -> String、Enum -> int等等) if (indexToExpander.containsKey(i)) { // 使用expander属性指定的class来转换输入值为另一个值 //(例如传入Date类型的参数,实际请求时需要的是yyyy-MM-dd这种格式的字符串, // 那么就需要写一个DateFormatExpander类来进行转换参数) value = expandElements(indexToExpander.get(i), value); } for (String name : entry.getValue()) { // 存储@Param注解value值 --> 参数实际值 的映射关系,用于后续表达式{expression}的求值 varBuilder.put(name, value); } } } //解析RequestTemplate RequestTemplate template = resolve(argv, mutable, varBuilder); // 为什么单独把queryMap放在这里解析,而不是在resolve方法中,或者在RequestTemplate中?(该问题来源于拍拍贷的博客内容) // 因为@QueryMap注解的参数不需要解析表达式 if (metadata.queryMapIndex() != null) { //判断是否有@QueryMap注解的参数 // 获取@QueryMap注解的参数的实际值 Object value = argv[metadata.queryMapIndex()]; // value可能是Map或者java bean对象,如果参数不是Map类型的则用queryMapEncoder进行编码 Map queryMap = toQueryMap(value); // 合并已有的查询参数和queryMap的参数,追加到RequestTemplate的queries属性中 template = addQueryMapQueryParameters(queryMap, template); } // 为什么单独把headerMap放在这里解析,而不是在resolve方法中,或者在RequestTemplate中? // 因为@HeaderMap注解的参数不需要解析表达式 // 参数中是否有@HeaderMap注解的参数 if (metadata.headerMapIndex() != null) { // 将@HeaderMap注解的参数的实际值与已有的(@Headers的)header参数合并, // 追加到RequestTemplate的headers属性中 template = addHeaderMapHeaders((Map) argv[metadata.headerMapIndex()], template); } return template; } /** * 注解的表达书求值 * * @params variables 指的是@Param注解value值 --> 参数实际值 的映射关系,用于后续表达式{expression}的求值 */ protected RequestTemplate resolve(Object[] argv, RequestTemplate mutable, Map variables) { // 由RequestTemplate.resolve(Map)实现,真正的去解析注解中的表达式{expression} return mutable.resolve(variables); } }
BuildFormEncodedTemplateFromArgs的实现
如果有formParam,并且bodyTemplate不为空,请求体为x-www-form-urlencoded格式
将会解析form参数,填充到bodyTemplate中
该类用于对参数列表中的form参数(未被表达式使用 的@Param
参数)使用encoder编码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 private static class BuildFormEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { private final Encoder encoder; ...省略部分代码 @Override protected RequestTemplate resolve (Object[] argv, RequestTemplate mutable, Map variables) { Map formVariables = new LinkedHashMap(); for (Entry entry : variables.entrySet()) { if (metadata.formParams().contains(entry.getKey())) { formVariables.put(entry.getKey(), entry.getValue()); } } try { encoder.encode(formVariables, Encoder.MAP_STRING_WILDCARD, mutable); } catch (EncodeException e) { throw e; } catch (RuntimeException e) { throw new EncodeException(e.getMessage(), e); } return super .resolve(argv, mutable, variables); } }
BuildEncodedTemplateFromArgs的实现
如果包含请求体,将会用encoder编码请求体对象(参数列表中的参数无任何注解 会作为请求体)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static class BuildEncodedTemplateFromArgs extends BuildTemplateByResolvingArgs { private final Encoder encoder; ...省略部分代码 @Override protected RequestTemplate resolve (Object[] argv, RequestTemplate mutable, Map variables) { Object body = argv[metadata.bodyIndex()]; checkArgument(body != null , "Body parameter %s was null" , metadata.bodyIndex()); try { encoder.encode(body, metadata.bodyType(), mutable); } catch (EncodeException e) { throw e; } catch (RuntimeException e) { throw new EncodeException(e.getMessage(), e); } return super .resolve(argv, mutable, variables); } }
RequestTemplate解析参数的方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 public final class RequestTemplate implements Serializable { private static final Pattern QUERY_STRING_PATTERN = Pattern.compile("(?); private final Map queries = new LinkedHashMap<>(); private final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String target; private String fragment; private boolean resolved = false ; private UriTemplate uriTemplate; private HttpMethod method; private transient Charset charset = Util.UTF_8; private Request.Body body = Request.Body.empty(); private boolean decodeSlash = true ; private CollectionFormat collectionFormat = CollectionFormat.EXPLODED; public RequestTemplate resolve (Map variables) { StringBuilder uri = new StringBuilder(); RequestTemplate resolved = RequestTemplate.from(this ); if (this .uriTemplate == null ) { this .uriTemplate = UriTemplate.create("" , !this .decodeSlash, this .charset); } uri.append(this .uriTemplate.expand(variables)); if (!this .queries.isEmpty()) { resolved.queries(Collections.emptyMap()); StringBuilder query = new StringBuilder(); Iterator queryTemplates = this .queries.values().iterator(); while (queryTemplates.hasNext()) { QueryTemplate queryTemplate = queryTemplates.next(); String queryExpanded = queryTemplate.expand(variables); if (Util.isNotBlank(queryExpanded)) { query.append(queryExpanded); if (queryTemplates.hasNext()) { query.append("&" ); } } } String queryString = query.toString(); if (!queryString.isEmpty()) { Matcher queryMatcher = QUERY_STRING_PATTERN.matcher(uri); if (queryMatcher.find()) { uri.append("&" ); } . else { uri.append("?" ); } uri.append(queryString); } } resolved.uri(uri.toString()); if (!this .headers.isEmpty()) { resolved.headers(Collections.emptyMap()); for (HeaderTemplate headerTemplate : this .headers.values()) { String header = headerTemplate.expand(variables); if (!header.isEmpty()) { String headerValues = header.substring(header.indexOf(" " ) + 1 ); if (!headerValues.isEmpty()) { resolved.header(headerTemplate.getName(), headerValues); } } } } resolved.body(this .body.expand(variables)); resolved.resolved = true ; return resolved; } public static RequestTemplate from (RequestTemplate requestTemplate) { RequestTemplate template = new RequestTemplate(requestTemplate.target, requestTemplate.fragment, requestTemplate.uriTemplate, requestTemplate.method, requestTemplate.charset, requestTemplate.body, requestTemplate.decodeSlash, requestTemplate.collectionFormat); if (!requestTemplate.queries().isEmpty()) { template.queries.putAll(requestTemplate.queries); } if (!requestTemplate.headers().isEmpty()) { template.headers.putAll(requestTemplate.headers); } return template; } }
我们回到SynchronousMethodHandler的invoke方法继续看executeAndDecode的实现
1 2 3 4 5 6 7 @Override public Object invoke (Object[] argv) throws Throwable { RequestTemplate template = buildTemplateFromArgs.create(argv); ...省略 return executeAndDecode(template, options);}
RquestTemplate转换Request 先来看看Request的结构,完整的http请求信息的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public final class Request { private final HttpMethod httpMethod; private final String url; private final Map> headers; private final Body body; public static class Body { private final byte [] data; private final Charset encoding; private final BodyTemplate bodyTemplate; } Request(HttpMethod method, String url, Map> headers, Body body) { this .httpMethod = checkNotNull(method, "httpMethod of %s" , method.name()); this .url = checkNotNull(url, "url" ); this .headers = checkNotNull(headers, "headers of %s %s" , method, url); this .body = body; } }
SynchronousMethodHandler的targetRequest方法将RequestTemplate转换为Request
1 2 3 4 5 6 7 8 Request targetRequest (RequestTemplate template) { for (RequestInterceptor interceptor : requestInterceptors) { interceptor.apply(template); } return target.apply(template); }
这块先应用所有拦截器,然后target的apply方法。拦截器和target都是扩展点,拦截器可以在构造好RequestTemplate后和发请求前修改请求信息,target默认使用HardCodedTarget直接发请求,feign还提供了LoadBalancingTarget,适配Ribbon来发请求,实现客户端的负载均衡。
http请求发送 SynchronousMethodHandler中构造好Request后,直接调用client的execute方法发送请求:
1 2 3 4 5 6 7 Object executeAndDecode (RequestTemplate template, Options options) throws Throwable { ...省略部分代码 response = client.execute(request, options); ...省略部分代码 }
client是一个Client
接口,默认实现类是Client.Default
,使用java api中的HttpURLConnection
发送http请求。feign还实现了:
feign.Client.Proxied
ApacheHttpClient
OkHttpClient
LoadBalancerFeignClient
FeignBlockingLoadBalancerClient
接口调用过程总结 我们再将接口调用过程捋一遍:
1、接口的动态代理Proxy
调用接口方法会执行的FeignInvocationHandler
2、FeignInvocationHandler
通过方法签名在属性Map dispatch
中找到SynchronousMethodHandler
,调用invoke
方法 3、SynchronousMethodHandler
的invoke
方法根据传入的方法参数,通过自身属性工厂对象RequestTemplate.Factory
创建RequestTemplate
,工厂里面会用根据需要进行Encode
4、SynchronousMethodHandler
遍历自身属性RequestInterceptor
列表,对RequestTemplate
进行改造 4、SynchronousMethodHandler
调用自身Target
属性的apply
方法,将RequestTemplate
转换为Request
对象 5、SynchronousMethodHandler
调用自身Client
的execute
方法,传入Request
对象 6、Client
将Request
转换为http请求,发送后将http响应转换为Response
对象 7、SynchronousMethodHandler
调用Decoder
的方法对Response
对象解码后返回 8、返回的对象最后返回到Proxy
时序图如下:
feign扩展点总结 前文分析源代码时,已经提到了feign的扩展点,最后我们再将feign的主要扩展点进行总结一下:
Contract 契约
1 2 3 4 5 6 7 8 9 10 11 12 13 - ```feign.Contract.Default``` feign默认实现 - ```JAXRSContract``` javax.ws.rs注解接口实现 - ```SpringMvcContract```是spring cloud提供SpringMVC注解实现方式。 - ```HystrixDelegatingContract``` hyxtrix注解的实现 - **InvocationHandler** 动态代理handler 通过```InvocationHandlerFactory```注入到```Feign.Builder```中,feign提供了Hystrix的扩展,实现Hystrix接入 - **Encoder** 请求body编码器 feign已经提供扩展包含: - 默认编码器,只能处理String和byte[] - json编码器```GsonEncoder```、```JacksonEncoder
XML编码器JAXBEncoder
FormEncoder
SpringEncoder
SpringFormEncoder
PageableSpringEncoder
Decoder http响应解码器 最基本的有:
feign默认的 StringDecoder
、OptionalDecoder
、feign.codec.Decoder.Default
、feign.Feign.ResponseMappingDecoder
json解码器 GsonDecoder
、JacksonDecoder
、JacksonIteratorDecoder
XML解码器 JAXBDecoder
Stream流解码器 StreamDecoder
spring的 SpringDecoder
、ResponseEntityDecoder
ErrorDecoder 错误解码器
Target 请求转换器 feign提供的实现有:
HardCodedTarget
默认Target,不做任何处理。
EmptyTarget
feign提供的
Client 发送http请求的客户端 feign提供的Client实现有:
Client.Default
默认实现,使用java api的HttpClientConnection
发送http请求
ApacheHttpClient
使用apache的Http客户端发送请求
OkHttpClient
使用OKHttp客户端发送请求
LoadBalancerFeignClient
spring-cloud-openfeign的通过负载均衡选择服务发送请求
RequestInterceptor 请求拦截器 调用客户端发请求前,修改RequestTemplate
,比如为所有请求添加Header就可以用拦截器实现。
BaseRequestInterceptor
BasicAuthRequestInterceptor
FeignAcceptGzipEncodingInterceptor
FeignContentGzipEncodingInterceptor
Retryer 重试策略 默认的策略是Retryer.Default
,包含3个参数:
间隔、
最大间隔
重试次数,第一次失败重试前会sleep输入的间隔时间的,后面每次重试sleep时间是前一次的1.5倍,超过最大时间或者最大重试次数就失败
文章绝大部分内容来源于:http://techblog.ppdai.com/2018/05/14/20180514/ 本人在其基础上加入自己的理解