Column series: SpringCloud column series

Series of articles:

SpringCloud source series (1) – registry initialization for Eureka

SpringCloud source code series (2) – Registry Eureka service registration, renewal

SpringCloud source code series (3) – Registry Eureka crawl registry

SpringCloud source code series (4) – Registry Eureka service offline, failure, self-protection mechanism

SpringCloud source series (5) – Registry EurekaServer cluster for Eureka

SpringCloud source Code Series (6) – Summary of the Registry Eureka

SpringCloud source series (7) – load balancing Ribbon RestTemplate

SpringCloud source series (8) – load balancing Ribbon core principles

SpringCloud source series (9) – load balancing Ribbon core components and configuration

SpringCloud source Series (10) – HTTP client component of load balancing Ribbon

SpringCloud Source Series (11) – Retries and summaries of the Load Balancing Ribbon

SpringCloud source Code Series (12) – Basic usage of Service invocation Feign

SpringCloud source Code Series (13) – Service invocation of Feign’s scanning @FeignClient annotation interface

The dynamic proxy factory component FeignClientFactoryBean

The FeignClientFactoryBean component that was analyzed in the previous article is the component that generates the dynamic proxy for the FeignClient interface.

FeignClientFactoryBean implements the FactoryBean interface. When a Bean implements the FactoryBean interface, Spring instantiates the factory and then calls getObject() to create the real Bean if needed.

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean.ApplicationContextAware {}Copy the code

The FeignClientFactoryBean implements the getObject() method, which in turn calls the getTarget() method, and getTarget() finally creates a dynamic proxy object for the FeignClient interface.

The main process for creating dynamic proxy objects is as follows:

  • The Feign context is first obtainedFeignContextFeignContext and RibbonSpringClientFactorySimilarly, you can get the context of each service. Because each service has its own configuration, Encoder, Decoder components, and so on, the components of the current service can be retrieved from the FeignContext.
  • And then you get that from the FeignContextFeign.BuilderThe feign.Builder is the constructor that is ultimately used to create dynamic proxy objects.
  • @feignClient If nourl, constructs the url with the service name from the service name, similar to the RestTemplate, which must end up as a load balancing request. If the URL is configured, the address is called directly.
  • All get one from FeignContext ClientIf the URL is configured, the url is obtained from the clientProxy objectsAnd set it to builder. Otherwise, set Client directly to Builder. That is, determine whether to use a load-balancing Client based on the URL.
  • Eventually they callTargetertarget()Method to construct a dynamic proxy object. The parameters passed in by target include the current FeignClientFactoryBean object, Feign.Builder, FeignContext, and the encapsulatedHardCodedTargetObject.
// Get the entry to the FeignClient proxy object
@Override
public Object getObject(a) throws Exception {
    return getTarget();
}

/** * create a proxy object for the FeignClient interface@FeignClientThe interface type of the annotation * *@param <T> the target type of the Feign client
 * @return a {@link Feign} client created with the specified data and the context information
 */
