Feign原理_feign基于什么协议

Feign原理_feign基于什么协议一、feign测试代码1.服务接口上标注@FeignClient@FeignClient(“order”)publicinterfaceOrderServiceFeign{@GetMapping(“/getOrder”)StringgetOrder();}2.调用方可以直接注入然后直接调用访问@RestController@RequestMapping(“/feign”)publicclassFeignTestController{@Autow

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一、feign测试代码

1.服务接口上标注@FeignClient

@FeignClient("order")
public interface OrderServiceFeign { 
   
    @GetMapping("/getOrder")
    String getOrder();
}

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

@RestController
@RequestMapping("/feign")
public class FeignTestController { 
   

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

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

@SpringBootApplication
@EnableFeignClients
public class UserServiceApplication { 
   
    public static void main(String[] args) { 
   
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
二、寻找入口

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

1.@EnableFeignClients干了什么?
@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容器

public class RegistrarBean { 
   
}

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

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 类

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(SpringRegistrarTest.class)
public @interface EnableRegistrar { 
   
}

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

@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这个类

public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) { 
   
		// 注册默认的配置类
		registerDefaultConfiguration(metadata, registry);
		// 注册feign客户端
		registerFeignClients(metadata, registry);
	}

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

		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客户端对象的时候,这个方法就会被调用

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类

@Bean
public FeignContext feignContext() { 

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

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

FeignContext类

	public FeignContext() { 

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

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

@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对象

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 这个类中创建的。

	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对象,里面有如下代码

@Configuration
class DefaultFeignLoadBalancedConfiguration { 

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

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

feign类

  public <T> T target(Target<T> target) { 

return build().newInstance(target);
}

build返回了一个ReflectiveFeign对象

    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类
可以看到返回一个代理对象

  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去执行

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • VHDL与Verilog的混合设计[通俗易懂]

    VHDL与Verilog的混合设计[通俗易懂]VHDL调用Verilog模块的时候,要在实例化模块前,加上“verilogmodelGM:”VHDL调用verlogverilogmodule:modulem(a,b,c);inputa,b;outputc;…endmodule调用如下:compoentmport(a:instd_logic;…

  • matlab 柱状图不同颜色(取巧哈)[通俗易懂]

    matlab 柱状图不同颜色(取巧哈)[通俗易懂]以前写过一个颜色索引的柱状图,但是年代久远想不起来了。今天需要出一个不同颜色的柱状图,看了一下博客,首先使用了matlab中的children,但是发现颜色没有变化。后来从另外的博客中发现,matalb2014以后的版本这个功能不能用了,what?只能用bar。好吧,用bar一遍一遍写,这里做个取巧的方式哈。mydata=[0.5,1.5,2.5,3.5,4.5];figure(1)holdonfori=1:length(mydata)h=bar(i,mydata.

    2022年10月10日
  • [转]Microsoft SQL Server 2008 安装图集[通俗易懂]

    [转]Microsoft SQL Server 2008 安装图集[通俗易懂]原文:http://blog.csdn.net/ranzj/archive/2008/08/25/2828778.aspx 见下:———————————————————————————————————…

  • 《斯坦福算法博弈论二十讲》学习笔记(持续更新)

    《斯坦福算法博弈论二十讲》学习笔记(持续更新)《斯坦福算法博弈论二十讲》学习笔记(持续更新)第一章简介和实例第二章机制设计基础第三章迈尔森引理第四章算法机制设计第一章简介和实例羽毛球比赛中的规则漏洞12年伦敦奥运会的羽毛球赛事中发生了一件钻规则漏洞的“丑闻”。赛制规则如下:共分为两个小组,每个小组四只队伍,小组赛阶段各个小组的前两名晋级,A组第1名对阵B组第2名,A组第2名对阵B组第1名,之后进行两两淘汰赛。TZ是本届赛事公认的强队,已经提前小组赛出线。WY与JK进行一场小组赛,二者之间的胜者将会对阵TZ,因此两只队伍都不想赢,都在

    2022年10月15日
  • 好看又有趣的404页面设计[通俗易懂]

    好看又有趣的404页面设计[通俗易懂]404页面作为提示错误的媒介,起着承上启下的作品,既要保证用户在看到错误页面的同时不会直接退出,又要给用户提供新的操作选择,两者兼顾才会有更好的用户体验。现在更多设计师愿意到可以到乐于分享学习经验的集设网www.ijishe.com,优秀的成功案例,可以开拓设计师的思路,值得在下一个项目中积攒经验!404页面这样设计好看又有趣-集设设计没思路,那么就需要从这几个方面着手:1、了解产品的背景,产品未来发展方向。2、去搜UI

  • ASP.NET中DropDownList 的使用

    ASP.NET中DropDownList 的使用1.如何避免DropDownList下拉框中重复值出现?AppendDataBoundItems:为是否填加重复值。真为添加,假为不填加 将DropDownList控件中AppendDataBoundItems属性设置为“False”即可。 2.如何给DropDownList添加项?//1.PreRender事件,在呈现该页前激发protectedvoidDropDow…

    2022年10月17日

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号