所有文章
https://www.cnblogs.com/lay2017/p/11908715.html
正文
上一篇文章中,我們了解到了@FeignClient注解的接口被掃描到以后,會生成一個FeignClientFactoryBean的BeanDefinition。然后,spring將會通過調用FeignClientFactoryBean的getObject方法來獲取@FeignClient注解的接口對應的代理對象。
生成proxy對象
本文,從FeignClientFactoryBean的getObject方法開始,看看代理對象的生成。跟進getObject方法
public Object getObject() throws Exception { return getTarget(); }
繼續跟進getTarget,該方法做了一些預處理。獲取了一個上下文以及Feign的構造器,沒有URL的情況下拼接了一個
FeignContext是在FeignAutoConfiguration被解析的時候成為Bean的
<T> T getTarget() { // 獲取一個上下文 FeignContext context = this.applicationContext.getBean(FeignContext.class); // feign用於構造代理對象,builder將會構建feign Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) { // 拼接URL地址,如:http://service-provider/ if (!this.name.startsWith("http")) { this.url = "http://" + this.name; } else { this.url = this.name; } this.url += cleanPath(); return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url)); } // ... 省略 }
預處理之后,進入loadBalance方法
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { // 獲取執行http請求的客戶端 Client client = getOptional(context, Client.class); if (client != null) { builder.client(client); // 選擇獲取代理對象的實現類,默認是HystrixTargeter Targeter targeter = get(context, Targeter.class); return targeter.target(this, builder, context, target); } throw new IllegalStateException("No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?"); }
獲取代理對象的實現由Targeter的實現類處理,默認是HystrixTargeter
跟進HystrixTargeter的target方法
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } // ... return feign.target(target); }
前面說過,Feign實現了構造代理對象的過程,所以這里將會回調feign的構造過程方法
跟進feign的target方法,build將會構造出Feign對象,而newInstance會返回代理對象
public <T> T target(Target<T> target) { return build().newInstance(target); } public Feign build() { // ... return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); }
跟進newInstance方法,看看代理對象是如何被構建的
public <T> T newInstance(Target<T> target) { Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target); Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>(); List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>(); 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); // jdk的動態代理獲取的代理對象 T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }
代理對象的構建主要由三塊內容
1、構建Method到MethodHandler的映射關系,后面調用代理的對象的時候將會根據Method找到MethodHandler然后調用MethodHandler的invoke方法,而MethodHandler將包含發起http請求的實現。
2、jdk動態代理需要提供InvocationHandler,這個大家比較熟悉了。而InvocationHandler將由InvocationHandlerFactory的create方法實現
3、通過Proxy.newProxyInstance方法,生成proxy對象。
這里我們看看factory.create方法生成InvocationHandler的實現吧
static final class Default implements InvocationHandlerFactory { @Override public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) { // 這是一個內部類的實現 return new ReflectiveFeign.FeignInvocationHandler(target, dispatch); } }
調用proxy對象發起http請求
我們知道,jdk的動態代理將會調用FeignInvocationHandler的invoke方法。所以,我們看看FeignInvocationHandler是怎么調用Method的
private final Map<Method, MethodHandler> dispatch; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // ... return dispatch.get(method).invoke(args); }
前面我們說到,構建proxy對象的時候會構建Method和MethodHandler的映射關系。而這里invoke代理對象的時候又會根據method來獲取到MethodHandler,再調用其invoke方法。
MethodHandler的默認實現類是SynchronousMethodHandler,我們跟進它的invoke方法
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) { // ... } } }
熟悉的代碼來了,executeAndDecode將會負責發起http請求
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { Request request = targetRequest(template); Response response; try { // 執行http請求 response = client.execute(request, options); } catch (IOException e) { } try { //... // http請求成功 if (response.status() >= 200 && response.status() < 300) { // 無需返回值 if (void.class == metadata.returnType()) { return null; } else { // 解碼結果 Object result = decode(response); return result; } } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) { // ... } else { // ... } } catch (IOException e) { // ... } finally { // ... } }
總結
openFeign生成@FeignClient注解的接口的代理對象是從FeignClientFactoryBean的getObject方法開始的,生成proxy對象主要由ReflectiveFeign對象來實現。動態代理方法由jdk原生的動態代理支持。
調用proxy對象,其實就是發起http請求,請求結果將被解碼並返回。
所以,正如Feign本身的意義一樣,http遠程調用被偽裝成了本地調用一樣簡單的代理對象,對於使用者來說就是調用本地接口一樣簡單。