SpringCloud之feign的使用以及源码解析
2021/9/18 1:05:04
本文主要是介绍SpringCloud之feign的使用以及源码解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
SpringCloud之feign的使用以及源码解析
feign的作用
Feign可以做到使用 HTTP 请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程方
法,更感知不到这是个 HTTP 请求。它像 Dubbo 一样,consumer 直接调用接口方法调用 provider,而不
需要通过常规的 Http Client 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法
一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
feign的使用
main方法上加上注解@EnableFeignClients
@EnableDiscoveryClient @SpringBootApplication @EnableFeignClients public class OrderMain83 { public static void main(String[] args) { SpringApplication.run(OrderMain83.class,args); } }
定义自己的service
@Component @FeignClient(value = "cloudalibaba-nacos-provider") public interface PaymentService { @GetMapping(value = "/payment/get/{id}") CommonResult<Payment> getPaymentById(@PathVariable("id") Long id); }
定义自己的controller
@RestController public class FeignController { @Autowired private PaymentService paymentService; @GetMapping(value = "nacos/consumer/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){ return paymentService.getPaymentById(id); } }
通过这两个步骤,就可以像调本地接口一样调用远程服务了
feign源码解析
代码整体流程图
EnableFeignClients注解的解析
先从@EnableFeignClients注解看起
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients {
import了一个FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
FeignClientsRegistrar继承了ImportBeanDefinitionRegistrar,所以我们看FeignClientsRegistrar的registerBeanDefinitions方法,看看注入了什么BeanDefinition
FeignClientsRegistrar#registerFeignClients方法
首先拿到注解EnableFeignClients的属性
Map<String, Object> attrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName());
new了一个注解的过滤器
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter( FeignClient.class);
然后就拿到需要扫描的包
basePackages = getBasePackages(metadata);
然后是一个for循环,遍历basePackages,然后是根据包找到有FeignClient注解的Components
Set<BeanDefinition> candidateComponents = scanner .findCandidateComponents(basePackage);
然后就到了注册这个BeanDefinition的方法了registerFeignClient(registry, annotationMetadata, attributes);
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) { String className = annotationMetadata.getClassName(); BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(FeignClientFactoryBean.class); validate(attributes); definition.addPropertyValue("url", getUrl(attributes)); definition.addPropertyValue("path", getPath(attributes)); String name = getName(attributes); definition.addPropertyValue("name", name); String contextId = getContextId(attributes); definition.addPropertyValue("contextId", contextId); definition.addPropertyValue("type", className); definition.addPropertyValue("decode404", attributes.get("decode404")); definition.addPropertyValue("fallback", attributes.get("fallback")); definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory")); definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); String alias = contextId + "FeignClient"; 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); }
一长串的definition的设置属性,有url,path,fallback等。
其中关键的是
BeanDefinitionBuilder definition = BeanDefinitionBuilder .genericBeanDefinition(FeignClientFactoryBean.class);
bean的类型被改成了FeignClientFactoryBean。
Bean的初始化
先看一下FeignClientFactoryBean
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
FeignClientFactoryBean实现了FactoryBean,说明bean是自定义实例化的,直接看getObject方法
@Override public Object getObject() throws Exception { return getTarget(); }
getObject方法有调用了getTarget方法,直接return值,return的方法是
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, this.name, this.url)); }
看loadBalance方法,
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) { //把LoadBalancerFeignClient放进去 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?"); }
根据context拿到targeter,执行targeter.target(this, builder, context, target);
由于没引入Hystrix,只引入了feign,所以直接调用feign.target(target)
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); }
进入feign.target
public <T> T target(Target<T> target) { return build().newInstance(target); }
先进入build方法
public Feign build() { SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } }
构造了一些属性,然后返回了new出来ReflectiveFeign返回,然后再进入newInstance方法
public <T> T newInstance(Target<T> target) { 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 methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method))); } } //拿到InvocationHandler 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; }
先解析了这个类里面所有的方法
放进去methodToHandler
得到InvocationHandler (FeignInvocationHandler)
生成动态代理
生成的methodToHandler
生成的动态代理类
就这样bean就被创建出来了。
方法的调用
之前了解过JDK动态代理的会知道,调用被代理类的方法时,会先进入InvocationHandler 的invoke方法,也就是FeignInvocationHandler的invoke方法
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); }
然后就会调用dispatch.get(method).invoke(args)
dispatch就是生成ReflectiveFeign时放进去的SynchronousMethodHandler,接下来就会调用
SynchronousMethodHandler的executeAndDecode
Request request = targetRequest(template); response = client.execute(request, options);
生成请求,调用client的execute方法,client就是FeignClientFactoryBean的loadBalance放进去的LoadBalancerFeignClient
Client client = getOptional(context, Client.class);
然后就去到LoadBalancerFeignClient的execute方法
然后就是整合ribbon去实现负载均衡,拼装request,去调用远程方法。
这篇关于SpringCloud之feign的使用以及源码解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-15JavaMailSender是什么,怎么使用?-icode9专业技术文章分享
- 2024-11-15JWT 用户校验学习:从入门到实践
- 2024-11-15Nest学习:新手入门全面指南
- 2024-11-15RestfulAPI学习:新手入门指南
- 2024-11-15Server Component学习:入门教程与实践指南
- 2024-11-15动态路由入门:新手必读指南
- 2024-11-15JWT 用户校验入门:轻松掌握JWT认证基础
- 2024-11-15Nest后端开发入门指南
- 2024-11-15Nest后端开发入门教程
- 2024-11-15RestfulAPI入门:新手快速上手指南