SpringCloud-OpenFeign学习-1

Feign调用原理


一、Springcloud-openfeign使用

示例:整合springboot项目的使用:版本对应关系

  1. 阿里官方对应版本:SpringBoot-SpringCloud-SpringCloudAlibaba各版本对应关系
  2. Spring官方:SpringBoot-SpringCloud版本对应关系,需格式化json串
  3. SpringCloud官方:点击对应的Reference文档,文档内有版本描述
  4. 版本对应不上,可能会出现兼容性问题,建议版本保持对应。

  1. 引入依赖
        <spring.boot.version>2.4.5</spring.boot.version>
        <spring.cloud.version>2020.0.2</spring.cloud.version>

		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-openfeign</artifactId>
		</dependency>
  1. 开启feign客户端注解
    SpringCloud-OpenFeign学习-1
/**
 * 服务提供方启动类
 */
@EnableFeignClients
@SpringBootApplication
public class OauthServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(OauthServerApplication.class,args);
    }

}
/**
 * feign客户端,能力提供者
 */
@RestController
@RequestMapping("/test")
public class TestFeignController {

    @GetMapping("/get")
    public String getTest(){
        return "ok";
    }
}
# 服务提供者应用配置
server:
  port: 8082
spring:
  application:
    name: test-application

/**
 * @Description:消费端启动类
 * @ClassName:ResourceApplication
 */
@EnableFeignClients
@SpringBootApplication
public class ResourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(ResourceApplication.class,args);
    }
}
@FeignClient(name = "oauthServiceFeign", path = "/test", url = "http://localhost:8082")
public interface OauthServiceFeign {

    @GetMapping("/get")
    String getTest();
}
# 服务消费端应用配置
server:
  port: 8081
spring:
  application:
    name: resource-application
  1. 测试用例
/**
 * 消费端测试类,调用服务端远程服务
 */
@SpringBootTest
@RunWith(SpringRunner.class)
class OauthServiceFeignTest {

    @Autowired
    OauthServiceFeign oauthServiceFeign;

    @Test
    void getTest() {
        String test = oauthServiceFeign.getTest();
        System.out.println("调用远程feign接口,返回结果: " + test);
    }
}
  1. 返回结果
2021-05-31 17:25:23.877  INFO 9252 --- [           main] c.t.r.feign.OauthServiceFeignTest        : Started OauthServiceFeignTest in 2.016 seconds (JVM running for 3.004)
调用远程feign接口,返回结果: ok
2021-05-31 17:25:24.274  INFO 9252 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor  : Shutting down ExecutorService 'applicationTaskExecutor'
Disconnected from the target VM, address: '127.0.0.1:63339', transport: 'socket'

Process finished with exit code 0

二、Open-Feign使用步骤解析

1. 引入spring-cloud-starter-openfeign

该pom引入:spring-cloud-openfeign-core,springboot启动过程中会加载spring.factories文件,并初始化该配置文件的配置类,从而达到自动装配。
SpringCloud-OpenFeign学习-1

# spring.factories 内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration

1.简单分析spring.factories做了哪些事情

  • FeignHalAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@ConditionalOnClass(RepresentationModel.class) // 指定RepresentationModel类存在类路径时,初始化该配置类   (该示例不存在该类,故该配置类在启动时未初始化) 
@AutoConfigureAfter({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
		RepositoryRestMvcAutoConfiguration.class })
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
public class FeignHalAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
			ObjectProvider<ObjectMapper> objectMapper, ObjectProvider<HalConfiguration> halConfiguration,
			ObjectProvider<MessageResolver> messageResolver, ObjectProvider<CurieProvider> curieProvider,
			ObjectProvider<LinkRelationProvider> linkRelationProvider) {

		ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
		mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

		HalConfiguration configuration = halConfiguration.getIfAvailable(HalConfiguration::new);

		CurieProvider curieProviderInstance = curieProvider
				.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));

		Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
				linkRelationProvider.getIfAvailable(), curieProviderInstance, messageResolver.getIfAvailable(),
				configuration);

		mapper.setHandlerInstantiator(halHandlerInstantiator);

		if (!Jackson2HalModule.isAlreadyRegisteredIn(mapper)) {
			Jackson2HalModule halModule = new Jackson2HalModule();
			mapper.registerModule(halModule);
		}

		TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
				RepresentationModel.class);
		converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
		converter.setObjectMapper(mapper);
		return converter;
	}

}

  • FeignAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class,
		FeignEncoderProperties.class })
