spring cloud openfeign 源碼實(shí)例解析
一、讀取注解信息
入口
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @SpringBootApplication @EnableFeignClients public class CjsPriceServiceApplication { public static void main(String[] args) { SpringApplication.run(CjsPriceServiceApplication.class, args); } }
spring boot 項目啟動后會自動掃描application上面的注解,@EnableFeignClients的注解如下
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { 。。。。 }
在注解中導(dǎo)入了 FeignClientsRegistrar類,用來像spring注冊,EnableFeignClients和FeignClient上面開發(fā)人員添加的注解信息
@Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }
二、當(dāng)項目啟動,讀取@Autowired時會調(diào)用,實(shí)現(xiàn)了FactoryBean接口的FeignClientFactoryBean.getObject()方法
@Override public Object getObject() throws Exception { return getTarget(); }
<T> T getTarget() { FeignContext context = this.applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) { 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)); } if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof LoadBalancerFeignClient) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap client = ((LoadBalancerFeignClient) client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>(this.type, this.name, url)); }
可以看到 getTarget()有兩種返回結(jié)果的情況,其原理都一樣后來調(diào)用了 targeter.target()方法
package org.springframework.cloud.openfeign; import feign.Feign; import feign.Target; /** * @author Spencer Gibb */ interface Targeter { <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target); }
默認(rèn)實(shí)現(xiàn)類
package org.springframework.cloud.openfeign; import feign.Feign; import feign.Target; /** * @author Spencer Gibb */ class DefaultTargeter implements Targeter { @Override public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget<T> target) { return feign.target(target); } }
然后再看 feign.target(target);方法
public <T> T target(Target<T> target) { return build().newInstance(target); } public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } }
build()方法返回了創(chuàng)建代理類的對象,然后調(diào)用了創(chuàng)建代理的 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); T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[] {target.type()}, handler); for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { defaultMethodHandler.bindTo(proxy); } return proxy; }
最后,當(dāng)我們項目中使用 @Autowired注入時,就回調(diào)用工廠類 FeignClientFactoryBean方法的 getObject()方法 返回我們的代理對象
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Springboot使用Maven占位符@替換不生效問題及解決
這篇文章主要介紹了Springboot使用Maven占位符@替換不生效問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-04-04Spring通過@Lazy解決構(gòu)造方法形式的循環(huán)依賴問題
這篇文章主要給大家介紹了Spring如何通過@Lazy解決構(gòu)造方法形式的循環(huán)依賴問題,文中有詳細(xì)的代碼示例,對大家的學(xué)習(xí)活工作有一定的幫助,具有一定的參考價值,需要的朋友可以參考下2023-10-10Spring activiti如何實(shí)現(xiàn)指定任務(wù)處理者
這篇文章主要介紹了Spring activiti如何實(shí)現(xiàn)指定任務(wù)處理者,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11擴(kuò)展tk.mybatis的流式查詢功能實(shí)現(xiàn)
mybatis查詢默認(rèn)是一次獲取全部,如果數(shù)據(jù)過于龐大,就會導(dǎo)致OOM問題,本文就介紹了tk.mybatis 流式查詢,具有一定的參考價值,感興趣的可以了解一下2021-12-12