2024年9月feignclient注解(Spring Boot 和 Feign Client 解析 Enum参数)

 更新时间:2024-09-21 08:41:56

  ⑴feignclient注解(SpringBoot和FeignClient解析Enum参数

  ⑵SpringBoot和FeignClient解析Enum参数

  ⑶SpringBoot中Enum解析默认使用的是EnumToStringConverter,默认转成枚举的名称。响应返回的JSON,Enum也默认解析为name。有时候不使用枚举的name,而是value来进行返回,参数解析。这时候提交gender=会解析失败,可以通过配置MessageConvertFactory实现解析。定义StringToEnumConverterFactory:配置mvc这时候调用访问,Feign会把gender转成name来进行访问?gender=MALE,这个情况下服务端肯定报错,因为我们的Enum解析已经不是name了。所以要配置下FeignClient,支持Enum也转成JsonValue注解的值进行访问。定义Converter:配置FeignClient:通过以上三个步骤,就可以实现Enum参数的请求和响应了。

  ⑷SpringCloudFeign源码分析-FeignClientFactoryBean

  ⑸关于Feign的启动原理分析,参照另一篇SpringCloudFeign源码分析-feign启动原理书接上文,上篇最后提到所有带FeignClient注解的interface都被封装成FeignClientFactoryBean的BeanDefinition。从名字上可以得知这个类是一个FactoryBean。关于FactoryBean的介绍参考...因此直接找getObject()。getTarget方法首先获取FeignContext的对象,基于这个context对当前feign的配置信息存放到Builder中。首先实例化bean:FeignContextFeignContext的定义在FeignAutoConfiguration第一次除了创建新的FeignContext对象之外,还设置了一组configurations,这组configurations是FeignClientSpecification类型,通过autowired注入。在扫描EnableFeignClients和各个FeignClient时,将configuration对应的class封装成了FeignClientSpecification的BeanDefinition,这里从容器中取出来创建对象注入到configurations通过断点可以看到这里有个FeignClientSpecification的对象一个是default.开头的在启动类里配置的configuration,剩下的都是FeignClient的configuration。FeignContext继承了NamedContextFactory,对应的范型就是FeignClientSpecification,看下NamedContextFactory构造方法这里设置了默认的defaultConfigType,feign里用的是FeignClientsConfiguration,定义了一系列的默认值。在获取到FeignContext之后,开始封装Feign.Builder。首先通过context实例化FeignLoggerFactory的对象,因为context是NamedContextFactory的子类,会给每个contextId创建一个独立的AnnotationConfigApplicationContext上下文,每一个k-v会存储在FeignContext的全局context中,key就是contextId这三个方法的实现完全体现了NamedContextFactory的作用:给每个name创建一个单独的ApplicationContext子上下文对象,后续凡是这个name的ioc操作,都由独立的ApplicationContext来完成,name之间的context相互隔离。所有的子上下文保存在了Map《String,AnnotationConfigApplicationContext》contexts中。在创建Context时,补充了configuration的设置:首先(的位置),从全局的configurations查找是否定义了只对当前name生效的configuration,也就是判断在当前name所属的FeignClient注解上是否定义了configuration。如果定义过,将这个configuration的Class封装成BeanDefinition注册到本name的子上下文中。接着(的位置),从全局的configurations查找是否定义了全局配置,也就是EnableFeignClients的defaultConfiguration的值,这里固定前缀是default.。如果也存在,就也将这个defaultConfiguration的Class封装成BeanDefinition注册到本name的子上下文中。第一次调用完毕get方法后,给每个FeignClient创建的FeignContext就完成了configuration初始化的动作,后面的所有操作,如配置encoder、decoder都是给当前的子上下文内注册BeanDefinition。最后将所有配置封装成Builder返回。在getTarget()构造完成builder属性之后,开始了整个请求调度过程。先看第一段:如果没有url属性,就用na

  ⑹SpringCloud+Zookeeper+Feign整合及Feign原理

  ⑺SpringCloud与Zookeeper的整合只需要添加相关的starter依赖和增加相关注解即可完成。pom.xml如下:bootstrap.yml如下:最后开启服务的注册与发现service和controller实现笔者加入了swagger,如果需要只需加入如下依赖和配置:至此,springcloud与zookeeper的整合就完成了,调用结果如下:为了测试与Feign的整合,再构建一个消费者:与上述构建的过程类似。pom.xml增加spring-cloud-starter-openfeign依赖bootstrap.yaml:开启服务注册与发现,EnableFeignClients注解注册FeignClientFeignClient注册声明定义FeignClient,笔者以两种方式定义了两个FeignClient:.通过请求路径定义FeignClient.通过生产者(即上述构建的helloService暴露出来的接口定义FeignClientcontroller测试:测试结果如下:知道了如何将SpringCloud,Zookeeper和Feign进行整合,我们知道了怎么使用,更重要的是要知道里面的原理,做到知其然更要知其所以然。通过上述对整合过程的描述中可以发现,EnableFeignClients和FeignClient两个注解是将Feign整合进SpringCloud的重要组成部分,因此,从这两个注解入手来了解Feign。EnableFeignClients注解通过Import引入了FeignClientsRegistrar进行feign客户端的注册,同时FeignClientsRegistrar通过实现ImportBeanDefinitionRegistrar来将bean注册spring容器中:至此,我们知道了通过EnableFeignClients和FeignClient两个注解以及其相关属性,在服务启动时,将每个feignclient以及其对应的配置和每个客户端通用的配置以bean的方式注册完到spring容器中。当使用Autowired注解自动注入FeignClient时,Spring容器会使用注册FeignClient用到的FeignClientFactoryBean为其生成FeignClient实例。默认使用的targeter是HystrixTargeter,根据builder的类型设置不同的属性,并生产Feignclient从上面的分析可以得出,当服务启动时,通过EnableFeignClients注解,启动对标注了FeignClient注解的类进行扫描和注册,通过FeignClientFactoryBean将FeignClient注册到Spring容器中。当使用Autowired注解进行自动注入时,注册到Spring容器中FeignClient会以动态代理的形式注入,这些动态代理中包含了接口方法的methodHandler用以处理调用转发。

  ⑻Feign比直接调用RestTemplate好在哪里

  ⑼Feign是一个声明式的REST客户端,通过Feign我们只需要定义服务绑定接口,以申明式的方法,优雅而简单的实现了服务调用。虽然RestTemplate已经可以将请求拦截来实现对依赖服务的接口调用,并对Http请求进行封装处理,形成一套模板化的调用方法,但是对服务依赖的调用可能不只一处,一个接口都会被多次调用,所以我们会像前面那样针对各个微服务字形封装一些客户端接口调用类来包装这些依赖服务的调用。由于RestTemplate的封装,几乎每一个调用都是简单的模板化内容,Feign在此基础上做了进一步的封装,由它来帮助我们定义和实现依赖服务接口的定义。在服务消费者创建服务调用接口,通过FeignClient注解指定服务名来绑定服务,然后再使用SpringMVC的注解来绑定具体该服务提供的REST接口。希望能帮到你

  ⑽Feign源码解析二

  ⑾本文会基于Feign源码,看看Feign到底是怎么实现远程调用上文中,我们的user-service服务需要调用远程的order-service服务完成一定的业务逻辑,而基本实现是order-service提供一个spi的jar包给user-service依赖,并且在user-service的启动类上添加了一个注解这个注解就是EnableFeignClients,接下来我们就从这个注解入手,一步一步解开Feign的神秘面纱该注解类上的注释大概的意思就是:扫描那些被声明为FeignClients(只要有.springframework.cloud.openfeign.FeignClient注解修饰的接口都是FeignClients接口)的接口下面我们继续追踪源码,看看到底什么地方用到了这个注解利用IDEA的查找调用链快捷键,可以发现在.class类型的文件中只有一个文件用到了这个注解OK,下面主要就是看这个类做了什么通过UML图我们发现该类分别实现了ImportBeanDefinitionRegistrar,ResourceLoaderAware以及EnvironmentAware接口这三个接口均是spring-framework框架的spring-context模块下的接口,都是和spring上下文相关,具体作用下文会分析总结下来就是利用这两个重要属性,一个获取应用配置属性,一个可以加载classpath下的文件,那么FeignClientsRegistrar持有这两个东西之后要做什么呢?上面将bean配置类包装成FeignClientSpecification,注入到容器。该对象非常重要,包含FeignClient需要的重试策略,超时策略,日志等配置,如果某个FeignClient服务没有设置独立的配置类,则读取默认的配置,可以将这里注册的bean理解为整个应用中所有feign的默认配置由于FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,这里简单提下这个接口的作用我们知道在spring框架中,我们如果想注册一个bean的话主要由两种方式:自动注册/手动注册知道了ImportBeanDefinitionRegistrar接口的作用,下面就来看下FeignClientsRegistrar类是何时被加载实例化的通过IDEA工具搜索引用链,发现该类是在注解EnableFeignClients上被import进来的,文章开始的图片中有这里提下Import注解的作用该注解仅有一个属性value,使用该注解表明导入一个或者多个Configuration类,其作用和.xml文件中的《import》等效,其允许导入Configuration类,ImportSelector接口/ImportBeanDefinitionRegistrar接口的实现,也同样可以导入一个普通的组件类注意,如果是XML或非Configuration的bean定义资源需要被导入的话,需要使用ImportResource注解代替这里我们导入的FeignClientsRegistrar类正是一个ImportBeanDefinitionRegistrar接口的实现FeignClientsRegistrar重写了该接口的registerBeanDefinitions方法,该方法有两个参数注解元数据metadata和bean定义注册表registry该方法会由spring负责调用,继而注册所有标注为FeignClient注解的bean定义下面看registerBeanDefinitions方法中的第二个方法,在该方法中完成了所有FeignClient注解接口的扫描工作,以及注册到spring中,注意这里注册bean的类型为FeignClientFactoryBean,下面细说总结一下该方法,就是扫描EnableFeignClients注解上指定的basePackage或clients值,获取所有FeignClient注解标识的接口,然后将这些接口一一调用以下两个重要方法完成注册configuration配置bean和注册FeignClientbean断点位置相当重要BeanDefinitionBuilderdefinition=BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);这里是利用了spring的代理工厂来生成代理类,即这里将所有的feignClient的描述信息BeanDefinition设定为FeignClientFactoryBean类型,该类继承自FactoryBean,因此这是一个代理类,FactoryBean是一个工厂bean,用作创建代理bean,所以得出结论,feign将所有的feignClientbean定义的类型包装成FeignClientFactoryBean最终其实就是存入了BeanFactory的beanDefinitionMap中那么代理类什么时候会触发生成呢?在spring刷新容器时,会根据beanDefinition去实例化bean,如果beanDefinition的beanClass类型为代理bean,则会调用其TgetObject()throwsException;方法生成代理bean,而我们实际利用注入进来的FeignClient接口就是这些一个个代理类这里有一个需要注意的点,也是开发中会遇到的一个启动报错点如果我们同时定义了两个不同名称的接口(同一个包下/或依赖方指定全部扫描我们提供的FeignClient,且这两个FeignClient接口注解的value/name/serviceId值一样的话,依赖方拿到我们的提供的spi依赖,启动类上EnableFeignClients注解扫描能同时扫描到这两个接口,就会启动报错原因就是Feign会为每个FeignClient注解标识的接口都注册一个以serviceId/name/value为key,FeignClientSpecification类型的bean定义为value去spring注册bean定义,又默认不允许覆盖bean定义,所以报错官方提示给出的解决方法要么改个FeignClient注解的serviceId,name,value属性值,要么就开启spring允许bean定义覆写至此我们知道利用在springboot的启动类上添加的EnableFeignClients注解,该注解中import进来了一个手动注册bean的FeignClientsRegistrar注册器,该注册器会由spring加载其registerBeanDefinitions方法,由此来扫描所有EnableFeignClients注解定义的basePackages包路径下的所有标注为FeignClient注解的接口,并将其注册到spring的bean定义Map中,并实例化bean下一篇博文中,我会分析为什么我们在调用(Resource这些由FeignClient注解的bean的方法时会发起远程调用

  ⑿SpringCloud系列之Feign-.EnableFeignClients底层机制深度解析

  ⒀EnableFeignClients源码比较值得一读,读完之后我们就学会了如何自己写一个注解并成功运用起来了

  ⒁首先我们进入到EnableFeignClients注解里面可以看到这个注解里面声明了几个属性,通过名称大概可以看到比如basePackages应该是包路径,value的话应该是个basePackages别名,我们暂且不管,看下这个注解上面有个Import的注解,点进去这个类来看一下

  ⒂如图可以看到FeignClientsRegistrar这个类实现了三个Spring的类,根据名称大概猜一下,第一个应该是关于类定义注册的类,第二个第三个相信大家可能用过或者了解过就是Spring的Aware的一些类,大致就是加载资源或者环境变量的类

  ⒃那我们看一下ImportBeanDefinitionRegistrar类里面是什么:

  ⒄可以看到里面定义了一个方法,那么我们重新返回到FeignClientsRegistrar看下他是怎么实现这个方法的:

  ⒅这个方法主要干了两件事儿,第一个方法是注册了默认的配置信息,第二个就是注册FeignClients,我们挨个一个个详细的看下:

  ⒆到了较为详细的源码时候,如果看不懂,我们最好是打断点,当我们启动了Eureka-server,Eureka-client,然后再启动Feign-consumer的时候,断点就可以进来,我们可以看到一些传参的信息:

  ⒇可以看到这个metedata里面的数据刚好就是在启动类上面的三个注解,并且还带有三个注解的属性信息,下面再给大家看下主类对照下就懂了:

  ⒈然后我们继续往下面学习:

  ⒉这一步就比较好理解了,我们拿到EnableFeignClients这个注解的属性信息。

  ⒊然后就是拼接了一个名称,这个名称就是启动类的前端加了个default.而已,然后registry没变还是传参过来,defaultAttrs.get(“defaultConfiguration“)这个属性从刚刚断点来看也是空的。调用了一个registerClientConfiguration方法:

  ⒋这个方法就是使用了Spring的BeanDefinitionBuilder把FeignClientSpecification这个bean给注册到Spring容器中了。

  ⒌然后我们继续放回到主方法中看下一个方法:

  ⒍这个方法根据名称registerFeignClients来说应该就是注册FeignClients类了,进入方法中,第一个scanner我们看下:

  ⒎有点看不懂,没关系,猜一下,可能是扫描类的工具把。

  ⒏我们继续王下面走,scanner加载了一个resourceLoader这个类,这个类我们可以查一下,他是Spring框架中与资源相关的类,然后再往下看

  ⒐下面还是获取主类中的注解EnableFeignClients的属性信息

  ⒑再往下,我们可以从图中看到在属性中获取关于clients的信息,但是没有,然后scanner就加了一个类似过滤器的东东,然后调用了getBasePackages的方法

  ⒒下面我们看下getBasePackages方法:

  ⒓看过之前的方法,这个方法就好理解了,首先就还是获取EnableFeignClients的所有属性信息,然后把值都给取出来,取得属性分别是value还是basePackages等关于包路径的属性值,如果都没有获取到,就获取一个默认的包路径

  ⒔这个包路径断点可以看到就是主类的包路径,所以整体上看,这些逻辑就是首先看注解中有没有关于FeignClient的包路径信息,如果没有配置,那程序就准备从主程序所在的包路径下找所有的FeignClient了。

  ⒕然后继续看这个registerClientConfiguration方法:

  ⒖这个方法我们之前看过就是把某个类加载到Spring中所以继续下一步,看registerFeignClient方法:

  ⒗获取到这个FeignClient的所有属性之后,我们就进行数据处理,把属性信息都赋值给definition

  ⒘这个方法表示我们把这个类以按照类型注入作为属性,然后

  ⒙这块逻辑就是为了防止两个有同一个父类的FeignClient出现问题所做的,我们应该都遇到过一个问题就是使用Autowire注入类的时候发现报错,说是有两个类不知道注入哪一个,而如果其中一个有primary注解的话,spring是会优先注入这个类的。

  ⒚下面就没什么了,直到最后执行了

  ⒛整个EnableFeignClients的实现到此执行完毕,这个注解的源码相对来说看起来算是比较清晰明了了,而且对于我们如果有做一个新注解的需求的话,完全可以参照着做,非常具有模板意义。

  SpringCloud系列之Feign-.Feign上下文构建解析

  首先在构建上下文的入口是在FeignClientFactoryBean的getObject方法.首先第一步就是构建了FeignContext类.然后我们进入到feign这个方法中看下:可以看到首先获取两个日志对象一个FeignLoggerFa

  feignClient的实现原理(

  如何的使用feignClient如要使用feignClient,必须开启对feignClient的支持,如在启动类加上配置如下如上feignClient只能配置在类上面,且可以在运行时通过反射拿到注解信息。简单的说下clients属性的用法。clients设置由FeignClient注解修饰的类列表。如果clients不是空数组,则不通过类路径自动扫描功能来加载FeignClient。如下在EnableFeignClients的注解上面,import了另外一个beanFeignClientsRegistrarFeignClientsRegistrar实现了ImportBeanDefinitionRegistrar,ImportBeanDefinitionRegistrar提供了动态注册bean的能力,等于说除了spring本身的一般的通过配置文件注册bean的流程,开放了一个hook的口子,让用户自己去注册bean。所有实现了该接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。(后面会专门的对BeanFactoryPostProcessor进行介绍在FeignClientsRegistrar中的这个方法就是用来注册相应的bean的,调用了两个方法//注册默认配置registerDefaultConfiguration(metadata,registry);与//注册所有的feignClientbeanDefinitionregisterFeignClients(metadata,registry);先看registerDefaultConfiguration(metadata,registry),此方法用来注册默认配置,如在EnableFeignClients里面配置的defaultConfiguration属性,如果配置了,就会进行加载注册,registerDefaultConfiguration会将其配置信息封装成一个FeignClientSpecification注册到容器中,该对象非常重要,包含FeignClient需要的重试策略,超时策略,日志等配置,如果某个服务没有设置,则读取默认的配置。接着执行registerFeignClients(metadata,registry),方法主要是扫描类路径,对所有的FeignClient生成对应的BeanDefinition,其中又执行了registerClientConfiguration(registry,name,attributes.get(“configuration“));这个方法是对每个feignClient自己配置的configuration进行注册。而registerFeignClient(registry,annotationMetadata,attributes);方法会将定义的feignClient注册到容器中。跟进去可以看到BeanDefinitionBuilderdefinition=BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);spring会将FeignClient注册成一个FeignClientFactoryBean.class,这个是最关键的,注册成了一个FactoryBean而不是普通的bean。上面是FeignClient的注册的过程,那什么时候实例化呢,当然是在我们getBean的时候,发现是个FeignClientFactoryBean,那spring会调用getObject()来返回一个对象。所以接下来我们只需要看FeignClientFactoryBean.class的getObject()是如何的返回对象就可以了。

您可能感兴趣的文章:

相关文章