说明:学习代码使用的是ruoyi-cloud开源代码
一、使用在接口上写入一个@FeignClient 注解
@FeignClient(contextId = "remoteFileService", value = ServiceNameConstants.FILE_SERVICE, fallbackFactory = RemoteFileFallbackFactory.class)
public interface RemoteFileService
{
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R upload(@RequestPart(value = "file") MultipartFile file);
}
FeignClient 注解属性,这个博客写过
二、Feign 如何实现首先是使用@EnableFeignClients注解导入一个FeignClientsRegistrar.class,因为这个类实现了importBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware。Aware接口啥的都是set一些属性,就不看了。直接看importBeanDefinitionRegistrar的实现方法
@Override
public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
// 注册默认的配置,可以参考FeignClient 注解属性中的configuration属性,只不过这个是加载EnableFeignClients中的defaultConfiguration属性
registerDefaultConfiguration(metadata, registry);
// 注册客户端
registerFeignClients(metadata, registry);
}
- 注册配置类的registerDefaultConfiguration代码
private void registerDefaultConfiguration(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
// 获取EnableFeignClients注解中的所有属性
Map defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
// 是否是内部类,是的话,就去取内部类的名字 xxx.xxx.xx.Class$Neibu
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
// 将这些类注册为 FeignClientSpecification.class
// bean 的名字为 default.xxx.xxx.xx.Class$Neibu.FeignClientSpecification
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
}
}
因为上面注册成了一个FeignClientSpecification类嘛,这个类是实现了NamedContextFactory.Specification,但是NamedContextFactory中的代码有很多
1、注册client
public void registerFeignClients(Annotationmetadata metadata, BeanDefinitionRegistry registry) {
linkedHashSet candidateComponents = new linkedHashSet<>();
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class>[] clients = attrs == null ? null : (Class>[]) attrs.get("clients");
// 如果指定了feginClient 那么就直接用指定的,如果没指定就去扫描包,
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
// 包含FeignClient注解的文件
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set basePackages = getbasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
Annotationmetadata annotationmetadata = beanDefinition.getmetadata();
Assert.isTrue(annotationmetadata.isInterface(), "@FeignClient can only be specified on an interface");
Map attributes = annotationmetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
// 注册FeignClient注解中configuration的配置类
registerClientConfiguration(registry, name, attributes.get("configuration"));
// 注册FeignClient客户端
registerFeignClient(registry, annotationmetadata, attributes);
}
}
}
具体的注册FeignClient,主要是将这个接口类注册为FeignClientFactoryBean以及设置一些属性信息。
private void registerFeignClient(BeanDefinitionRegistry registry, Annotationmetadata annotationmetadata, MapFeignClientFactoryBean 类attributes) { String className = annotationmetadata.getClassName(); Class clazz = ClassUtils.resolveClassName(className, null); ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) registry : null; String contextId = getContextId(beanFactory, attributes); String name = getName(attributes); // 重点 FeignClientFactoryBean factoryBean = new FeignClientFactoryBean(); factoryBean.setBeanFactory(beanFactory); factoryBean.setName(name); factoryBean.setContextId(contextId); factoryBean.setType(clazz); factoryBean.setRefreshableClient(isClientRefreshEnabled()); BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> { factoryBean.setUrl(getUrl(beanFactory, attributes)); factoryBean.setPath(getPath(beanFactory, attributes)); factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404")))); Object fallback = attributes.get("fallback"); if (fallback != null) { factoryBean.setFallback(fallback instanceof Class ? (Class>) fallback : ClassUtils.resolveClassName(fallback.toString(), null)); } Object fallbackFactory = attributes.get("fallbackFactory"); if (fallbackFactory != null) { factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class>) fallbackFactory : ClassUtils.resolveClassName(fallbackFactory.toString(), null)); } return factoryBean.getObject(); }); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); definition.setLazyInit(true); validate(attributes); AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className); beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean); // has a default, won't be null boolean primary = (Boolean) attributes.get("primary"); beanDefinition.setPrimary(primary); String[] qualifiers = getQualifiers(attributes); if (ObjectUtils.isEmpty(qualifiers)) { qualifiers = new String[] { contextId + "FeignClient" }; } BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers); BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); registerOptionsBeanDefinition(registry, contextId); }
首先这个类实现了 FactoryBean, 以及InitializingBean,还有俩Aware接口,直接看getObject() 方法 和 afterPropertiesSet()方法
public class FeignClientFactoryBean implements FactoryBean
验证你FeginClient的 contextId 和 name属性 有没有值
public void afterPropertiesSet() {
Assert.hasText(contextId, "Context id must be set");
Assert.hasText(name, "Name must be set");
}
@Override
public Object getObject() {
return getTarget();
}
T getTarget() {
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
// 个人理解这行代码就是把你原先的日志啊、编解码器啊等东西设置进去,获取一个builder对象。说白了就是构造器模式创建嘛
Feign.Builder builder = feign(context);
// 没指定url的话,就用http:// + 服务名称
if (!StringUtils.hasText(url)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
注册Client流程图