<T> T getTarget(a) {
    // Feign context
    FeignContext context = applicationContext.getBean(FeignContext.class);
    // Feign constructor
    Feign.Builder builder = feign(context);

    // If the URL is not configured directly, the load balancing request is processed
    if(! StringUtils.hasText(url)) {if(! name.startsWith("http")) {
            url = "http://" + name;
        }
        else {
            url = name;
        }
        // Address with service name => http://demo-consumer
        url += cleanPath();
        // The type returned must be load-balancing; HardCodedTarget => HardCodedTarget
        return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
    }

    // If the url is configured, request the URL directly
    if(StringUtils.hasText(url) && ! url.startsWith("http")) {
        url = "http://" + url;
    }
    String url = this.url + cleanPath();
    // Client => Feign the core component that initiates the HTTP call
    Client client = getOptional(context, Client.class);
    if(client ! =null) {
        if (client instanceof LoadBalancerFeignClient) {
            // Get the proxy object, which is the native client.default
            client = ((LoadBalancerFeignClient) client).getDelegate();
        }
        if (client instanceof FeignBlockingLoadBalancerClient) {
            // Get the proxy object, which is the native client.default
            client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
        }
        builder.client(client);
    }
    Targeter targeter = get(context, Targeter.class);
    // targeter creates a dynamic proxy object
    return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
Copy the code
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
    / / for the Client
    Client client = getOptional(context, Client.class);
    if(client ! =null) {
        builder.client(client);
        // Targeter => HystrixTargeter
        Targeter targeter = get(context, Targeter.class);
        // targeter creates a dynamic proxy object
        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?");
}
Copy the code

Dynamic proxy constructor feign.builder

The feign() method returns feign.Builder, which is also retrieved from the FeignContext. The most important thing about this method is that it sets Logger, Encoder, Decoder, Contract, And read the configuration file feign.client.* related configuration. FeignClientsConfiguration configured in the default implementation of this a few interface classes, we can also customize these implementation class.

protected Feign.Builder feign(FeignContext context) {
    FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
    Logger logger = loggerFactory.create(type);

    // We can customize Logger, Encoder, Decoder, Contract
    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));

    // Read the configuration of feign. Client.* in the configuration file to configure feign
    configureFeign(context, builder);

    return builder;
}
Copy the code

What is the default implementation of Feign.Builder? Can know from FeignClientsConfiguration, by default is Feign. Builder, if you enable Feign hystrix. Enabled, the default implementation is HystrixFeign. The Builder.

What’s the difference between Feign.Builder and Hystrixfeign.build? The main difference is that the implementation class InvocationHandler to create the dynamic proxy is different. In the case of Hystrix, there are fuses, downgrades, etc. Hystrixfeign. Build also sets fallback and fallbackFactory classes configured by @FeignClient. We’ll look at this later when we analyze the Hystrix source code. All you need to know is that Feign is not hystrix enabled and @FeignClient’s fallback/fallbackFactory downgrade callbacks are not valid.

public class FeignClientsConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer(a) {
        // Never retry
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Feign.Builder feignBuilder(Retryer retryer) {
        // Default is feign.builder
        return Feign.builder().retryer(retryer);
    }

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
    protected static class HystrixFeignConfiguration {

        // Hystrix is introduced and feign.hystrix.enabled = true
        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(name = "feign.hystrix.enabled")
        public Feign.Builder feignHystrixBuilder(a) {
            // feign When hystrix is enabled, feign. Builder is hystrixfeign. Builder
            returnHystrixFeign.builder(); }}}Copy the code

Feign configuration

The configureFeign() method configures feign.Builder to verify the priority of the Feign configuration in the base article.

Feign has three configurations. One is to configure Feign in Configuration mode and then set the Configuration parameter to @FeignClient. This is followed by the global feign.client.default configuration, and the service-specific configuration feign.client.

.

As you can see from the configureFeign() method, by default, the lowest priority is the code configuration, followed by the default configuration, and the highest priority is the service-specific configuration.

If you want the code configuration to take precedence over the configuration in the file, you can set feign.client.defalut-to-properties=false to change the priority for the feign configuration to take effect.

protected void configureFeign(FeignContext context, Feign.Builder builder) {
    Feign.client.* Client configuration in the configuration file
    FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);

    FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
    setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());

    if(properties ! =null && inheritParentContext) {
        // defaultToProperties: The configuration in the configuration file is preferred
        if (properties.isDefaultToProperties()) {
            // Lowest priority: use Configuration in the code
            configureUsingConfiguration(context, builder);
            // Secondary priority: use the default value feign.client.default
            configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
            // High priority: the configuration defined using feign.client.
      
            configureUsingProperties(properties.getConfig().get(contextId), builder);
        }
        // Java code configuration is preferred
        else{ configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); configureUsingProperties(properties.getConfig().get(contextId), builder); configureUsingConfiguration(context, builder); }}else{ configureUsingConfiguration(context, builder); }}Copy the code

The network calls the component Client

