Feign底层原理剖析 (feign底层)

Feign底层原理剖析 (feign底层)

本文目录导航:

Feign底层原理剖析-智能装载&灵活代理

本篇文章仅引见Feign的智能装载与灵活代理机制等外容,不会过火深化细节。

1、什么是Feign?

这里套用Feign官网Github上的引见:“Feign是一个灵感来自于Retrofit、JAXRS-2.0、WebSocket的Java Http客户端,Feign的重要目的是降落大家经常使用Http API的复杂性”。

其实,Feign底层依赖于Java的灵活代理机制,对原生Java Socket或许Apache HttpClient启动封装,成功了基于Http协定的远程环节调用。

当然,Feign还在此基础上成功了负载平衡、熔断等机制。

2、为什么要经常使用Feign?

申明式Http Client相关于编程式Http Client代码逻辑愈加繁复,不须要处置复杂的编码恳求和照应,只须要像调用本地方法即可,提高编码效率

集中治理Http恳求方法,代码边界愈加明晰

更好的集成负载平衡、熔断升级等性能

3、Feign依赖注入原理

经常使用过Feign的同窗都知道,@EnableFeignClients注解是开启Fiegn性能的关键,咱们理论会在该注解中参与FeignClient的所在包,以便Spring容器能够扫描到一切的FeignClient,并启动托管。

前面咱们便可以经常使用@Autowired注解智能导入了。

@SpringBootApplication@EnableFeignClients(basePackages = {com.**})public class Application {}

该注解样式也是很多第三方包集成Springboot所经常使用的套路:普通都是开启该注解后,Springboot便可以智能装载第三方包所指定的Class,咱们便可以间接经常使用第三方包所提供的性能,十分繁难。

接上去不会详细引见智能装载的局部,而是间接给出智能装载的主头绪,看看Spring容器究竟装载了什么bean。

3.1、Feign智能装载

首先进入@EnableFeignClients源码中,检查该注解导入了什么Registrar注册器,这个注册器便是智能装载的关键。

@Retention()@Target()@Documented@Import()public @interface EnableFeignClients {...}

从源码中可以看出,@EnableFeignClients注解导入的是自定义的FeignClientsRegistrar类。

这种类型的注册器普通会承袭Spring中的ImportBeanDefinitionRegistrar接口,并在registerBeanDefinitions成功方法中向Spring容器注册一些bean,以到达智能注入第三方性能的目的。

// [1] 承袭ResourceLoaderAware和EnvironmentAwareclass FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {// [2] 注册自动的Feign性能registerDefaultConfiguration(metadata, registry);// [3] 注册一切定义的FeignClientregisterFeignClients(metadata, registry);}}

[1] 从类的定义中,咱们可以发现还成功了ResourceLoaderAware、EnvironmentAware两个Spring钩子接口,那么该注册类肯定持有资源加载器和Spring的环境变量等消息,这个不过多叙说。

[2] 该方法会从@EnableFeignClients注解中提取defaultConfiguration这个key和对应的value,并把它当作自动的Feign性能注册到Spring容器中。

假设没有该key,则不做任何处置。

[3] registerFeignClients方法会扫描@EnableFeignClients注解的basePackages,注册一切的FeignClient,上方详细引见。

public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();Map<String, Object> attrs = (());// [1] 失掉clients属性final Class<?>[] clients = attrs == null ? null : (Class<?>[]) (clients);if (clients == null || == 0) {// [2] 假设clients属性为null,则失掉basePackages属性,扫描其中的一切clientClassPathScanningCandidateComponentProvider scanner = getScanner();();(new AnnotationTypeFilter());Set<String> basePackages = getBasePackages(metadata);for (String basePackage : basePackages) {((basePackage));}} else {// [3] 假设clients属性不为null,则间接注入for (Class<?> clazz : clients) {(new AnnotatedGenericBeanDefinition(clazz));}}// [4] 遍历一切的clients,将其封装为BeanDefinition注册进Spring容器中for (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;AnnotationMetadata annotationMetadata = ();((), @FeignClient can only be specified on an interface);Map<String, Object> attributes = (());String name = getClientName(attributes);registerClientConfiguration(registry, name, (configuration));registerFeignClient(registry, annotationMetadata, attributes);}}}

registerFeignClients方法的代码稍微有点多,但重要思绪就是解析@EnableFeignClients注解,依据解析消息失掉FeignClients,而后包装成BeanDefinition注册进Spring容器中。详细分为以下几步:

