目录
一、feign简介应用
1、feign的概念
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。feigin是一种模板化,声明式的http客户端,feign可以通过注解绑定到接口上来简化Http请求访问。与当我们访问别的服务端口的时候 大部分使用httpclient等请求进行调用不同,在eureka注册的服务,我们可以使用Feign 声明接口的形式来进行相关服务的调用,并提供了失败回退(其实是Hystrix组件的使用)。Feign只是一个便利的rest框架,简化调用,最后还是通过ribbon在注册服务器中找到服务实例,然后对请求进行分配。
2、feign的使用
- maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
- 主项目中添加@EnableFeignClients
@SpringBootApplication
//扫描项目下所有使用@FeignClient注解修饰的接口
@EnableFeignClients
@EnableEurekaClient
public class FeignApplication {
public static void main(String[] args) {
SpringApplication.run(FeignApplication.class, args);
}
}
- 使用feign调用微服务
//使用feignclien注解和SpringMVC相关的常用注解 声明一个feign接口 调用微服务信息
@FeignClient(value = "service-ribbon",url = "https://api.github.com",decode404 = false,
fallback = MyFallback.class, fallbackFactory = MyFallBackFactory.class )
public interface SayHiFeign {
@RequestMapping("/hi", method = RequestMethod.GET)
String sayHi(@RequestParam(name="name",required = true) String name);
}
如上便完成了使用feign完成别的为服务调用,是不是很简单。点击下载demo源码 feign组件使用demo
二、feign原理分析
1、前言
通过如上的简单介绍我们大致了解了feign的概念以及如何在项目中进行使用,通过上面的观察以及使用,我们发现feign组件在springclound项目中使用是如此只简单:只需在项目的主启动类使用@EnableFeignClients注解,在服务接口类中使用@FeignClient修饰。下面我们就从这两个注解入手揭开feign的神秘面纱(貌似话说的有点大)。
2、@EnableFeignClients注解
为了方便理解,我们在这里先不给出结论,直接从源码中一步一步分析代码,最后再给出结论。
2.1、@EnableFeignClients
//运行时保留
@Retention(RetentionPolicy.RUNTIME)
//该注解用于修饰描述类、接口(包括注解类型) 或enum声明
@Target(ElementType.TYPE)
//可以生产api doc
@Documented
//这个比较重要使用注解修饰的项目 需要先初始化FeignClientsRegistrar该类
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
//basePackages的别名
String[] value() default {};
//指定需要扫描的包名列表
String[] basePackages() default {};
//指定需要扫描的class列表信息
Class<?>[] basePackageClasses() default {};
//feign调用的配置信息类 该对象非常重要,
//包含FeignClient需要的重试策略,超时策略,日志等配置,
//如果某个服务没有设置, 则读取默认的配置。
Class<?>[] defaultConfiguration() default {};
//直接指定FeignClient注解修饰的class
Class<?>[] clients() default {};
该注解 用来开启 Feign,所以其属性主要是指定要扫描的包名类名 FeignClient列表(value属性,basePackages属性,basePackageClasses属性,clients属性),以及相关的配置信息(defaultConfiguration),这些并非我们关注的重点 在该注解类上有个使用@Import注解引入FeignClientsRegistrar.class 的实体类 其实该类是实现feign调用的部分功能(前期准备)的实现,下面让我们来分析一下
FeignClientsRegistrar类
2.2、FeignClientsRegistrar类
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
通过名字可以看出该类是一个注册器,该类实现了 ImportBeanDefinitionRegistrar 通过扫描某个特性的类,将bean注册到IOC中。他的生命周期方法是registerBeanDefinitions ,Spring 通过调用其 registerBeanDefinitions 方法来获取其提供的 bean definition。
//生命周期的方法
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注册feign 相关的特性 配置信息
registerDefaultConfiguration(metadata, registry);
//注册FeignClient 对象
registerFeignClients(metadata, registry);
}
上面开启feign主要分两大部分:
- 注册feign的配置信息,比如重试机制,日志配置
- 注册所有使用@FeigenClient修饰的类注册
2.3、加载feign的配置信息
//注册配置信息
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//获取EnableFeignClients注解的元数据信息
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//判断是否存在defaultConfiguration 属性信息
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
//注册defaultConfiguration 对应的属性信息类 ,如果为空则使用默认的配置
//FeignAutoConfiguration
//最终将配置类解析成一个BeanDefinition类注入到spring中
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
如上主要是从启动类的@EnableFeignClients 中获取defaultConfiguration属性中用户自定义的配置信息,如果没有则使用默认的配置信息类FeignAutoConfiguration,最终将其包装成BeanDefinition类注册到spring容器中(代码比较繁琐,个人只贴到这里)。配置中心主要提供了feign 的功能特性,该配置信息是适用于@FeignClient注解的全局配置,也可以在@FeignClient的configuration属性做单独的配置。该配置主要包含熔断器、失败重试、超时策略、日志配置等。
2.4、注册FeignClient
//注册FeigenClient
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//获取类扫描组件
ClassPathScanningCandidateComponentProvider scanner = getScanner();
//添加资源加载服务类 提供加载指定目录下的资源信息 便于后面的类扫描
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//获取EnableFeignClients注解的属性信息 其中可能包含需要扫描的包名或者class信息
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
//创建扫描过滤规则 只扫描包含FeignClient注解修饰的类
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
//获取注解中包含client 属性 如果有则不进行包名扫描 直接遍历存在的client 进行注册
final Class<