Client is a component in Feign-core. It has only one interface, Execute, which is the URL that calls Request and encapsulates the return interface into Response.

public interface Client {

  /**
   * Executes a request against its {@link Request#url() url} and returns a response.
   *
   * @param request safe to replay.
   * @param options options to apply to this request.
   * @return connected response, {@link Response.Body} is absent or unread.
   * @throws IOException on a network error connecting to {@link Request#url()}.
   */
  Response execute(Request request, Options options) throws IOException;
}
Copy the code

Client has the following implementation classes:

Client automation configuration class is FeignRibbonClientAutoConfiguration, FeignRibbonClientAutoConfiguration imported the HttpClient, OkHttp and default Feign load balance configuration class.

@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled", matchIfMissing = true)
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(FeignAutoConfiguration.class)
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
@Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class })
public class FeignRibbonClientAutoConfiguration {}Copy the code

To enable the Apache HttpClient

As can be seen from the HttpClientFeignLoadBalancedConfiguration configuration, to enable the apache httpclient, need to set the feign. Httpclient. Enabled = true (the default is true). And you need to add the feign-httpclient dependency (ApacheHttpClient)

After ApacheHttpClient is enabled, the proxy object of LoadBalancerFeignClient is apache HttpClient in Feign-HttpClient.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(ApacheHttpClient.class)
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
@Import(HttpClientFeignConfiguration.class)
class HttpClientFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, HttpClient httpClient) {
        ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
        return newLoadBalancerFeignClient(delegate, cachingFactory, clientFactory); }}Copy the code

Enable OkHttp

As can be seen from the OkHttpFeignLoadBalancedConfiguration configuration, to enable the okhttp, need to set the feign. Okhttp. Enabled = true, The feign-okhttp dependency (OkHttpClient) needs to be introduced.

When okHTTP is enabled, the proxy object of LoadBalancerFeignClient is the OkHttpClient of Feign-okHTTP.

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(OkHttpClient.class)
@ConditionalOnProperty("feign.okhttp.enabled")
@Import(OkHttpFeignConfiguration.class)
class OkHttpFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean(Client.class)
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
        OkHttpClient delegate = new OkHttpClient(okHttpClient);
        return newLoadBalancerFeignClient(delegate, cachingFactory, clientFactory); }}Copy the code

The default configuration

Without introducing feign – httpclient or feign – okhttp, will leave the default DefaultFeignLoadBalancedConfiguration. The Default proxy object, client. Default, uses HttpURLConnection to make HTTP calls.

@Configuration(proxyBeanMethods = false)
class DefaultFeignLoadBalancedConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {
        return new LoadBalancerFeignClient(new Client.Default(null.null), cachingFactory, clientFactory); }}Copy the code

As you can see, the Client objects created by the three configuration classes are LoadBalancerFeignClient, which supports load balancing requests. The Default proxy class is Client.Default, and the underlying is HttpURLConnection.

This is similar to the load balancing for RestTemplate when analyzing the Ribbon source code.

Dynamic proxy target Targeter

The Targeter interface has only one interface method, which fetches a dynamic proxy object through the target() method. Targeter has two implementation classes DefaultTargeter and HystrixTargeter.

interface Targeter {

    <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget
       
         target)
       ;
}
Copy the code

As you can see in the FeignAutoConfiguration configuration class, the default implementation of Targeter is HystrixTargeter whenever HystrixFeign is introduced.

HystrixTargeter was originally designed to integrate Feign with Hystrix, allowing feign calls to fuse, limit, and degrade.

public class FeignAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
    protected static class HystrixFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter(a) {
            return newHystrixTargeter(); }}@Configuration(proxyBeanMethods = false)
    @ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
    protected static class DefaultFeignTargeterConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public Targeter feignTargeter(a) {
            return newDefaultTargeter(); }}}Copy the code

The difference between HystrixTargeter and DefaultTargeter is that HystrixTargeter sets the demotion callback processing class to feign.Builder so that when a Feign call triggers a fuse break, the demotion can be entered into the callback class processing.