[1] 首先解析@EnableFeignClients注解中的clients消息,假设存在,说明开发人员间接指定了FeignClient的全门路,因此只需加载这些全门路的class即可。

假设未指定,则经过扫包的模式加载。

[2] 假设clients属性为null,则创立一个扫描器Scanner,并指定要扫描的类肯定有FeignClient注解,而后经过getBasePackages()方法从@EnableFeignClients注解中失掉basePackages消息,最后遍历一切待扫描的包,将扫描到的FeignClient类参与candidateComponents中,待后续加载进容器。

[3] 假设clients属性不为null,上方也说了,会间接注入

[4] 到了这里,一切的FeignClient都被扫描到并且封装成BeanDefinition,接上去会遍历这些BeanDefinition,而后在()方法中判别这些BeanDefinition能否为接口,由于@FeignClient注解只能经常使用在接口上。

校验完后咱们应该关注registerFeignClient()这个真正注册FeignClient的方法。

private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,Map<String, Object> attributes) {String className = ();Class clazz = (className, null);ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory? (ConfigurableBeanFactory) registry : null;String contextId = getContextId(beanFactory, attributes);String name = getName(attributes);FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();(beanFactory);(name);(contextId);(clazz);BeanDefinitionBuilder definition = (clazz, () -> {(getUrl(beanFactory, attributes));(getPath(beanFactory, attributes));404((((decode404))));Object fallback = (fallback);if (fallback != null) {(fallback instanceof Class ? (Class<?>) fallback: ((), null));}Object fallbackFactory = (fallbackFactory);if (fallbackFactory != null) {(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory: ((), null));}return ();});(_BY_TYPE);(true);validate(attributes);AbstractBeanDefinition beanDefinition = ();(_TYPE_ATTRIBUTE, className);(feignClientsRegistrarFactoryBean, factoryBean);// has a default, wont be nullboolean primary = (Boolean) (primary);(primary);String[] qualifiers = getQualifiers(attributes);if ((qualifiers)) {qualifiers = new String[] { contextId + FeignClient };}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);(holder, registry);}

registerFeignClient()是对单个FeignClient注册的方法。

这个方法乍一看很长,但其实主线十分明晰。

就是将扫描到的BeanDefinition中的元消息提取进去,而后构形成一个Feign自定义的FactoryBean,即FeignClientFactoryBean,前面咱们每次失掉容器中的FeignClient时,就会经过该FactoryBean的getObject()方法中失掉(这个触及到了Spring容器中普通bean和FactoryBean的区别,大家可以自行去了解下)。

方法中的其它局部都是为上方所说的逻辑服务,包括FactoryBean的结构,BeanDefinition注入到容器中等环节。

咱们不用太过关心,只需抓住重点即可。

讲到这里,其实智能装载环节曾经成功了,容器中曾经蕴含了自定义的FeignClientFactoryBean。

这里用一张流程图总结下智能装载的全环节。

然而咱们临时还不知道智能装载的FeignClientFactoryBean究竟做了什么。

上方咱们就深化去了解。

3.2、FeignClientFactoryBean#getObject

FeignClientFactoryBean的源码如下:

public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {@Overridepublic Object getObject() {// [1] 真正的失掉对象的方法委托给getTarget()方法了return getTarget();}<T> T getTarget() {// 失掉Feign的高低文FeignContext context = beanFactory != null ? (): (); builder = feign(context);if (!(url)) {// [2] 假设url没有定义,则进入到该判别中创立对象,该判别中创立的对象具备负载平衡性能if (!(http)) {url = http:// + name;}else {url = name;}url += cleanPath();return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));}if ((url) && !(http)) {url = http:// + url;}// [3] 假设url定义了,说明用户指定了某台机器,也就没有必要启动负载平衡了,则从上方的方法创立对象String url = + cleanPath();// 可以发现上方的口头逻辑和loadBalance()十分像,只是多了两个if判别,这两个if判别就是移除负载平衡的关键Client client = getOptional(context, );if (client != null) {if (client instanceof FeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((FeignBlockingLoadBalancerClient) client)();}if (client instanceof RetryableFeignBlockingLoadBalancerClient) {// not load balancing because we have a url,// but Spring Cloud LoadBalancer is on the classpath, so unwrapclient = ((RetryableFeignBlockingLoadBalancerClient) client)();}(client);}Targeter targeter = get(context, );return (T) (this, builder, context, new HardCodedTarget<>(type, name, url));}}// 创立负载平衡的clientprotected <T> T loadBalance( builder, FeignContext context, HardCodedTarget<T> target) {Client client = getOptional(context, );if (client != null) {(client);Targeter targeter = get(context, );return (this, builder, context, target);}throw new IllegalStateException(No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?);}

getObject()会依据用户定义的FeignClient能否认义url属性选择能否前往具备负载平衡属性的对象。详细环节如下:

[1] 重写的方法委托给getTarget()口头了

[2] 假设没有定义url,则间接经过loadBalance()方法创立代理对象。

此时会先冷静器中拿到Client对象,而后经常使用()方法创立Client的代理对象。

留意,咱们最终经常使用的FeignClient是Client的灵活代理对象,而Client对象是真正口头http恳求的对象。

Client普通是httpClient或许是Feign自定义的具备LoadBalance性能的LoadBalancerClient。

前者很好了解,可以间接以为是Apache HttpClient;后者是Feign在Apache HttpClient的基础上封装了Spring Cloud Loadbalancer一系列对象,而Apache HttpClient作为被封装的delegate,在delegate真正口头http恳求时同时启动Loadbalancer的负载平衡逻辑。

[3] 假设url曾经定义了,说明用户指定了详细某台机器,此时曾经没有必要启动负载平衡了(

第4章:让Feign协助你轻松成功微服务间的调用

永恒学习、乐于分享、独特生长!在名目中,远程接口调用是如何成功的呢?以下是咱们了解的几种模式:HttpClient是Apache Jakarta Common下的子名目,提供了高效的、最新的、性能丰盛的允许Http协定的客户端编程工具包,允许HTTP协定最新版本和倡导。

HttpClient相比传统JDK自带的URLConnection,优化了易用性和灵敏性,使客户端发送HTTP恳求变得容易,提高了开发的效率。

HttpURLConnection是Java的规范类,承袭自URLConnection,可用于向指定网站发送GET、POST恳求。

HttpURLConnection经常使用比拟复杂,不像HttpClient那样容易经常使用。

OkHttp是一个处置网络恳求的开源名目,是安卓端最火的轻量级框架,由Square公司奉献,用于代替HttpUrlConnection和Apache HttpClient。

OkHttp领有繁复的API、高效的性能,并允许多种协定(HTTP/2 和 SPDY)。

RestTemplate是Spring提供的用于访问Rest服务的客户端,提供了多种方便访问远程HTTP服务的方法,能够大大提高客户端的编写效率。

什么是Feign?在接触Feign之前,咱们最罕用到的是上方提到的几种HTTP编程工具包,而Feign也是一个HTTP编程工具包,它的经常使用方法愈加简双繁难。

Feign是Netflix开发的申明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS-2.0以及WebSocket。

Feign可协助咱们愈加方便、优雅地调用HTTP API。

在前面两个章节中,咱们在服务间启动调用是怎么的呢?经过RestTemplate联合Ribbon负载平衡器对远程服务启动调用,代码如下:Feign的长处:Feign可以做到经常使用HTTP恳求远程服务时就像调用本地方法一样的体验,开发者齐全感知不到这是远程方法,更感知不到这是个HTTP恳求。

它像Dubbo一样,consumer间接调用接口方法调用provider,而不须要经过惯例的Http Client结构恳求再解析前往数据。

它处置了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注散布式环境开发。

经常使用Feign的调用模式:Spring Cloud openfeign对Feign启动了增强,使其允许SpringMVC注解,另外还整合了Ribbon和Nacos,从而使得Feign的经常使用愈加繁难。

极速开局:经常使用Spring Cloud Alibaba极速整合OpenFeign,咱们以step02工程作为服务提供者,新建一个step04工程测实验证经常使用Feign。

Feign罕用的自定义性能及经常使用:Feign提供了很多的裁减机制,让用户可以愈加灵敏的经常使用。

上方罗列几个比拟罕用的性能,后续在名目中遇到其他的适用性能再启动补充。

1.日志级别性能:有时刻咱们在开发、测试环境遇到bug时,比如接口调用失败、参数没收到等疑问,或许想看看调用性能,就可以经过性能Feign的日志来诊断疑问,以此来让Feign把恳求日志消息打印进去。

2.自定义阻拦器成功认证逻辑:有时刻在访问外部某个服务时须要指定身份认证消息能力调用,这时刻就须要设置token的认证消息,可以经过自定义阻拦器将token消息参与到header外面。

3.超时期间性能:经过Options可以性能衔接超时期间和读取超时期间。

客户端组件性能:Feign中自动经常使用JDK原生的URLConnection发送HTTP恳求,咱们可以集成别的组件来交流掉URLConnection,比如Apache HttpClient、OkHttp。

紧缩性能:开启紧缩可以有效浪费网络资源,优化接口性能,咱们可以性能GZIP来紧缩数据总结:

经常使用JavaAPI的5个技巧

经常使用JavaAPI的5个技巧

几平每团体都知道防止去成功加密等算法。

雷同情理,你的运行的安保栈的其他局部也是一样,或许须要破费很大,失掉的风险也很大。

你很或许会犯一些失误。

自1999年以来,曾经有个CVE(公共破绽和泄露)颁布了。

而其中地下的大局部的发现者都是那些十分痴呆的人。

你或许以为处置一个繁难的用例(例如验证用户的明码)是很繁难的事件一你所做的一切只是比拟一对字符串。

这样想就错了。

你须要验证明码的哈希值,审核尝试登录的次数,缩小针对字典的攻打,这只是冰山一角。

你最好的选用是经常使用现有的成熟的库或框架,例如Apache的Shiro或许SpringSecurity,让这些框架去处置各类复杂的安保疑问。

TLS,Always!永远经常使用TLS!

能让你的运行须要TLS(HTTPS/SSL)只须要繁难的一行代码,一切人都应该这样做!假设经常使用Apache Shiro框架,只须要设置属性:[urls]/**=ssl假设经常使用Spring Security.,只须要在设置HttpSecurity时,简干燥用一个方法即可。

()anyRequest()(),在Spring Boot中,仅需设置一些属性,如下=8443 -store= -store-password=secret -password=another-secret。

3.经常使用Spring Booti创立Web Service

Spring Boot:是Spring平台的一个简化,能让编写Spring运行变得很繁难,例如能用很少的代码,编写《app运行中思考的12个要素》一文中提到的观念。

假设你还在经常使用建War包的模式编码,那么Spring Boot值得你去学习。

经常使用Spring Booti可以复杂的、不同类型的运行,例如可以经常使用繁难的注解。

(@EnableResourceServer))就搭建一个OAuth资源主机,或许经过繁难的属性扭转其端口 8090假设不青睐经常使用SpringBoot,.那么可以经常使用Dropwizard去搭建JAX-RS技术栈。

4,监督运行和性能目的

假设无任何数据的状况下是很难发现程序的失误的。

Spring Booti经过经常使用Actuator,能让搜集目的数据变得容易,只须要在运行中参与一个依赖,如下:<dependency<groupld></groupld>。

<artifactld>spring-boot-starter-actuator</artifactld>。

而后就可以经过阅读器中,在访问运行地址后输入/health或许/metrics去审核运行的肥壮状况或许目的。

Dropwizard框架经过healthcheck和/metrics成功雷同的性能。

5.包全敏感消息

人们都以为API密钥是不安保的,这是理想。

密钥经过电子邮件发送或源代码治理系统控制。

兴许这是它们看起来比明码更不安保的要素,但它们也一样敏感。

假设须要将AP川密钥存储在文件中,请确保授予文件有限的访问权限。

例如,咱们倡导在公家目录中寄存Okta的YAML文件并且赋予文件一切者只读权限。

chmod u=r,go-rwx ~//假设你正为经常使用你的APP的用户创立API,记得提示他们,假设无设置好权限的话,SSH的忽文件是放在你的~/目录下,假设无设置好权限的话。

GitHub把它们放在“风险区域”,以提示用户,这是十分有用的。

你可能想看:
« 上一篇
下一篇 »

相关推荐

java中什么是冒泡,冒泡算法

2026年09月06日

27阅读

java中冒号,java双冒号

2026年09月06日

26阅读

java中的->,java中的是什么意思

2026年09月06日

24阅读

java中的super用法,Java中super的说明

2026年09月06日

28阅读

java中反射的应用,java中反射的应用场景

2026年09月06日

25阅读

java中开关的定义,Java关键字有哪些

2026年09月06日

25阅读