spring-cloud-openfeign-core-2.1.1.RELEASE.jarсерединаHystrixFeignПодробный процесс сборки:
@EnableFeignClients -> FeignClientsRegistrar сканирует аннотированные классы @Feign -> FeignClientFactoryBean создает FeignClient через Targeter -> Targeter создает Feign через Feign.Builder -> Feign.Builder
1. Подготовка (конфигурация)
- Класс автоматической настройки FeignAutoConfiguration
@Configuration
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
protected static class HystrixFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new HystrixTargeter();
}
}
@Configuration
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
protected static class DefaultFeignTargeterConfiguration {
@Bean
@ConditionalOnMissingBean
public Targeter feignTargeter() {
return new DefaultTargeter();
}
}
-
Когда класс feign.hystrix.HystrixFeign существует, он
HystrixTargeter
зарегистрирован какTargeter
тип фасоли -
Если класс feign.hystrix.HystrixFeign не существует, используйте
DefaultTargeter
. -
Похоже, что можно использовать настраиваемый таргетинг вместо Hystrix или по умолчанию, чтобы можно было настраивать различные функции.Вообще-то нет,так как
Targeter
даpackageУровень доступа. -
FeignClientsConfiguration
@Configuration
public class FeignClientsConfiguration {
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder().retryer(retryer);
}
@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();
}
}
}
важный:Feignи внутренний классFeign.BuilderобаpublicУровень доступа, вы можете вводить пользовательские компоненты.
2. Классы EnableFeignClients и FeignClientsRegistrar
Зарегистрируйте класс, аннотированный с помощью @FeignClient, как компонент Spring и используйте конфигурацию в аннотации.
- Импортируйте класс FeignClientsRegistrar в аннотацию @EnableFeignClients.
- Класс FeignClientsRegistrar реализует класс ImportBeanDefinitionRegistrar, и метод реализации будет выполняться фреймворком Spring.
registerBeanDefinitions(AnnotationMetaData, BeanDefinitionRegistry)
- FeignClientsRegistrar в
registerBeanDefinitions
метод вызывает два метода-
registerDefaultConfiguration
: зарегистрировать конфигурацию по умолчанию -
registerFeignClients
: Зарегистрировать клиент Feign (фокус)
-
-
registerFeignClients
:Получать@EnableFeignClients
Конфигурация, указанная в аннотации, сканирует фиктивный клиент. -
registerFeignClients
:пройти черезregisterFeignClient(BeanDefinitionRegistry, AnnotationMetadata, Map)
Метод регистрирует каждый feignClient, процесс:@FeignClient
Конфигурация, определенная в аннотации, применяет конфигурацию к фабрике bean-компонентов Spring.FeignClientFactoryBean
, через фабричный классFeignClientFactoryBean
за каждое использование@FeignClient
Производство аннотированного классаFeignClient
, подробности см. в следующем разделе
3.FeignClientFactoryBean
Фабричный компонент FeignClient.
class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware{
//...
}
методом реализацииFactoryBean#getObject()
Для создания FeignClient с помощью фреймворка Spring.
@Override
public Object getObject() throws Exception {
return getTarget();
}
/**
* 获得目标
* 1. 获得FeignContext
* 2. 从FeignContext中获得Feign构建器Feign.Builder
* 3. 从FeignContext中获得Client,判断是否进行负载均衡
* 4. 从FeignContext中获得Target,并执行Target的默认方法target(FeignClientFactoryBean, Feign.Builder,
FeignContext, Target.HardCodedTarget<T>);
* 5.由于一开始注入的Feign.Builder是HystrixFeign.Builder,则此处是调用HystrixFeign.Builder里的对应方法
*/
<T> T getTarget() {
FeignContext context = this.applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
//省略部分代码
// ......
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof LoadBalancerFeignClient) {
// not load balancing because we have a url,
// but ribbon is on the classpath, so unwrap
client = ((LoadBalancerFeignClient) client).getDelegate();
}
builder.client(client);
}
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context,
new HardCodedTarget<>(this.type, this.name, url));
}
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;
}
Фабрика получает объект (цель):
1. 获得FeignContext(feign上下文)
2. 从FeignContext中获得Feign构建器Feign.Builder(public,可以在此使用自定义构建器)
3. 从FeignContext中获得Client,判断是否进行负载均衡
4. 从FeignContext中获得Target,并执行Target的默认方法target(FeignClientFactoryBean, Feign.Builder,
FeignContext, Target.HardCodedTarget<T>);
5. 由于一开始注入的 *Targeter* 是 *HystrixTargeter* ,则此处是调用 HystrixTargeter 里的对应方法(从第一节的配置来看,只要 *feign.hystrix.HystrixFeign* 类存在,就是注入的 *HystrixTargeter *, 否则是 *DefaultTargeter*,对于需要**自定义构建feign的,这里不太重要**)
4.Targeter
4.1.HystrixTargeter
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
// 若不是 HystrixFeign,则执行其对应的默认target方法。
// 此处只处理HystrixFeign。
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder,
fallback);
}
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
return targetWithFallbackFactory(factory.getName(), context, target, builder,
fallbackFactory);
}
// 调用从Feign.Builder继承的方法。
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
FallbackFactory.class);
return builder.target(target, fallbackFactory);
}
private <T> T targetWithFallback(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
Class<?> fallback) {
T fallbackInstance = getFromContext("fallback", feignClientName, context,
fallback, target.type());
return builder.target(target, fallbackInstance);
}
//...
}
- HystrixTarget обрабатывает толькоFeign.BuilderТипfeign.hystrix.HystrixFeign.Builderиз
- Если построитель симуляций не относится к типу feign.hystrix.HystrixFeign.Builder, выполните целевой метод по умолчанию внедренного построителя симуляций.
- Таким образом, даже если введенный TargeterHystrixTargeter, вы также можете выполнить пользовательский здесьFeign.Builder.
- понимать:Feign.Builder#target(Target)Метод обычно не переопределяется (позже мы объясним, почему этот метод не переопределяется).
4.2.DefaultTargeter
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
FeignContext context, Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
- Выполняет целевой метод по умолчанию, соответствующий Feign.Builder (под)типу.
- понимать:Feign.Builder#target(Target)Метод обычно не переопределяется (позже мы объясним, почему этот метод не переопределяется).
5.FeignBuilder
feign builder: создайте фиктивный объект.
FeignЦель состоит в том, чтобы обернуть http API в спокойном стиле для облегчения разработки.
В реализации Feign — это фиктивный объект, который генерирует фиктивные объекты для целевого http API (
Feign#newInstance
) фабрика.
Вышеупомянутые шаги в настоящее время требуются через соответствующийBuilderПостройте соответствующий Feign.
public abstract class Feign {
public static Builder builder() {
return new Builder();
}
public abstract <T> T newInstance(Target<T> target);
public static class Builder {
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
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);
}
}
}
- Feign.Builder#target(Target)Метод действительно вызываетbuild()метод для построения объекта, поэтому переопределения метода build() достаточно, нет необходимости также переопределять метод target(Target)
- Feignи внутренний классFeign.Builderобаpublic,Может переопределять и вводить пользовательские bean-компоненты.
5.1.HystrixFeign
public final class HystrixFeign {
public static final class Builder extends Feign.Builder {
@Override
public Feign build() {
return build(null);
}
// 提供一系列的target方法,支持各种配置:fallback、FallBackFactory等
public <T> T target(Target<T> target, T fallback) {
return build(fallback != null ? new FallbackFactory.Default<T>(fallback) : null)
.newInstance(target);
}
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
return build(fallbackFactory).newInstance(target);
}
public <T> T target(Class<T> apiType, String url, T fallback) {
return target(new Target.HardCodedTarget<T>(apiType, url), fallback);
}
public <T> T target(Class<T> apiType,
String url,
FallbackFactory<? extends T> fallbackFactory) {
return target(new Target.HardCodedTarget<T>(apiType, url), fallbackFactory);
}
/** Configures components needed for hystrix integration. */
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
return new HystrixInvocationHandler(target, dispatch, setterFactory,
nullableFallbackFactory);
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
На этом этапе можно настроить все, что необходимо установить.
- Хотя метод сборки включаетInvocationHandler, но в принципе ничего менять не надо, аInvocationHandlerДаже такpackageуровень доступа, поэтому мне пришлось скопировать один и использовать свой.
- HystrixDelegatingContract является общедоступным, если вам не нужно его изменять, вы все равно используете его.
5.2 Примеры
Следующий пример ссылкиSentinelFeign
один из нихYiFeiXiInvocationHandler
а такжеYiFeiXiFeignFallbackFactory
обычай.
@Override
public Feign build() {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override
public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
// using reflect get fallback and fallbackFactory properties from
// FeignClientFactoryBean because FeignClientFactoryBean is a package
// level class, we can not use it in our package
Object feignClientFactoryBean = Builder.this.applicationContext
.getBean("&" + target.type().getName());
Class fallback = (Class) getFieldValue(feignClientFactoryBean,
"fallback");
Class fallbackFactory = (Class) getFieldValue(feignClientFactoryBean,
"fallbackFactory");
String name = (String) getFieldValue(feignClientFactoryBean, "name");
Object fallbackInstance;
FallbackFactory fallbackFactoryInstance;
// check fallback and fallbackFactory properties
// 以下逻辑在HystrixTargeter中有,但执行自定义的builder,不会执行到那段逻辑,因此此处加上。
if (void.class != fallback) {
fallbackInstance = getFromContext(name, "fallback", fallback,
target.type());
return new YiFeiXiInvocationHandler(target, dispatch, setterFactory,
new FallbackFactory.Default(fallbackInstance));
}
if (void.class != fallbackFactory) {
fallbackFactoryInstance = (FallbackFactory) getFromContext(name,
"fallbackFactory", fallbackFactory,
FallbackFactory.class);
return new YiFeiXiInvocationHandler(target, dispatch, setterFactory,
fallbackFactoryInstance);
}
// 若注解中没有使用fallback或fallbackFactory,则使用一个默认的FallbackFactory。
return new YiFeiXiInvocationHandler(target, dispatch, setterFactory, new YiFeiXiFeignFallbackFactory<>(target));
}
private Object getFromContext(String name, String type,
Class fallbackType, Class targetType) {
Object fallbackInstance = feignContext.getInstance(name,
fallbackType);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No %s instance of type %s found for feign client %s",
type, fallbackType, name));
}
if (!targetType.isAssignableFrom(fallbackType)) {
throw new IllegalStateException(String.format(
"Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
type, fallbackType, targetType, name));
}
return fallbackInstance;
}
});
super.contract(new HystrixDelegatingContract(contract));
return super.build();
}
Необходимо настроить fallbackFactory, а затем реализоватьfeign.hystrix.FallbackFactoryкласс, необходимо настроить запасной вариант, а затем реализоватьorg.springframework.cglib.proxy.MethodInterceptorПросто
6. Резюме
- Поскольку процесс сборки Feign используетTargeterдаpackageУровень доступа, нельзя использовать пользовательский
- Feignа такжеFeign.Builderдаpublilc, давая нам возможность расширяться.
7. Ссылки
- feign-hystrix-10.1.0.jarа такжеspring-cloud-openfeign-core-2.1.1.RELEASE.jar
- spring-cloud-alibaba-sentinel-0.9.0.RELEASE.jarсерединаsentinelFeignвыполнить
- Spring Cloud Alibaba Sentinel объединяет дизайн и реализацию Feign