前言
前几天,我们尝试阅读了 Feign 的源码,那么在本次阅读中。我们学习到了什么呢?
本篇就来总一下。
Feign 核心 - 代理模式的使用
Feign 的总体思路,还是通过动态代理的方式来帮我们做增强。
我们定义一个下面这样的接口:
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value = "/stores")
Page<Store> getStores(Pageable pageable);
}
Feign 总过分析类上注解 @FeignClient("stores")
、方法上注解@RequestMapping(method = RequestMethod.GET, value = "/stores")
、方法入参、方法返回值,来为我们构造一个 HTTP 请求。
整体流程如下:
Spring Boot Starter 中结合代理模式的正确姿势
根据阅读 Feign 的源码,我们其实可以总结一套在 Spring Boot Starter 中使用代理模式的正确姿势。
第一步:创建启动注解/功能注解
启动注解就是 @EnableXxxClients
,其中有一些属性可以定义需要扫描哪些类,以及一些公共配置属性。
功能注解就是 @XxxClient
,其中可以定义一些实现功能需要的业务属性,以及一些针对性的配置类。
第二步:创建自己的 FactoryBean。
这里主要实现 getObject()
方法,在此方法中,根据传过来的 type 类型,创建对应的代理对象。
可以根据需要,决定采用 Cglib 还是 JDK 的动态代理。
public class XxxFactoryBean implements FactoryBean<Object> {
private Class<?> type;
private String name;
Object getObject() throws Exception {
// ....
};
}
第三步:开发 ImportBeanDefinitionRegistrar
,也就是 BD注册器。
用于把扫描出来的类,包装为 XxxFactoryBean
,随后用 XxxFactoryBean
代替扫描出的类,注册到 Spring Boot 的 BD 注册器中。
class XxxxClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
XxxFactoryBean factoryBean = new XxxFactoryBean();
// ... 填充一些需要的业务属性
factoryBean.setName(name);
factoryBean.setType(clazz);
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(clazz, () -> {
// ... 填充一些需要的业务属性
return factoryBean.getObject();
});
// 注册
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
}
在 启动注解 @EnableXxxClients
,上导入XxxxClientsRegistrar
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
// ... 一些自定义的属性
}
第四步:开发自动配置类
开发自动配置类,加载需要的配置项。
- resource
- META-INF
- spring.properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration= com.xxx.xxx.xxx.XxxConguration.class
为第三方预留扩展
这个需要代码抽象灵活一些,用更通俗地话说能用父类的地方不用子类。
我们随便数数 Feign 提供的可配置的增强功能,HTTP 请求客户端、熔断、负载均衡、请求前置处理器、编解码器、错误解码器等等。
让我们来看一段代码,这是获取代理类的一段代码,这段代码就很能体现可扩展性。
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
// Http 请求,各 HTTP 请求客户端 可以实现 Client 从而来接入 Feign 。
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
// 获取代理对象功能类,对 feign 做增强的,可以实现自己 Targeter 。
// 例如 HystrixTargeter ,就是 Hystrix 熔断器接入 feign 的实现。
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon or spring-cloud-starter-loadbalancer?")
}
其实这里还体现了一点,即依赖倒置。像这里的 Client、Targeter 都是从 FeginContext 容器中获取,而不是直接创建的。
小结
以上就是在粗略查看 Feign 源码时,获得的一些启发。
后面,我们可以根据前面《使用与配置篇》、《原理篇》、《收获篇》,来基于 Feign 封装一些适合业务项目的小工具。例如:服务之间的数据透传,Feign 的异常封装,以及模仿 Feign 开发一个简版的 Http 调用工具。