They all end up essentially calling the target() method of feign.Builder to create a dynamic proxy object.

class HystrixTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget
       
         target)
        {
        if(! (feigninstanceof feign.hystrix.HystrixFeign.Builder)) {
            // If not hystrixfeign. Builder, call target directly
            return feign.target(target);
        }
        When Hystrix is enabled, the callback class or callback factory is set to hystrixfeign.Builderfeign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName() : factory.getContextId(); Class<? > fallback = factory.getFallback();// Set the callback class
        if(fallback ! =void.class) {
            return targetWithFallback(name, context, target, builder, fallback);
        }
        // Set the callback factory classClass<? > fallbackFactory = factory.getFallbackFactory();if(fallbackFactory ! =void.class) {
            return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
        }
        // Call feign.builder to create the dynamic proxy
        returnfeign.target(target); }}Copy the code
class DefaultTargeter implements Targeter {

    @Override
    public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, Target.HardCodedTarget
       
         target)
        {
        returnfeign.target(target); }}Copy the code

Feign.builder creates dynamic proxy

Builder is the default implementation of feign. Builder. HystrixTargeter calls the target method of Feign.Builder to create a dynamic proxy.

  • Called first in the target methodbuild()Method to constructFeignAnd then call Feign’snewInstanceCreate dynamic proxy objects.
  • build()Method that reads the configuration firstClient, Retryer, Logger, Contract, Encoder, DecoderSuch as object.
  • And then we getInvocationHandlerFactoryThe default isInvocationHandlerFactory.Default, which is a factory class provided by Feign to create proxy objectsInvocationHandler.
  • The interface method processor factory is then createdSynchronousMethodHandler.FactoryIs used to encapsulate an interface method as a method executorMethodHandler, the default implementation class isSynchronousMethodHandler.
  • A SpringMVC annotation handler is also createdParseHandlersByNameAs you can imagine, this is used to handle springMVC annotations in the interface, parsing the REST interface to produce MethodHandler.
  • Finally, the Feign object is created, and the implementation class is ReflectiveFeign. After that, it is time to use ReflectiveFeign to create the dynamic proxy object.
public <T> T target(Target<T> target) {
  return build().newInstance(target);
}

/ / build Feign
public Feign build(a) {
    // Feign Http call Client.Default is client.default
    Client client = Capability.enrich(this.client, capabilities);
    // Retries, the default is reretry
    Retryer retryer = Capability.enrich(this.retryer, capabilities);
    // the Feign request interceptor can do some customization to the Feign RequestTemplate RequestTemplate
    List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
      .map(ri -> Capability.enrich(ri, capabilities))
      .collect(Collectors.toList());
    // Log component, default Slf4jLogger
    Logger logger = Capability.enrich(this.logger, capabilities);
    // Interface protocol component, default is SpringMvcContract
    Contract contract = Capability.enrich(this.contract, capabilities);
    / / configuration class
    Options options = Capability.enrich(this.options, capabilities);
    / / encoder
    Encoder encoder = Capability.enrich(this.encoder, capabilities);
    / / decoder
    Decoder decoder = Capability.enrich(this.decoder, capabilities);
    // Create a factory class for InvocationHandler
    InvocationHandlerFactory invocationHandlerFactory =
      Capability.enrich(this.invocationHandlerFactory, capabilities);
    QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
    // Interface method processor factory
    SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
      new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
          logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
    // Parse springMVC annotations
    ParseHandlersByName handlersByName =
      new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
          errorDecoder, synchronousMethodHandlerFactory);
    // ReflectiveFeign
    return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
Copy the code

InvocationHandlerFactory contains a create interface method, the Default implementation is InvocationHandlerFactory. The Default, Return the InvocationHandler type is ReflectiveFeign FeignInvocationHandler.

package feign;

public interface InvocationHandlerFactory {

  // Create a dynamic proxy
  InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);

  // Method handler
  interface MethodHandler {

    Object invoke(Object[] argv) throws Throwable;
  }

  static final class Default implements InvocationHandlerFactory {

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
      return newReflectiveFeign.FeignInvocationHandler(target, dispatch); }}}Copy the code

