feign是如何创建代理对象的

Feign如何创建代理对象的

前言

首先我们使用feign调用其他服务的接口的时候,只需要定义一个接口,比如就像下面这样:
在这里插入图片描述
业务代码调用的时候也只需要通过接口直接调用就可以,但是底层到底是如何实现调用的呢

@EnableFeignClients注解

首先@EnableFeignClients注解是是开启feign调用功能的关键,这个注解我们一般会加在启动类上面,让我们看看这个注解里面都是什么

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {

	/**
	 * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
	 * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
	 * {@code @ComponentScan(basePackages="org.my.pkg")}.
	 * @return the array of 'basePackages'.
	 */
	String[] value() default {};

通过注解我们看到有个关键的类就是FeignClientsRegistrar,这个类就是核心,观察这个类可以发现这个类是实现了ImportBeanDefinitionRegistrar接口,实现这个接口的目的就是为了实现bean的动态注入,接着看这个类里面的方法

	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		registerDefaultConfiguration(metadata, registry);
		registerFeignClients(metadata, registry);
	}
  1. registerDefaultConfiguration 这个方法的作用就是初始化自定义配置的,这个配置就是我们在feign接口上定义的 configuration 要实现的类作用一般就是配置我们自定义的一些配置,比如日志的级别或什么RequestInterceptor请求拦截器等等,最终会把我们要实现的类注入到容器中,另外官方说configuration的配置类是不需要加比如@Configuration注解的。
  2. registerFeignClients 这个方法的作用其实就是为后面初始化代理对象用的,看一下这个方法的源码
    public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);

        Set<String> basePackages;

        Map<String, Object> attrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
                FeignClient.class);
        final Class<?>[] clients = attrs == null ? null
                : (Class<?>[]) attrs.get("clients");
        if (clients == null || clients.length == 0) {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(metadata);
        } else {
            final Set<String> clientClasses = new HashSet<>();
            basePackages = new HashSet<>();
            for (Class<?> clazz : clients) {
                basePackages.add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }
            AbstractClassTestingTypeFilter filter = new AbstractClassTestingTypeFilter() {
                @Override
                protected boolean match(ClassMetadata metadata) {
                    String cleaned = metadata.getClassName().replaceAll("\\$", ".");
                    return clientClasses.contains(cleaned);
                }
            };
            scanner.addIncludeFilter(new FeignClientsRegistrar.AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
        }
        //遍历要扫描的包
        for (String basePackage : basePackages) {
            //扫描包,找到FeignClient注解的类,scanner的扫描过滤条件已在上面加上了
            Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
            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");

                    //拿到FeignClient注解类上面的注解值
                    Map<String, Object> attributes = annotationMetadata
                            .getAnnotationAttributes(
                                    FeignClient.class.getCanonicalName());

                    String name = getClientName(attributes);
                    //拿到注解值后来注册我们自定义的configuration 要初始化到容器的类
                    registerClientConfiguration(registry, name, attributes.get("configuration"));
                    //注册feign的客户端
                    registerFeignClient(registry, annotationMetadata, attributes);
                }
            }
        }
    }

可以看到这个方法刚开始的时候会扫描我们的类路径下的包,并找出带有@FeignClient注解的接口,然后就是初始化我们configuration 类以及注册FeignClient客户端,下面看一下registerFeignClient注册客户端这个方法都干了写什么

    private void registerFeignClient(BeanDefinitionRegistry registry,
                                     AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
        String className = annotationMetadata.getClassName();
        //FeignClientFactoryBean类很重要后面的初始化代理对象就是在这里面做的
        BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
        validate(attributes);
        definition.addPropertyValue("url", getUrl(attributes));
        definition.addPropertyValue("path", getPath(attributes));
        String name = getName(attributes);
        definition.addPropertyValue("name", name);
        String contextId = getContextId(attributes);
        definition.addPropertyValue("contextId", contextId);
        definition.addPropertyValue("type", className);
        definition.addPropertyValue("decode404", attributes.get("decode404"));
        definition.addPropertyValue("fallback", attributes.get("fallback"));
        definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

        String alias = contextId + "FeignClient";
        AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();

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

        beanDefinition.setPrimary(primary);

        String qualifier = getQualifier(attributes);
        if (StringUtils.hasText(qualifier)) {
            alias = qualifier;
        }

        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
                new String[] { alias });
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }

registerFeignClient这个方法的主要作用就是为初始化FeignClientFactoryBean做准备的

  1. FeignClientFactoryBean 这个类其实就是初始化Feign代理对象的地方,稍微看一下这个类的部分代码

/**
 * @author Spencer Gibb
 * @author Venil Noronha
 * @author Eko Kurniawan Khannedy
 * @author Gregor Zurowski
 */
class FeignClientFactoryBean
		implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {

可以看到这个类实现了spring里面的FactoryBean接口,所以实际操的地方就是下面这个方法

	@Override
	public Object getObject() throws Exception {
		return getTarget();
	}

在看一下getTarget方法的内容

	<T> T getTarget() {
		FeignContext context = this.applicationContext.getBean(FeignContext.class);
		Feign.Builder builder = feign(context);

		if (!StringUtils.hasText(this.url)) {
			if (!this.name.startsWith("http")) {
				this.url = "http://" + this.name;
			}
			else {
				this.url = this.name;
			}
			this.url += cleanPath();
			return (T) loadBalance(builder, context,
					new HardCodedTarget<>(this.type, this.name, this.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);
		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();
			}
			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<>(this.type, this.name, url));
	}

到了这一步就是feign真正返回代理对象的一个方法。
4. getTarget 看一下getTarget是怎么做的,首先刚开始Feign会创建一个Builder这个Builder主要是设置一些配置的,比如Feign底层调用的客户端(DefaultClient、ApacheHttpClient、OKhttp、LoadBalancerClient等等可以根据实际需要配置想用的客户端,这个配置默认在FeignAutoConfiguration里面)、encoder、decoder(编码解码类,比如用FastJson等)、contract(设置Feign接口的解析器,比如用springMvc来解析等)、logger(设置feign的日志级别)
接下来看方法会发现最终创建返回接口代理对象的是一个Feign接口里面的newInstance方法这个Feign接口有个实现类叫ReflectiveFeign,看一下ReflectiveFeign的方法
5. ReflectiveFeign

  @Override
  public <T> T newInstance(Target<T> target) {
    Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
    Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
    List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();

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

到了这一步真正的代理对象就是这个方法里面给创建出来的
接下就是这个方法里面有个MethodHandler接口,这个接口有个实现SynchronousMethodHandler这个实现类里面有个invoke方法,最后我们的实际发请求就是这个方法里面处理的。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值