@Import(DefaultGzipDecoderConfiguration.class) // 引入了该配置类,并初始化该配置类:主要配置默认 Gzip 解码器,具体作用这里不做说明
public class FeignAutoConfiguration {

	private static final Log LOG = LogFactory.getLog(FeignAutoConfiguration.class);

	@Autowired(required = false) // 从容器中注入FeignClientSpecification实例 创建时机下面说明
	private List<FeignClientSpecification> configurations = new ArrayList<>();

	@Bean	// 作用不做说明
	public HasFeatures feignFeature() {
		return HasFeatures.namedFeature("Feign", Feign.class);
	}

	@Bean	// 初始化feign上下文
	public FeignContext feignContext() {
		FeignContext context = new FeignContext();
		// 
		context.setConfigurations(this.configurations);
		return context;
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Module.class, Page.class, Sort.class }) // 该示例不存在该类,故该配置类在启动时未初始化
	@ConditionalOnProperty(value = "feign.autoconfiguration.jackson.enabled", havingValue = "true")
	protected static class FeignJacksonConfiguration {

		@Bean
		@ConditionalOnMissingBean(PageJacksonModule.class)
		public PageJacksonModule pageJacksonModule() {
			return new PageJacksonModule();
		}

		@Bean
		@ConditionalOnMissingBean(SortJacksonModule.class)
		public SortJacksonModule sortModule() {
			return new SortJacksonModule();
		}

	}

	@Configuration(proxyBeanMethods = false)
	@Conditional(FeignCircuitBreakerDisabledConditions.class)
	protected static class DefaultFeignTargeterConfiguration {

		@Bean
		@ConditionalOnMissingBean // feign代理类
		public Targeter feignTargeter() {
			return new DefaultTargeter();
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(CircuitBreaker.class)
	@ConditionalOnProperty(value = "feign.circuitbreaker.enabled", havingValue = "true")
	protected static class CircuitBreakerPresentFeignTargeterConfiguration {

		@Bean
		@ConditionalOnMissingBean(CircuitBreakerFactory.class)
		public Targeter defaultFeignTargeter() {
			return new DefaultTargeter();
		}

		@Bean
		@ConditionalOnMissingBean
		@ConditionalOnBean(CircuitBreakerFactory.class)
		public Targeter circuitBreakerFeignTargeter(CircuitBreakerFactory circuitBreakerFactory) {
			return new FeignCircuitBreakerTargeter(circuitBreakerFactory);
		}

	}

	// the following configuration is for alternate feign clients if
	// SC loadbalancer is not on the class path.
	// see corresponding configurations in FeignLoadBalancerAutoConfiguration
	// for load-balanced clients.
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(ApacheHttpClient.class) // 该示例不存在该类,故该配置类在启动时未初始化
	@ConditionalOnMissingBean(CloseableHttpClient.class)
	@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
	@Conditional(HttpClient5DisabledConditions.class)
	protected static class HttpClientFeignConfiguration {

		private final Timer connectionManagerTimer = new Timer(
				"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);

		@Autowired(required = false)
		private RegistryBuilder registryBuilder;

		private CloseableHttpClient httpClient;

		@Bean
		@ConditionalOnMissingBean(HttpClientConnectionManager.class)
		public HttpClientConnectionManager connectionManager(
				ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
				FeignHttpClientProperties httpClientProperties) {
			final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
					httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
					httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
					httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
			this.connectionManagerTimer.schedule(new TimerTask() {
				@Override
				public void run() {
					connectionManager.closeExpiredConnections();
				}
			}, 30000, httpClientProperties.getConnectionTimerRepeat());
			return connectionManager;
		}

		@Bean
		public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
				HttpClientConnectionManager httpClientConnectionManager,
				FeignHttpClientProperties httpClientProperties) {
			RequestConfig defaultRequestConfig = RequestConfig.custom()
					.setConnectTimeout(httpClientProperties.getConnectionTimeout())
					.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
			this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager)
					.setDefaultRequestConfig(defaultRequestConfig).build();
			return this.httpClient;
		}

		@Bean
		@ConditionalOnMissingBean(Client.class)
		public Client feignClient(HttpClient httpClient) {
			return new ApacheHttpClient(httpClient);
		}

		@PreDestroy
		public void destroy() {
			this.connectionManagerTimer.cancel();
			if (this.httpClient != null) {
				try {
					this.httpClient.close();
				}
				catch (IOException e) {
					if (LOG.isErrorEnabled()) {
						LOG.error("Could not correctly close httpClient.");
					}
				}
			}
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(OkHttpClient.class) // 该示例不存在该类,故该配置类在启动时未初始化
	@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
	@ConditionalOnProperty("feign.okhttp.enabled")
	protected static class OkHttpFeignConfiguration {

		private okhttp3.OkHttpClient okHttpClient;

		@Bean
		@ConditionalOnMissingBean(ConnectionPool.class)
		public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
				OkHttpClientConnectionPoolFactory connectionPoolFactory) {
			Integer maxTotalConnections = httpClientProperties.getMaxConnections();
			Long timeToLive = httpClientProperties.getTimeToLive();
			TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
			return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
		}

		@Bean
		public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
				FeignHttpClientProperties httpClientProperties) {
			Boolean followRedirects = httpClientProperties.isFollowRedirects();
			Integer connectTimeout = httpClientProperties.getConnectionTimeout();
			Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
			this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
					.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
					.connectionPool(connectionPool).build();
			return this.okHttpClient;
		}

		@PreDestroy
		public void destroy() {
			if (this.okHttpClient != null) {
				this.okHttpClient.dispatcher().executorService().shutdown();
				this.okHttpClient.connectionPool().evictAll();
			}
		}

		@Bean
		@ConditionalOnMissingBean(Client.class)
		public Client feignClient(okhttp3.OkHttpClient client) {
			return new OkHttpClient(client);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(ApacheHttp5Client.class) // 该示例不存在该类,故该配置类在启动时未初始化
	@ConditionalOnMissingBean(org.apache.hc.client5.http.impl.classic.CloseableHttpClient.class)
	@ConditionalOnProperty(value = "feign.httpclient.hc5.enabled", havingValue = "true")
	@Import(org.springframework.cloud.openfeign.clientconfig.HttpClient5FeignConfiguration.class)
	protected static class HttpClient5FeignConfiguration {

		@Bean
		@ConditionalOnMissingBean(Client.class)
		public Client feignClient(org.apache.hc.client5.http.impl.classic.CloseableHttpClient httpClient5) {
			return new ApacheHttp5Client(httpClient5);
		}

	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass(OAuth2ClientContext.class) // 该示例不存在该类,故该配置类在启动时未初始化
	@ConditionalOnProperty("feign.oauth2.enabled")
	protected static class Oauth2FeignConfiguration {

		@Bean
		@ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class)
		@ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class })
		public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext,
				OAuth2ProtectedResourceDetails resource) {
			return new OAuth2FeignRequestInterceptor(oAuth2ClientContext, resource);
		}

	}

}
  • FeignAcceptGzipEncodingAutoConfiguration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