Then look at the newInstance() method of ReflectiveFeign:

  • newInstanceThe target argument is encapsulated aboveTarget.HardCodedTargetIt encapsulates the clientType, urlSuch attributes.
  • The first is to useParseHandlersByNameConvert the interface in the FeignClient interface to MethodHandler, the actual typeSynchronousMethodHandlerThis detail is not to be seen.
  • And then useInvocationHandlerFactoryCreate the InvocationHandler proxy object, that isReflectiveFeign.FeignInvocationHandlerMethods that call dynamic proxy objects eventually go into the execution handler.
  • Finally, you can see where the dynamic proxy is createdProxyCreates a FeignClient dynamic proxy object whose type is the type of the interface annotated by @FeignClient. Once finally injected into the IoC container, you can inject your own FeignClient client component into your code.

The final step is to create a dynamic Proxy that implements the FeignClient interface through the Proxy, and then all calls to the interface methods are intercepted by the FeignInvocationHandler.

public <T> T newInstance(Target<T> target) {
    // Use ParseHandlersByName to convert the interface in the FeignClient interface to MethodHandler. Springmvc annotations are handled by the Contract component
    // MethodHandler => SynchronousMethodHandler
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

    // Convert to method-methodHandler mapping
    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))); }}/ / SynchronousMethodHandler. Create SynchronousMethodHandler Factory
    InvocationHandler handler = factory.create(target, methodToHandler);
    // Use Proxy to create a dynamic Proxy, which is a SynchronousMethodHandler
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        newClass<? >[] {target.type()}, handler);for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
      defaultMethodHandler.bindTo(proxy);
    }
    return proxy;
}
Copy the code

A diagram summarizes the FeignClient process for generating dynamic proxies

The following diagram summarizes the process of generating FeignClient dynamic proxies:

  • First of all,@EnableFeignClientsThe imported registryFeignClientsRegistrarscans@FeignClientAnnotated interface, and generatedFeingClientFactoryBeanBeanDefinitionRegister with the container. The FeingClientFactoryBean will be calledgetObjectMethod to get a dynamic proxy object for the interface.
  • The getObject method that goes into the FeingClientFactoryBean gets it firstFeignContextIt is essentially a container for each client, similar to a Map structure that caches the relationship between the client and the container. Most of the subsequent components are retrieved from the FeignContext.
  • Get the Feign constructor from the FeignContextFeign.Builder, and configure Feign.Builder. The configuration source has many places, the highest priority is the configuration in Application. You can also configurefeign.client.default-to-properties=falseSets the Java code configuration to high priority.
  • If @feignClient is configured with a URL, the load balancing request will be sent.
    • If a URL is configured to represent a specific address, set the LoadBalancerFeignClient’s delegate as the Client to feign.Builder.
    • If the URL is not configured, indicating that the request is made by the service name, LoadBalancerFeignClient is set to feign.Builder as a Client.
  • And get it from FeignContextTargeter, to call ittargetMethod to get the dynamic proxy.
  • In the Target method, the feign.Builder’sbuild()Method to constructReflectiveFeign:
    • First, get the proxy object factoryInvocationHandlerFactory, used to createInvocationHandler
    • Then, a method processor factory is constructed from each componentSynchronousMethodHandler.FactoryNext, a method resolver is createdParseHandlersByName
    • Finally, it is constructed based on InvocationHandlerFactory and ParseHandlersByNameReflectiveFeign
  • Finally, call ReflectiveFeignnewInstanceMethod reflects the dynamic proxy for creating the interface:
    • First use the method parser ParseHandlersByName to parse the interface intoSynchronousMethodHandler
    • The proxy object is then created using the InvocationHandlerFactoryInvocationHandler(ReflectiveFeign FeignInvocationHandler)
    • Finally usingProxyCreate a dynamic proxy object whose type is the type of the interface and whose type is the proxy objectReflectiveFeign.FeignInvocationHandler.