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

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

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

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

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux fusion io简介,linux – 收集FusionIO库存

    linux fusion io简介,linux – 收集FusionIO库存我需要编写一个脚本,从Linux服务器收集FusionIO驱动器的库存数据.我能找到的唯一方法是fio-status实用程序,但它的目的是输出人类可读的文本,而不是机器可解析的文本.我可以刮它,但那很脏.我检查/proc/fusion但它没有足够的信息可供任何使用.我希望有更好的方法,可以通过某种方式与libiodrivesdk.so或已经存在的实用程序进行交互来完成这项工作.我最初使用的是…

    2022年10月22日
    0
  • docker镜像操作_docker 本地镜像

    docker镜像操作_docker 本地镜像前言Docker的三大核心概念:镜像、容器、仓库。初学者对镜像和容器往往分不清楚,学过面向对象的应该知道类和实例,这跟面向对象里面的概念很相似我们可以把镜像看作类,把容器看作类实例化后的对象。|

    2022年7月29日
    3
  • Mac搭建PHP环境[通俗易懂]

    Mac搭建PHP环境[通俗易懂]一、配置环境1启动APacheMac系统本身自带Apache,所以可以直接打开。以下为两种方式:打开“系统设置偏好(SystemPreferences)”->“共享(Sharing)”->“Web共享(WebSharing)”打开“终端(terminal)“,然后输入sudoapachectlstart,使Apache运行。开启Apache就是开启Web共享,这时输入“http://localhost”,就可以看到“Itworks!”的页面Apache默认根目

    2022年9月22日
    0
  • DropDownList绑定数据库「建议收藏」

    DropDownList绑定数据库「建议收藏」//获取文本this.DropDownList1.SelectedItem.Text;本类内使用protectedvoidPage_Load(objectsender,EventArgse)   {       this.DropDownList1.Items.Clear();       this.DropDownList1.DataSource=DbHelperSQL.Query(“select*fromauthors”).Tables[0];       this.Drop

    2022年10月8日
    0
  • 前端必备技能之如何制作精灵图「建议收藏」

    前端必备技能之如何制作精灵图「建议收藏」为什么要用精灵图?比如京东首页的这些icon,如果每个icon都去请求一个资源。是非常浪费资源的。因为我们浏览器在同一个域名下并发加载的资源(CSS、JS、图片等)数量是有限的。我们可以将这些icon放到一张图片,通过截取这张图片的不同的区域,拿到不同的icon制作一个背景透明的Icon截图你喜欢的logo,最好是正方形。打开ps,使用快速选择工具选择白色区域ctrl+x删除…

    2022年6月3日
    126
  • iframe自适应高度_iframe动态自适应高度

    iframe自适应高度_iframe动态自适应高度转载(来源忘记了。。。)<!–内容部分–><divclass=”con”><iframeid=”mainFrame”name=”mainFrame”src=”main.html”style=”width:100%;”frameborder=”0″scrolling=”no”onLoad=”this.height=100″></iframe></div>$(function(){//时间控制每

    2022年10月12日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

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