Feign拦截器,底层Feign运行流程核心代码解读(三)

RequestInterceptor

doc
可以配置零个或多个requestinterceptor,以便将头添加到所有请求。对于拦截器的使用顺序,不作任何保证。一旦使用了拦截器,目标.apply调用(RequestTemplate)来创建通过客户端.execute(请求,请求。选项).

该接口只有一个抽象方法

public interface RequestInterceptor {
    
    

  /**
   * Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
   * 每一个要求。使用提供的{@link RequestTemplate}上的方法添加数据。 
   * 意思就是我们在调用请求时我们可以通过该方法实现自己的一些功能
   */
  void apply(RequestTemplate template);
}

我们在client 的被调用方法中增加
在这里插入图片描述
在feign项目中编写一个配置类实现RequstInterceptor接口重新他的apply方法

@Configuration
public class FeignClientInterceptorConfig implements RequestInterceptor {
    
    


    @Override
    public void apply(RequestTemplate template)  {
    
    
        System.out.println("feign interceptor entered");

        template. header("authKey","myKeyInfo");
    }
}

将feign接口拦截器配置文件指定好
在这里插入图片描述

重新启动client 和fegin项目 访问之前的http://localhost:8888/getStudentByFeign就可以看到
在这里插入图片描述

在这里插入图片描述
此时就可以在请求发送前执行自己的操作

Feign流程解析

FeignClientsRegistrar

在这里插入图片描述

该接口是feign接口中重要的接口 是用来登记@FeignClient注解定义的抽象接口集合 通过里面的方法registerFeignClients将其注册到spring工厂当中。当我们在使用@EnableFeignClients注解时 项目在启动的的时候会去扫描@FeignClient注解定义的抽象接口 打包成集合

@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
    
    
		//用来注册默认客户端的基础规范配置信息 具体的一些
		//默认信息放到了FeignClientSpecification这个规范类当中
		registerDefaultConfiguration(metadata, registry);
		//用来注册我们定义@FeignClient修饰的feign接口
		registerFeignClients(metadata, registry);
	}

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 AllTypeFilter(Arrays.asList(filter, annotationTypeFilter)));
		}
		//扫描每一个包内容进行预处理
		for (String basePackage : basePackages) {
    
    
			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");

					Map<String, Object> attributes = annotationMetadata
							.getAnnotationAttributes(
									FeignClient.class.getCanonicalName());

					String name = getClientName(attributes);
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
					//此时开始注册feign的客户端
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

registerFeignClient

private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
    
    
		String className = annotationMetadata.getClassName();
		//定义生成器
		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();
		beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);

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

		beanDefinition.setPrimary(primary);

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

		BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
				new String[] {
    
     alias });
		//此时将那些被修饰的bean定义注册到spring工厂当中
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

FeignClientFactoryBean.class (重要)

这是一个工厂bean包含了(Feign的属性和方法)用来注册一个一个对象每个对象都包含对应的方法,该工厂bean 提供了被修饰的抽象接口的动态代理,负载均衡,和拦截器实现。

	/*
		该类就完成了类的装配工作 
	*/
	protected Feign.Builder feign(FeignContext context) {
    
    
		FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
		Logger logger = loggerFactory.create(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;
	}


	/**
	实现了负载均衡 有次异常可以看出 Feign的负载均衡是基于Ribbon
	*/
	protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
    
    
		Client client = getOptional(context, Client.class);
		if (client != null) {
    
    
			builder.client(client);
			
			Targeter targeter = get(context, Targeter.class);
			//此时获取到负载均衡返回的接口的动态代理对象
			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?");
	}

在这里插入图片描述

ReflectiveFeign

Feign的目的是简化对假扮restfulity的httpapi的开发。在实现中,Feign是生成目标httpapi的工厂。
在这里插入图片描述

 /**
   * creates an api binding to the {@code target}. As this invokes reflection, care should be taken
   * to cache the result.
   */
  @SuppressWarnings("unchecked")
  @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)));
      }
    }
    //创建对应动态代理的执行器 里面的invoke方法真正的执行操作
    InvocationHandler handler = factory.create(target, methodToHandler);
    /*
    此时生成需要被代理也就是@FeignClient 修饰的接口的代理对象proxy 他就实现
    了我们被feign定义的三个抽象对象的接口
    */
    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
        new Class<?>[] {
    
    target.type()}, handler);

    for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
    
    
      defaultMethodHandler.bindTo(proxy);
    }
    //返回对应的代理对象 此时我们调用抽象方法的请求就会在底层调用对应的请求
    return proxy;
  }

在这里插入图片描述
当我们打完断点重新启动客户端时
在这里插入图片描述
在这里插入图片描述

由此我们可以看出程序在运行时Fegin底层是对ribbon 进行包装 ribbon实现负载均衡底层通过动态代理进行数据请求的包装并返回动态代理对象

此时启动完成我们调用
http://localhost:8888/getStudentByFeign
在这里插入图片描述
此时在invoke方法中就能拿到Student这个返回对象

总结

程序在启动时我们首先通过@EnableFeignClients 注解扫描 @FeignClient接口 将其封装到FeignClientsRegistrar当中通过registerFeignClients方法对数据进行解析和配置 然后通过FeignClientFactoryBean构建工厂的loadBalance方法 实现负载均衡l并通过动态代理将请求发送出去,此时我们在请求url时动态代理就能通过invocationHander执行器的 invoke方法找到对应的客户端资源

猜你喜欢

转载自blog.csdn.net/qq_42261668/article/details/108168785