本文主要内容:
- 简单了解原生Feign、Spring Cloud Open Fegin是什么
一、Spring Cloud Open Fegin介绍
1.1、Feign基础介绍
Feign:是申明式Web服务客户端,基于JAX-RS(javaRest规范)实现的
- 申明式:通过接口声明、Annotation驱动
- Web服务:HTTP通讯协议
- 客户端:用户服务调用存根
Feign可以将一个Service声明为可以被HTTP方式调用,底层基于面向接口的动态代理方式生成实现类,将请求调用委托到动态代理实现类上。
1.2、Spring Cloud Open Fegin基础介绍
Spring Cloud对原生Feign进行封装,使其支持SpringWebMvc,也就是Spring Cloud Open Feign
REST框架 | 使用场景 | 请求映射注解 | 请求参数注解 |
---|---|---|---|
JAX-RS | 客户端、服务端声明 | @Path | @*Param |
Feign | 客户端声明 | @RequestLine | @Param |
Spring Web MVC | 服务端声明 | @ReqeustMapping | @RequestParam |
SpringCloudOpenFeign | 客户端声明 | @ReqeustMapping | @RequestParam |
Spring Cloud Open Feign利用Feign的高扩展性,以及使用标准Spring Web MVC来声明客户端 Java 接口:
-
Feign
-
注解扩展性
-
HTTP 请求处理
-
REST 请求元信息解析
-
-
Spring Cloud Open Feign
- 提供 Spring Web MVC 注解处理
- 提供 Feign 自动装配
1.3、Spring Cloud Open Fegin的使用
1.3.1、引入Spring Cloud Open Fegin对应jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.3.2、启用Spring Cloud Open Fegin功能
启用Spring Cloud Open Fegin功能: 在启动类上添加注解@EnableFeignClients注解,并定义要扫描的包名
@EnableFeignClients(basePackages = "com.springcloud.feginclients")
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
1.3.1、声明一个FeignClient
准备一个接口:UserService.class
public interface UserService {
@GetMapping("/getUsers")
String getUsers();
@PostMapping("/addUsers")
int addUser(User user);
}
在定义的basePackages中创建对应的FeignClient,例如:UserServiceFeignClient .calss
- value: 需要和注册注册中心上的服务名称一致
- fallback: 请求失败时,会调用fallback指定类中的同名方法
@FeignClient(value = "user-service",
fallback = UserServiceFeignClient.UserServiceFeignClientFallback.class)
public interface UserServiceFeignClient extends UserService{
@Component
class UserServiceFeignClientFallback implements UserServiceFeignClient{
@Override
public String getUsers() {
return "查询用户列表失败,请重试";
}
@Override
public int addUser(User user) {
System.out.println("新增用户失败,请重试");
return -1;
}
}
}
1.3.4、通过接口方式调用FeignClient
通过@Autowired自动注入UserService,SpringCloud会为UserService创建一个动态代理存到IOC容器中
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@GetMapping("/getUsers")
public List<User> getUsers(){
userService.getUsers();
}
@PostMapping("/addUsers")
public int addUser(User user){
userService.addUsers(user);
}
}
通过使用Spring Cloud Open Fegin我们可以想调用本地方法一样,去请求远程服务接口。下面我们就来分析一下它的原理
二、Spring Cloud Open Fegin原理分析
Spring Cloud Open Fegin的核心原理,可以简单总结为以下意思:Spring Cloud Open Fegin会为标注了@FeignClient注解的接口创建动态代理,然后通过这个代理发送HTTP请求到我们指定分服务端
其中的关键点有两个:
- Spring Cloud Open Fegin时如何加载并创建这个代理对象的?(2.1章分析说明)
- 代理对象如何完成远程服务调用?(2.2章分析说明)
2.1、为@FeignClient注册动态代理的流程图
Spring Cloud Open Fegin对于@FeignClient注册动态代理的流程大体可以分为以下三步:
2.1.1、步骤一:FeignClientsRegistrar注册FeignClientFactoryBean
(1)通过@EnableFeignClients启用功能
我在介绍SpringBean装载过程时,介绍过Spring的动态装配Bean:通过@Enable模式+@import方式,可导入三类Class文件
- 添加了@Configuration注解的配置类
- ImportSelector接口的实现类
- ImportBeanDefinitionRegistrar接口的实现类
@EnableFeignClients:通过导入ImportBeanDefinitionRegistrar接口实现类的方式,实现Bean的动态注入
,源代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
增加@EnableFeignClients注解后,Spring会调用FeignClientsRegistrar.registerBeanDefinitions()方法将每一个@FeignClient注册到IOC容器中
FeignClientsRegistrar.registerBeanDefinitions()方法,源码如下:
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 如果EnableFeignClients配置了defaultConfiguration
// 注册一个FeignClientSpecification到IOC中,用于扫描自定义配置类
registerDefaultConfiguration(metadata, registry);
// 注册FeignClients:会每一个@FeignClient创建代理
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration方法:注册@EnableFeignClients中定义的defaultConfiguration默认配置(不做分析)
registerFeignClients()方法:会每一个@FeignClient创建代理(下面信息此方法源码)
(2)注册FeignClientFactoryBean到容器中
registerFeignClients.registerFeignClients()方法:会为每一个@FeignClient创建代理,它的核心功能如下:
定义一个scanner扫描器,并指定扫描路径、筛选条件
:- 扫描路径:根据@EnableFeignClients中配置的参数,获取要扫描的包路径basePackages
- 设置一个AnnotationTypeFilter过滤器:根据@EnableFeignClients中配置的参数,指定要过滤的文件(添加@FeignClient注解的class文件,或者指定名称的class文件)
扫描指定basePackages包下,添加@FeignClient注解的class文件
:数据会存储到一个Set列表钟,一个class文件对应一个BeanDefinition读取每一个BeanDefinition中@FeignClient注解中的属性
根据@FeignClient注解中的属性,再生成一个FeignClientFactoryBean的BeanDefinition
(PS:需要注意,这个BeanDefinition对应的是一个FeignClientFactoryBean(工厂Bean)。在Spring中,工厂Bean是用来创建复杂Bean对象的)注册BeanDefinition到容器中(FeignClientFactoryBean的BeanDefinition)
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 定义scanner扫描器,
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 获取EnableFeignClients中定义的basePackages
Set<String> basePackages;
Map<Strin