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)


相关推荐

  • CountDownLatch详解

    CountDownLatch详解参考:Java并发编程的艺术JDK版本:AdoptOpenJDK11.0.2+91概念CountDownLatch允许一个或者多个线程去等待其他线程完成操作。CountDownLatch接收一个int型参数,标识要等待的工作线程的个数。当然也不一定是多线程,在单线程中可以用这个int型参数标识多个操作步骤。2方法CountDownLatch2例子下面代码演示2个等待线程通过CountDownLatch去等待3个工作线程完成操作:publicclassCountDownLa.

  • c#程序中的AssemblyInfo.cs

    c#程序中的AssemblyInfo.cs在asp.net中有一个配置文件AssemblyInfo.cs主要用来设定生成的有关程序集的常规信息dll文件的一些参数,下面是默认的AssemblyInfo.cs文件的内容具体介绍//是否符合公共语言规范(CLS)[assembly:CLSCompliant(true)]//控制程序集中所有类型对COM的可访问性[assembly:ComVisible(false)]//代码的作者和这…

  • 香农编码的matlab实现总结_matlab简单代码实例

    香农编码的matlab实现总结_matlab简单代码实例用MATLAB编程实现香农编码实验四、用MATLAB编程实现香农编码⒈实验目的和要求(1)了解信源变长码的编码方法(2)掌握香农编码方法(3)掌握MATLAB的编程⒉实验主要内容使用MATLAB语言和excel对以下信源进行香农编码,并求出平均码长和编码效率。3.实验原理Ⅰ.香农编码方法将信源发出的N个消息符号按其概率的递减次序排列按下式计算第个消息的二进制代码组的码长,并取整计算第个消息的累加概…

  • SQL最常用的语句

    SQL最常用的语句语法:一步步由浅到深,这里用的都是mysql做的。基础:连接数据库:mysql-h10.20.66.32-uroot-p1234561-h后面是mysqlServer所在地址,-u后面是用户名,-p后面是密码。查看数据库showdatabases;1使用数据库usetest;1查看表showtables;1查看表结构descwinton1建表createtablet1(…

  • scrollIntoView()的用法[通俗易懂]

    scrollIntoView()的用法[通俗易懂]STARTscrollIntoView是一个与页面(容器)滚动相关的API(官方解释),该API只有boolean类型的参数能得到良好的支持(firefox36+都支持),所以在这里只讨论参数Boolean类型的情况。EXPLAIN调用方法为element.scrollIntoView()参数默认为true。参数为true时调用该函数,页面(或容器)发生滚动,使elem…

  • vim查找指令

    vim查找指令一、vi查找:当你用vi打开一个文件后,因为文件太长,如何才能找到你所要查找的关键字呢?在vi里可没有菜单-〉查找,不过没关系,你在命令模式下敲斜杆(/)这时在状态栏(也就是屏幕左下脚)就出现了“/”然后输入你要查找的关键字敲回车就可以了。如果你要继续查找此关键字,敲字符n就可以继续查找了。值得注意的是“/”是向下查找,而“?”是向上查找,而在键盘定义上“?”刚好是“/”的上档符。二、vi替换:vi/vim中可以使用:s命令来替换字符串以前只会使用一种格式来全文替换,今天发现该命令有很多种写法

发表回复

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

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