FeignContext是怎么样被spring工厂收录的?

FeignAutoConfiguration是怎么被收录的?

不是通过@EnableFeignClients,那么是根据jar包判断,那么是怎么回事?

Feign.Builder是怎么收录的?getBean得不到,但是在FeignClientFactoryBean的getObject方法中,是可以从spring工厂获取的

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();
        }
    }

如果有HystrixFeign这个类,就用HystrixTargeter,如果没有那就DefaultTargeter。Targeter是在FeignClientFactoryBean的getObject方法中,调用target方法,返回实际的feignclient类,是default feignclient或者hystrixCommand的入口。在里面调用builder的target方法,---->返回ReflectiveFeign--->newInstance方法中,逻辑写在中间类中,返回代理类。在Hystrix的中间类HystrixInvocationHandler中,invoke方法,创建一个hystrixCommand,调用并返回execute方法。

ReflectiveFeign中的newInstance方法,Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target),创造一个methodHandler,把接口的抽象方法,对应成一个实际远程调用的方法。

这个methodHandler是怎么生成的?DefaultTargeter和HystrixTargeter区别就是最后newInstance方法中,使用的动态代理中间类不同。

DefaultTargeter是用的Feign的内部类的builder,build方法返回的ReflectiveFeign传入的参数是通过下面得到。

 

private InvocationHandlerFactory invocationHandlerFactory =
        new InvocationHandlerFactory.Default();

 

而HystrixTargeter的builder继承了Feign.builder,但是build方法使用了自己的InvocationHandler,一样的是根据抽象方法名,找到实际的http方法,但是有fallback,有hystrix的熔断,隔离等机制

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();
    }

 

先看DefaultTargeter的生成methodHandler的逻辑。

nameToHandler是一个map,String----Methodhandler,ParseHandlersByName类的apply方法,在这里,根据接口的method和它上面的注解,生成具体的,可以执行http远程调用的方法===>

先用contract过滤一下,提取中间不是default的抽象方法,buildTemplate封装一下参数啥的,最关键的是factory.create(key, md, buildTemplate, options, decoder, errorDecoder)方法,new 一个SynchronousMethodHandler,

调用其中的invoke方法,完成请求

@Override
  public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
      try {
        return executeAndDecode(template);
      } catch (RetryableException e) {
        retryer.continueOrPropagate(e);
        if (logLevel != Logger.Level.NONE) {
          logger.logRetry(metadata.configKey(), logLevel);
        }
        continue;
      }
    }
  }

 

就是从接口方法里面提取url,参数,等待,发起http请求,用connection。

但是怎么和eureka结合的呢?

 

在spring工厂中的实现feign.Client接口的默认类是ribbon的LoadBalancerFeignClient,这个client是怎么被放到spring工厂的,还是个未解的难题

 

System.err.println(ac.getBean(Client.class));

 

org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient@553da911

 但是 client是怎么被赋值给builder的呢?

在factoryBean的getObject() 方法中,

@Override
    public Object getObject() throws Exception {
        FeignContext context = applicationContext.getBean(FeignContext.class);
        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);//从spring工厂取出client,按照上面说的,是ribbon的,检测类型,赋值给builder(建造者模式?build)
        if (client != null) {
            if (client instanceof LoadBalancerFeignClient) {
                // not lod 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 targeter.target(this, builder, context, new HardCodedTarget<>(
                this.type, this.name, url));
    }

 Feign.Builder.class 在run返回的applicationcontext中找不到,但是FactoryBean中可以获取到,是有一个子applicationContext,parent是那个大的spring工厂,那么这个子工厂是什么时候建立的呢?

 FeignContext extends NamedContextFactory---->实现了ApplicationContextAware接口,把parent属性赋值大spring工厂。

FeignAutoConfiguration里面会实例化FeignContext,调用构造函数的时候给NamedContextFactory的构造参数传入了FeignClientsConfiguration.class,在FactoryBean调用feign()方法获取的时候会调用getcontext,

去自己的子工厂去找,如果没有子工厂,创建一个,FeignClientsConfiguration里面的bean都会被实例化

猜你喜欢

转载自www.cnblogs.com/chuliang/p/9218076.html