前言一、使用示例二、注册FeignClients三、实例化FeignClients四、FeignContext总结
前言
在spring cloud alibaba 中使用OpenFeign,可以用来使用HTTP请求访问远程服务。
一、使用示例
1、引入依赖
org.springframework.cloud spring-cloud-starter-openfeign3.0.0
2、启动类
加上注解@EnableFeignClients
@SpringBootApplication @EnableFeignClients public class CloudApplication { public static void main(String[] args) { SpringApplication.run(CloudApplication.class, args); System.out.println("started..."); } }
3、创建服务api接口
用@FeignClient 注解标识,value = “server” 表示要访问的服务提供者的名称是server
@FeignClient(value = "server") @Component public interface ServerApi { @GetMapping("/server/test/{id}") Map test(@PathVariable String id); }
4、Controller接口
@RestController @RequestMapping("/client") public class ClientController { @Autowired private ServerApi serverApi; @GetMapping("/test/{id}") public Map test(@PathVariable String id){ return serverApi.test(id); } }二、注册FeignClients
1、@EnableFeignClients
在启动类上加上了注解@EnableFeignClients,该注解导入了配置类@import(FeignClientsRegistrar.class)
2、FeignClientsRegistrar
实现了 importBeanDefinitionRegistrar 接口,重写了 registerBeanDefinitions( ) 方法,
在配置类加载过程中,会回调该方法
loadBeanDefinitionsFromRegistrars(configClass.getimportBeanDefinitionRegistrars());
3、回调方法
FeignClientsRegistrar.java @Override public void registerBeanDefinitions(Annotationmetadata metadata, BeanDefinitionRegistry registry) { //注册默认的配置类 registerDefaultConfiguration(metadata, registry); //注册feign客户端 FeignClients registerFeignClients(metadata, registry); }
4、注册默认的配置类
private void registerDefaultConfiguration(Annotationmetadata metadata, BeanDefinitionRegistry registry) { //获取 @EnableFeignClients 注解信息 MapdefaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } //注册客户端配置类 registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }
注册 FeignClientSpecification 类
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class); builder.addConstructorArgValue(name); builder.addConstructorArgValue(configuration); registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition()); }
5、注册feign客户端
(1)registerFeignClients( )
public void registerFeignClients(Annotationmetadata metadata, BeanDefinitionRegistry registry) { linkedHashSetcandidateComponents = new linkedHashSet<>(); //1、获取 @EnableFeignClients 注解信息 Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName()); //@FeignClient 注解类型过滤器 AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class); final Class>[] clients = attrs == null ? null : (Class>[]) attrs.get("clients"); //2、获取包含@FeignClient 注解的接口 if (clients == null || clients.length == 0) { //注解中没有指定要加载的 FeignClient 接口类,则进行扫描指定的包或当前处理的配置类所在的包 //获取扫描器 ClassPathScanningCandidateComponentProvider scanner = getScanner(); scanner.setResourceLoader(this.resourceLoader); //设置注解类型过滤器 scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class)); //获取@FeignClient注解上的value/basePackages/basePackageClasses 或当前处理的配置类所在的包 Set basePackages = getbasePackages(metadata); for (String basePackage : basePackages) { //找出包含@FeignClient 注解的接口 candidateComponents.addAll(scanner.findCandidateComponents(basePackage)); } } else { //注解中指定了要加载的 FeignClient 接口类 for (Class> clazz : clients) { candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz)); } } //3、解析@FeignClient接口 for (BeanDefinition candidateComponent : candidateComponents) { if (candidateComponent instanceof AnnotatedBeanDefinition) { // verify annotated class is an interface AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent; Annotationmetadata annotationmetadata = beanDefinition.getmetadata(); Assert.isTrue(annotationmetadata.isInterface(), "@FeignClient can only be specified on an interface"); Map attributes = annotationmetadata .getAnnotationAttributes(FeignClient.class.getCanonicalName()); //@FeignClient制定的服务端的名称 String name = getClientName(attributes); //根据服务端的名称注册FeignClientSpecification类 registerClientConfiguration(registry, name, attributes.get("configuration")); //4、注册FeignClient registerFeignClient(registry, annotationmetadata, attributes); } } }
6、注册FeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, Annotationmetadata annotationmetadata, Map三、实例化FeignClientsattributes) { String className = annotationmetadata.getClassName(); //设置类型为 FeignClientFactoryBean,实现了FactoryBean
上一步中注册的 FeignClient 类型为 FeignClientFactoryBean 类型,FeignClientFactoryBean 实现了FactoryBean, InitializingBean, ApplicationContextAware接口,则在实例化过程中会调用 getObject( ) 和 afterPropertiesSet( ) 方法。
1、afterPropertiesSet( )
只是做了一些校验
public void afterPropertiesSet() { Assert.hasText(contextId, "Context id must be set"); Assert.hasText(name, "Name must be set"); }
2、 getObject( )
public Object getObject() { return getTarget(); }
T getTarget() { //这里获取到feign的上下文信息,下面会对FeignContext做分析 FeignContext context = applicationContext.getBean(FeignContext.class); //创建 Feign.Builder Feign.Builder builder = feign(context); if (!StringUtils.hasText(url)) { //没有指定服务端的url,使用服务端的名称 if (!name.startsWith("http")) { url = "http://" + name; } else { url = name; } url += cleanPath(); //获取带有负载均衡的客户端 return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url)); } if (StringUtils.hasText(url) && !url.startsWith("http")) { url = "http://" + url; } String url = this.url + cleanPath(); Client client = getOptional(context, Client.class); if (client != null) { if (client instanceof FeignBlockingLoadBalancerClient) { // not load balancing because we have a url, // but Spring Cloud LoadBalancer is on the classpath, so unwrap client = ((FeignBlockingLoadBalancerClient) client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url)); }
3、创建 Feign.Builder
(1)feign( )
protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(type); // @formatter:off //根据FeignContext中的configuration,也就是@FeignClient中指定的configuration属性创建,没有指定configuration属性时,根据默认的一些配置创建,默认的配置定义在FeignClientsConfiguration配置类中 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 //配置Feign.Builder configureFeign(context, builder); //自定义的一些配置 applyBuildCustomizers(context, builder); return builder; }
(2)configureFeign( )
protected void configureFeign(FeignContext context, Feign.Builder builder) { //获取FeignClientProperties @ConfigurationProperties("feign.client") FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class); FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class); setInheritParentContext(feignClientConfigurer.inheritParentConfiguration()); if (properties != null && inheritParentContext) { if (properties.isDefaultToProperties()) { //配置@FeignClient中指定的configuration属性:Logger.Level、Retryer、ErrorDecoder、FeignErrorDecoderFactory、Request.Options、RequestInterceptor、QueryMapEncoder、ExceptionPropagationPolicy configureUsingConfiguration(context, builder); //配置FeignClientProperties 中的默认属性 configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); //配置FeignClientProperties 中指定的属性 configureUsingProperties(properties.getConfig().get(contextId), builder); } else { configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(contextId), builder); configureUsingConfiguration(context, builder); } } else { configureUsingConfiguration(context, builder); } }
(3)applyBuildCustomizers( )
自定义配置回调 FeignBuilderCustomizer
private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) { MapcustomizerMap = context.getInstances(contextId, FeignBuilderCustomizer.class); if (customizerMap != null) { customizerMap.values().stream().sorted(AnnotationAwareOrderComparator.INSTANCE) .forEach(feignBuilderCustomizer -> feignBuilderCustomizer.customize(builder)); } }
4、loadBalance( )
protectedT loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget target) { //在DefaultFeignLoadBalancerConfiguration中默认创建FeignBlockingLoadBalancerClient Client client = getOptional(context, Client.class); if (client != null) { //设置client builder.client(client); //默认是DefaultTargeter Targeter targeter = get(context, Targeter.class); //调用target() return targeter.target(this, builder, context, target); } throw new IllegalStateException( "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?"); }
5、target( )
publicT target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget target) { return feign.target(target); }
publicT target(Target target) { //创建feign 目标对象 return build().newInstance(target); }
6、build( )
增强feign里的属性,创建ReflectiveFeign
public Feign build() { //通过自定义的Capability来增强各属性 Client client = Capability.enrich(this.client, capabilities); Retryer retryer = Capability.enrich(this.retryer, capabilities); ListrequestInterceptors = this.requestInterceptors.stream() .map(ri -> Capability.enrich(ri, capabilities)) .collect(Collectors.toList()); Logger logger = Capability.enrich(this.logger, capabilities); Contract contract = Capability.enrich(this.contract, capabilities); Options options = Capability.enrich(this.options, capabilities); Encoder encoder = Capability.enrich(this.encoder, capabilities); Decoder decoder = Capability.enrich(this.decoder, capabilities); InvocationHandlerFactory invocationHandlerFactory = Capability.enrich(this.invocationHandlerFactory, capabilities); QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities); SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding); ParseHandlersByName handlersByName = new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, errorDecoder, synchronousMethodHandlerFactory); return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder); } }
7、newInstance( )
publicT newInstance(Target target) { //方法名称与SynchronousMethodHandler的映射 Map nameToHandler = targetToHandlersByName.apply(target); //方法与SynchronousMethodHandler的映射 Map methodToHandler = new linkedHashMap (); List defaultMethodHandlers = new linkedList (); 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))); } } //创建FeignInvocationHandler 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; }
最终创建出了@FeignClient接口的代理对象
四、FeignContextFeignContext 作为feign的上下文,包含了 FeignClient中指定的一些配置信息
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(Feign.class) @EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class }) @import(DefaultGzipDecoderConfiguration.class) public class FeignAutoConfiguration { private static final Log LOG = LogFactory.getLog(FeignAutoConfiguration.class); @Autowired(required = false) private Listconfigurations = new ArrayList<>(); @Bean public HasFeatures feignFeature() { return HasFeatures.namedFeature("Feign", Feign.class); } //创建bean FeignContext @Bean public FeignContext feignContext() { FeignContext context = new FeignContext(); //设置 FeignClientSpecification 类 context.setConfigurations(this.configurations); return context; } ... }
FeignClientSpecification 类的注册在第二步中已经分析过。
总结1、给@FeignClient接口创建代理对象
2、创建增强handler FeignInvocationHandler
3、创建SynchronousMethodHandler或DefaultMethodHandler 处理真正的方法
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)