@ConditionalOnClass(Feign.class)
@ConditionalOnBean(Client.class)
@ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false) // 该示例不存在该配置项,故该配置类在启动时未初始化
// The OK HTTP client uses "transparent" compression.
// If the accept-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignAcceptGzipEncodingAutoConfiguration {

	@Bean
	public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(
			FeignClientEncodingProperties properties) {
		return new FeignAcceptGzipEncodingInterceptor(properties);
	}

}
  • FeignContentGzipEncodingAutoConfiguration
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
@ConditionalOnClass(Feign.class)
// The OK HTTP client uses "transparent" compression.
// If the content-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@ConditionalOnProperty("feign.compression.request.enabled") // 该示例不存在该配置项,故该配置类在启动时未初始化
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignContentGzipEncodingAutoConfiguration {

	@Bean
	public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(
			FeignClientEncodingProperties properties) {
		return new FeignContentGzipEncodingInterceptor(properties);
	}

}
  • FeignLoadBalancerAutoConfiguration
@ConditionalOnClass(Feign.class)
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })// 该示例不存在该Bean,故该配置类在启动时未初始化
@AutoConfigureBefore(FeignAutoConfiguration.class)
@AutoConfigureAfter({ BlockingLoadBalancerClientAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties(FeignHttpClientProperties.class)
@Configuration(proxyBeanMethods = false)
// Order is important here, last should be the default, first should be optional
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class,
		HttpClient5FeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {

}

2. 开启Feign注解:@EnableFeignClients


1. @EnableFeignClients 作用


1. @EnableFeignClients 参数

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class) // 重要:初始化FeignClientsRegistrar实例
public @interface EnableFeignClients {

	// basePackages()属性的别名。 允许更简洁的注释声明
	String[] value() default {};
	
	// 用于扫描带注释组件的基本包
	String[] basePackages() default {};

	// 用于指定要扫描带注释组件的包。 将扫描指定的每个类的包
	Class<?>[] basePackageClasses() default {};

	// 客户端默认配置
	Class<?>[] defaultConfiguration() default {};

	// 用@FeignClient 注释的类列表。 如果不为空,则禁用类路径扫描
	Class<?>[] clients() default {};

}

2. FeignClientsRegistrar作用

class FeignClientsRegistrar implements 
ImportBeanDefinitionRegistrar,  // 动态创建自定义Bean到Spring中: 即 @FeignClient 注解的bean的解析
ResourceLoaderAware, 
EnvironmentAware {
......

	// 解析入口, 即FeignClient Bean 注入容器时机
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
		// 解析 @EnableFeignClients 注解
		registerDefaultConfiguration(metadata, registry);
		// 解析@FeignClient注解(重点)
		registerFeignClients(metadata, registry);
	}

	public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {

		LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
		Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
		final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");

		// @EnableFeignClients没有指定clients 时,会通过扫描class创建feignclient bean
		if (clients == null || clients.length == 0) {
			// 扫描器
			ClassPathScanningCandidateComponentProvider scanner = getScanner();
			// 类加载器
			scanner.setResourceLoader(this.resourceLoader);
			// 过滤类型:指定过滤出注解类型为FeignClient的 bean 的定义
			scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
			// 拿到解析包路径:默认是@EnableFeignClients 注解所在类下的包
			Set<String> basePackages = getBasePackages(metadata);
			for (String basePackage : basePackages) {
				candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
			}
		}
		else {
			for (Class<?> clazz : clients) {
				candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
			}
		}
		
		// 拿到所有FeignClient注解的bean的定义,进行进一步处理
		for (BeanDefinition candidateComponent : candidateComponents) {
		
			if (candidateComponent instanceof AnnotatedBeanDefinition) {
				// 校验@FeignClient只能注解在接口上
				AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
				AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
				Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

				// 拿到 @FeignClient注解的所有属性值
				Map<String, Object> attributes = annotationMetadata
						.getAnnotationAttributes(FeignClient.class.getCanonicalName());

				String name = getClientName(attributes);
				// 注入@FeignClient 中指定的 configuration,注入的beanname 为 name + FeignClientSpecification
				registerClientConfiguration(registry, name, attributes.get("configuration"));
				
				// 注入 @FeignClient bean定义(重点)
				registerFeignClient(registry, annotationMetadata, attributes);
			}
		}
	}

	private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
			Map<String, Object> attributes) {
		String className = annotationMetadata.getClassName();
		Class clazz = ClassUtils.resolveClassName(className, null);
		ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
				? (ConfigurableBeanFactory) registry : null;
		String contextId = getContextId(beanFactory, attributes);
		String name = getName(attributes);
		
		// 构造FeignClient bean工厂,所有的FeignClient bean在这里生成
		FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
		factoryBean.setBeanFactory(beanFactory);
		factoryBean.setName(name);
		factoryBean.setContextId(contextId);
		factoryBean.setType(clazz);

		// 指定FeignClient 生成的步骤(简单看一下这个方法)
		BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
			factoryBean.setUrl(getUrl(beanFactory, attributes));
			factoryBean.setPath(getPath(beanFactory, attributes));
			factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
			Object fallback = attributes.get("fallback");
			if (fallback != null) {
				factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
						: ClassUtils.resolveClassName(fallback.toString(), null));
			}
			Object fallbackFactory = attributes.get("fallbackFactory");
			if (fallbackFactory != null) {
				factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
						: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
			}
			return factoryBean.getObject();
		});
		definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
		definition.setLazyInit(true);
		validate(attributes);

		AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
		beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);

		// has a default, won't be null
		boolean primary = (Boolean) attributes.get("primary");

		beanDefinition.setPrimary(primary);

		String[] qualifiers = getQualifiers(attributes);
		if (ObjectUtils.isEmpty(qualifiers)) {
			qualifiers = new String[] { contextId + "FeignClient" };
		}

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

}

	// 指定FeignClient 生成的步骤
	public static <T> BeanDefinitionBuilder genericBeanDefinition(Class<T> beanClass, Supplier<T> instanceSupplier) {
		BeanDefinitionBuilder builder = new BeanDefinitionBuilder(new GenericBeanDefinition());
		builder.beanDefinition.setBeanClass(beanClass);
		// 注意这里指定生成bean的方式,在获取时通过该方法拿到代理对象
		builder.beanDefinition.setInstanceSupplier(instanceSupplier);
		return builder;
	}



总结

本篇仅大致描述feign作用及注入流程,后续学习代理对象详细生成。
上一篇:OpenFeign 远程调用下载文件 以及上传文件


下一篇:SpringCloud中的OpenFeign的超时控制和日志增强