前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Feign原理_feign基于什么协议

Feign原理_feign基于什么协议

作者头像
全栈程序员站长
发布2022-11-10 14:31:03
4680
发布2022-11-10 14:31:03
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

一、feign测试代码

1.服务接口上标注@FeignClient

代码语言:javascript
复制
@FeignClient("order")
public interface OrderServiceFeign { 
   
    @GetMapping("/getOrder")
    String getOrder();
}

2.调用方可以直接注入然后直接调用访问

代码语言:javascript
复制
@RestController
@RequestMapping("/feign")
public class FeignTestController { 
   

    @Autowired
    private OrderServiceFeign orderServiceFeign;
    
    @GetMapping("/order")
    public String getOrder() { 
   
        return orderServiceFeign.getOrder();
    }
}

3.在调用方的启动类上加上一个启动注解@EnableFeignClients

代码语言:javascript
复制
@SpringBootApplication
@EnableFeignClients
public class UserServiceApplication { 
   
    public static void main(String[] args) { 
   
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
二、寻找入口

springcloud组件基本都是启动的注解,也就是@EnableFeignClients

1.@EnableFeignClients干了什么?
代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients { 

我们看到Import注解里面有个FeignClientsRegistrar类,在spring中看到以Registrar结尾的类,就会想到spring的bean的动态装备,一个是selecter,一个是registrar.

简单介绍下registrar 比如我们要将类RegistrarBean 装在的spring 装载到spring容器

代码语言:javascript
复制
public class RegistrarBean { 
   
}

定义一个类实现ImportBeanDefinitionRegistrar 接口,实现registerBeanDefinitions方法,然后通过registry的registerBeanDefinition可以注册一个实例到spring当中

代码语言:javascript
复制
public class SpringRegistrarTest implements ImportBeanDefinitionRegistrar { 
   
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
   
        BeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClassName(RegistrarBean.class.getName());
        registry.registerBeanDefinition("registrarBean",beanDefinition);
    }
}

定义一个自定义注解,用Import注解导入我们上面写的SpringRegistrarTest 类

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(SpringRegistrarTest.class)
public @interface EnableRegistrar { 
   
}

将我们写的注解配置到启动类上

代码语言:javascript
复制
@SpringBootApplication
@EnableFeignClients
@EnableRegistrar
public class UserServiceApplication { 
   

    public static void main(String[] args) { 
   
        ConfigurableApplicationContext applicationContext = SpringApplication.run(UserServiceApplication.class, args);
        Object registrarBean = applicationContext.getBean("registrarBean");
        System.out.println(registrarBean);
    }
}

我们在返回的context对象去获取我们去注册的bean,是可以拿到的

在这里插入图片描述
在这里插入图片描述

我们接着看FeignClientsRegistrar这个类

代码语言:javascript
复制
public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) { 
   
		// 注册默认的配置类
		registerDefaultConfiguration(metadata, registry);
		// 注册feign客户端
		registerFeignClients(metadata, registry);
	}

我们看registerFeignClients里面有这样几行代码

代码语言:javascript
复制
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
beanDefinition.setPrimary(primary);
String qualifier = getQualifier(attributes);
if (StringUtils.hasText(qualifier)) { 

alias = qualifier;
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { 
 alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);

我们发现注册了一个FeignClientFactoryBean的类,它实现了FactoryBean,它提供了一个getObject方法,当我们依赖注入一个feign客户端对象的时候,这个方法就会被调用

代码语言:javascript
复制
public Object getObject() throws Exception { 

// 获取FeignContext 对象
FeignContext context = applicationContext.getBean(FeignContext.class);
// 构建一个Feign.Builder 对象
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(this.url)) { 

String url;
if (!this.name.startsWith("http")) { 

url = "http://" + this.name;
}
else { 

url = this.name;
}
url += cleanPath();
return loadBalance(builder, context, new HardCodedTarget<>(this.type,
this.name, url));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { 

this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) { 

if (client instanceof LoadBalancerFeignClient) { 

client = ((LoadBalancerFeignClient)client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}

1.获取FeignContext 对象,FeignContext 是怎么来的? 有一个自动装配类FeignAutoConfiguration,创建了这个对象 FeignAutoConfiguration类

代码语言:javascript
复制
@Bean
public FeignContext feignContext() { 

FeignContext context = new FeignContext();
context.setConfigurations(this.configurations);
return context;
}

而且我们发现创建FeignContext中还调用了父类的构造器,传入了一个FeignClientsConfigurationfegin会根据不同的服务创建不同的spring上下文,在创建不同的应用上下文时,这个类会被加载到上下文当中

FeignContext类

代码语言:javascript
复制
	public FeignContext() { 

super(FeignClientsConfiguration.class, "feign", "feign.client.name");
}

FeignClientsConfiguration类 这个类创建了Decoder ,Encoder 对象,对数据进行编解码,相当于序列化和反序列化。有一个Contract对象, 这个对象是用的SpringMvcContract对象,这个对象是用来解析请求的一个模板的,不如请求方式,路径,参数等。 还有 Feign.Builder,日志工厂等对象。

代码语言:javascript
复制
@Configuration
public class FeignClientsConfiguration { 

@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Autowired(required = false)
private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();
@Autowired(required = false)
private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();
@Autowired(required = false)
private Logger logger;
@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() { 

return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}
@Bean
@ConditionalOnMissingBean
public Encoder feignEncoder() { 

return new SpringEncoder(this.messageConverters);
}
@Bean
@ConditionalOnMissingBean
public Contract feignContract(ConversionService feignConversionService) { 

return new SpringMvcContract(this.parameterProcessors, feignConversionService);
}
@Bean
public FormattingConversionService feignConversionService() { 

FormattingConversionService conversionService = new DefaultFormattingConversionService();
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) { 

feignFormatterRegistrar.registerFormatters(conversionService);
}
return conversionService;
}
@Configuration
@ConditionalOnClass({ 
 HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration { 

@Bean
@Scope("prototype")
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "feign.hystrix.enabled")
public Feign.Builder feignHystrixBuilder() { 

return HystrixFeign.builder();
}
}
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() { 

return Retryer.NEVER_RETRY;
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) { 

return Feign.builder().retryer(retryer);
}
@Bean
@ConditionalOnMissingBean(FeignLoggerFactory.class)
public FeignLoggerFactory feignLoggerFactory() { 

return new DefaultFeignLoggerFactory(logger);
}

2.构建一个Feign.Builder对象

代码语言:javascript
复制
protected Feign.Builder feign(FeignContext context) { 

FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
Logger logger = loggerFactory.create(this.type);
// @formatter:off
Feign.Builder builder = get(context, Feign.Builder.class)
// required values
.logger(logger)
.encoder(get(context, Encoder.class))
.decoder(get(context, Decoder.class))
.contract(get(context, Contract.class));
// @formatter:on
configureFeign(context, builder);
return builder;
}

在上下文当中去获取Feign.Builder,并设置编码,解码器,模板解析,日志等,这些对象都是在FeignClientsConfiguration 这个类中创建的。

代码语言:javascript
复制
	protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) { 

Client client = getOptional(context, Client.class);
if (client != null) { 

builder.client(client);
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?");
}

在loadBalance方法中,又在builder,设置了一个client 对象,这个对象是LoadBalancerFeignClient的这么一个对象,他在是FeignRibbonClientAutoConfiguration这个自动装备类,有个Import注解的内容加载了一个DefaultFeignLoadBalancedConfiguration对象,里面有如下代码

代码语言:javascript
复制
@Configuration
class DefaultFeignLoadBalancedConfiguration { 

@Bean
@ConditionalOnMissingBean
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
SpringClientFactory clientFactory) { 

return new LoadBalancerFeignClient(new Client.Default(null, null),
cachingFactory, clientFactory);
}
}

feign类

代码语言:javascript
复制
  public <T> T target(Target<T> target) { 

return build().newInstance(target);
}

build返回了一个ReflectiveFeign对象

代码语言:javascript
复制
    public Feign build() { 

SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
}

ReflectiveFeign类 可以看到返回一个代理对象

代码语言:javascript
复制
  public <T> T newInstance(Target<T> target) { 

// 通过contract 将一个个方法解析成MethodHandler对象。实际上是SynchronousMethodHandler
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) { 

if (method.getDeclaringClass() == Object.class) { 

continue;
} else if(Util.isDefault(method)) { 

DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else { 

methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
// 返回一个动态代理对象
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{ 
target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) { 

defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
static class FeignInvocationHandler implements InvocationHandler { 

private final Target target;
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) { 

this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 

if ("equals".equals(method.getName())) { 

try { 

Object
otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) { 

return false;
}
} else if ("hashCode".equals(method.getName())) { 

return hashCode();
} else if ("toString".equals(method.getName())) { 

return toString();
}
return dispatch.get(method).invoke(args);
}

通过contract 将一个个方法解析成MethodHandler对象。实际上是SynchronousMethodHandler。 当请求过来后会进入到FeignInvocationHandlerinvoke方法,最后获取相应的MethodHandler去执行

流程图

在这里插入图片描述
在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/188893.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年9月27日 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、feign测试代码
  • 二、寻找入口
    • 1.@EnableFeignClients干了什么?
    相关产品与服务
    文件存储
    文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档