feign源码解析--feign的初始化

一 基本原理图

feign的注册:

在这里插入图片描述

  • 客户端在引入EnableFeignClients时会通过FeignClientsRegistrar完成feign的客户端的注册
  • FeignClientsRegistrar中通过registerDefaultConfiguration注册默认配置
  • FeignClientsRegistrar中通过registerFeignClients注册Feign的客户端

feign的代理生成:

在这里插入图片描述

  • spring在刷新容器时触发FeignClientfactory中的getObet
  • 在getObject中获得feignContext,在feignContex中包含了很多feign的日志,解析器等配置
  • 然后根据Feign接口 的URL(要么通过@FeignClient(url=“”)指定,如果没指定,就根据@FeignClient(value=“服务名”)拼接) 生成负载均衡代理类
  • 通过loadBalance方法是如何生成负载均衡代理类的
  • ReflectiveFeign通过Proxy.newProxyInstance为Feign接口生成代理

二 源码分析

2.1 feign的注册

Feign是如何对客户端接口进行注册到Spring容器中的,从==@EnableFeignClients==标签入手

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

该注解导入了FeignClientsRegistrar,他是一个客户端的注册器

主要是通过registerDefaultConfigurationregisterFeignClients完成feign客户端的相应配置

class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
		ResourceLoaderAware, EnvironmentAware {

	// patterned after Spring Integration IntegrationComponentScanRegistrar
	// and RibbonClientsConfigurationRegistgrar

	private ResourceLoader resourceLoader;

	private Environment environment;

	public FeignClientsRegistrar() {
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		this.resourceLoader = resourceLoader;
	}
	//registerBeanDefinitions方法是ImportBeanDefinitionRegistrar接口中的用来注册Bean到Spring容器的方法
	
	@Override
	public void registerBeanDefinitions(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		//注册默认的配置
		registerDefaultConfiguration(metadata, registry);
		//注册Feign的客户端
		registerFeignClients(metadata, registry);
	}

2.1.1 feign默认配置的注册

 private void registerDefaultConfiguration(AnnotationMetadata metadata,
            BeanDefinitionRegistry registry) {
            //得到@EnableFeignClients注解的原始信息
        Map<String, Object> defaultAttrs = metadata
                .getAnnotationAttributes(EnableFeignClients.class.getName(), true);
		//是否配置了@EnableFeignClients(defaultConfiguration=)默认配置
		//使用的类型是FeignClientsConfiguration
        if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
            String name;
            //处理类型,以default开头
            if (metadata.hasEnclosingClass()) {
                name = "default." + metadata.getEnclosingClassName();
            }
            else {
                name = "default." + metadata.getClassName();
            }
            //注册默认配置,name 默认以 default 开头,后续会根据名称选择配置
            registerClientConfiguration(registry, name,
                    defaultAttrs.get("defaultConfiguration"));
        }
    }

上面这个方法在为==@EnableFeignClients(defaultConfiguration=)==默认配置类做注册,一般可以不配置该属性

 private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
            Object configuration) {
            //创建一个BeanDefinitionBuilder
        BeanDefinitionBuilder builder = BeanDefinitionBuilder
                .genericBeanDefinition(FeignClientSpecification.class);
                //添加参数,把配置类交给builder
        builder.addConstructorArgValue(name);
        builder.addConstructorArgValue(configuration);
        //注册了Bean到Spring容器
        registry.registerBeanDefinition(
                name + "." + FeignClientSpecification.class.getSimpleName(),
                builder.getBeanDefinition());
    }

在调用registerClientConfiguration将其转换为FeignClientSpecification注册到spring容器,然后再FeignContext初始化是通过setconfig注册到上下文中

2.1.2 Feign的客户端注册

public void registerFeignClients(AnnotationMetadata metadata,
			BeanDefinitionRegistry registry) {
		
		//ClassPathScanningCandidateComponentProvider是用来扫Feign描客户端接口的
		ClassPathScanningCandidateComponentProvider scanner = getScanner();
		//resourceLoader是用来加载类资源的
		scanner.setResourceLoader(this.resourceLoader);

		Set<String> basePackages;
		//获取@EnableFeignClients注解元数据
		Map<String, Object> attrs = metadata
				.getAnnotationAttributes(EnableFeignClients.class.getName());
		//类型过滤器,过滤FeignClient
		AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
				FeignClient.class);
		//检查是否配置clients属性
		final Class<?>[] clients = attrs == null ? null
				: (Class<?>[]) attrs.get("clients");
		if (clients == null || clients.length == 0) {
		//默认走这里
		//如果没有配置@EnableFeignClient(clients={})属性,就获取basePackages属性
		//给扫描添加includeFilters过滤器,目的是为了扫描@FeignClient注解
			scanner.addIncludeFilter(annotationTypeFilter);
			basePackages = getBasePackages(metadata);
		}
		else {
			//如果配置了clients就把 clients的包添加到扫描器中
			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) {
		//把basePackage下的注解了@FeignClient的接口封装成BeanDefinition
			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);
					//获取@FeignClient注解的configuration配置,对配置进行注册
					registerClientConfiguration(registry, name,
							attributes.get("configuration"));
					//注册客户端接口feignClient
					registerFeignClient(registry, annotationMetadata, attributes);
				}
			}
		}
	}

上面代码主要扫描了注解了@FeignClient的接口,会扫描到很多的接口,然后做2个事情

  • 将其配置类通过registerClientConfiguration注册到容器中,这个方法在刚才已经分析过了,这里是第二次调用
  • 通过registerFeignClient注册FeignClient接口到容器,内部会生成接口的代理类
private void registerFeignClient(BeanDefinitionRegistry registry,
			AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
			//获取到组合的Feign接口类名
		String className = annotationMetadata.getClassName();
		//FeignClientFactoryBean是用用来创建FeignClient接口的,
		//这里要对Feign接口进行代理,使用的是FeignClientFactoryBean
		BeanDefinitionBuilder definition = BeanDefinitionBuilder
				.genericBeanDefinition(FeignClientFactoryBean.class);
		//校验@FeignClient相关属性
		validate(attributes);
		//封装相关的属性到definition
		definition.addPropertyValue("url", getUrl(attributes));
		definition.addPropertyValue("path", getPath(attributes));
		String name = getName(attributes);
		definition.addPropertyValue("name", name);
		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 = name + "FeignClient";
		//得到BeanDefinition对象,它是对Feign客户端的Bean的定义对象,基于FeignClientFactoryBean
		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 });
        //将BeanDefinition加入到spring容器
		BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
	}

2.2 feign代理生成

通过上面的分析我们可以知道FeignClientFactoryBean中将用于feign代理类的生成

class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
		ApplicationContextAware {
	//Feign接口类型
	private Class<?> type;
	//Fiegn客户端名字/请求的服务名
	private String name;
	//请求的url
	private String url;
	
	private String path;

	private boolean decode404;

	private ApplicationContext applicationContext;

	private Class<?> fallback = void.class;
	//降级工厂
	private Class<?> fallbackFactory = void.class;
	....省略....
	
	@Override
	public Object getObject() throws Exception {
	//获取Feign的上下文,在Feign的初始化一节有说道
		FeignContext context = applicationContext.getBean(FeignContext.class);
		//调用feign方法得到Builder构造器,用来生成feign
		//开启Hystrix使用的是HystrixFeign.Builder 
		//否则使用的是feign.Feign.Builder
		Feign.Builder builder = feign(context);
		//如果没有url,即没有指定@FeignClient(url=""),就根据服务名拼接url
		if (!StringUtils.hasText(this.url)) {
			String url;
			//拼接请求的url如:http://user-server
			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));
		}
		//如果配置了@FeignClient(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();
			}
			builder.client(client);
		}
		Targeter targeter = get(context, Targeter.class);
		return targeter.target(this, builder, context, new HardCodedTarget<>(
				this.type, this.name, url));
	}

FeignClientFactoryBean实现了FactoryBean,getObject方法即是用来创建FeignClient实例的

  • 该方法授权通过ApplicationContext得到FeignContext上下文
  • 然后在从FeignContext上下文中得到Feign.Builder,它是用来构建Feign客户端代理类的
  • 然后根据Feign接口 的URL(要么通过@FeignClient(url=“”)指定,如果没指定,就根据@FeignClient(value=“服务名”)拼接) 生成负载均衡代理类

2.2.1 Feign.Builder

Feign.Builder中封装了Feign的日志注解,编码器,解码器,拦截器等等,这些东西是在FeignClientsConfiguration中被创建的

 public static class Builder {
 		//请求拦截器
        private final List<RequestInterceptor> requestInterceptors = new ArrayList();
      	...省略...

        public Builder() {
        	//Feign的日志打印级别,默认不打印
            this.logLevel = Level.NONE;
            this.contract = new Default();
            this.client = new feign.Client.Default((SSLSocketFactory)null, (HostnameVerifier)null);
            //重试策略
            this.retryer = new feign.Retryer.Default();
            this.logger = new NoOpLogger();
            //编码器
            this.encoder = new feign.codec.Encoder.Default();
            //解码器
            this.decoder = new feign.codec.Decoder.Default();
            //错误解码器
            this.errorDecoder = new feign.codec.ErrorDecoder.Default();
            this.options = new Options();
            //方法调用工厂
            this.invocationHandlerFactory = new feign.InvocationHandlerFactory.Default();
        }
		//设置日志级别
        public Feign.Builder logLevel(Level logLevel) {
            this.logLevel = logLevel;
            return this;
        }

        public Feign.Builder contract(Contract contract) {
            this.contract = contract;
            return this;
        }

        public Feign.Builder client(Client client) {
            this.client = client;
            return this;
        }

        public Feign.Builder retryer(Retryer retryer) {
            this.retryer = retryer;
            return this;
        }

        public Feign.Builder logger(Logger logger) {
            this.logger = logger;
            return this;
        }

        public Feign.Builder encoder(Encoder encoder) {
            this.encoder = encoder;
            return this;
        }

        public Feign.Builder decoder(Decoder decoder) {
            this.decoder = decoder;
            return this;
        }

        public Feign.Builder mapAndDecode(ResponseMapper mapper, Decoder decoder) {
            this.decoder = new Feign.ResponseMappingDecoder(mapper, decoder);
            return this;
        }

        public Feign.Builder decode404() {
            this.decode404 = true;
            return this;
        }

        public Feign.Builder errorDecoder(ErrorDecoder errorDecoder) {
            this.errorDecoder = errorDecoder;
            return this;
        }

        public Feign.Builder options(Options options) {
            this.options = options;
            return this;
        }

        public Feign.Builder requestInterceptor(RequestInterceptor requestInterceptor) {
            this.requestInterceptors.add(requestInterceptor);
            return this;
        }
		//添加Feign的拦截器
        public Feign.Builder requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) {
            this.requestInterceptors.clear();
            Iterator var2 = requestInterceptors.iterator();

            while(var2.hasNext()) {
                RequestInterceptor requestInterceptor = (RequestInterceptor)var2.next();
                this.requestInterceptors.add(requestInterceptor);
            }

            return this;
        }

        public Feign.Builder invocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
            this.invocationHandlerFactory = invocationHandlerFactory;
            return this;
        }

        public <T> T target(Class<T> apiType, String url) {
            return this.target(new HardCodedTarget(apiType, url));
        }
		//这个方法挺重要的,它是用来构建FeignClient的代理类的
        public <T> T target(Target<T> target) {
            return this.build().newInstance(target);
        }
        
		//注意:这里使用的是ReflectiveFeign作为Feign的默认实现
		//创建Feign接口的代理就是ReflectiveFeign搞定的
        public Feign build() {
            Factory synchronousMethodHandlerFactory = new Factory(this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, this.decode404);
            ParseHandlersByName handlersByName = new ParseHandlersByName(this.contract, this.options, this.encoder, this.decoder, this.errorDecoder, synchronousMethodHandlerFactory);
            return new ReflectiveFeign(handlersByName, this.invocationHandlerFactory);
        }
    }

2.2.2 FeignClientsConfiguration

@Configuration
public class FeignClientsConfiguration {
	//Feign的消息转换器
	@Autowired
	private ObjectFactory<HttpMessageConverters> messageConverters;

	@Autowired(required = false)
	private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList<>();

	@Autowired(required = false)
	private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList<>();

	@Autowired(required = false)
	private Logger logger;

	//解码器
	@Bean
	@ConditionalOnMissingBean
	public Decoder feignDecoder() {
		return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
	}
	//编码器
	@Bean
	@ConditionalOnMissingBean
	public Encoder feignEncoder() {
		return new SpringEncoder(this.messageConverters);
	}

	@Bean
	@ConditionalOnMissingBean
	public Contract feignContract(ConversionService feignConversionService) {
		return new SpringMvcContract(this.parameterProcessors, feignConversionService);
	}

	@Bean
	public FormattingConversionService feignConversionService() {
		FormattingConversionService conversionService = new DefaultFormattingConversionService();
		for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
			feignFormatterRegistrar.registerFormatters(conversionService);
		}
		return conversionService;
	}
	//如果开启了Hystrix,那么使用的是HystrixFeign.builder();
	//而不是Feign.Builder
	@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();
		}
	}
	//重试策略
	@Bean
	@ConditionalOnMissingBean
	public Retryer feignRetryer() {
		return Retryer.NEVER_RETRY;
	}
	//注册Feign.Builder
	@Bean
	@Scope("prototype")
	@ConditionalOnMissingBean
	public Feign.Builder feignBuilder(Retryer retryer) {
		return Feign.builder().retryer(retryer);
	}
	//日志工厂
	@Bean
	@ConditionalOnMissingBean(FeignLoggerFactory.class)
	public FeignLoggerFactory feignLoggerFactory() {
		return new DefaultFeignLoggerFactory(logger);
	}

}

2.2.3 loadBalance生成feign负载均衡代理

从FeignContext上下文中获取到LoadBalancerFeignClient负载客户端设置给Feign.Builder ,然后从FeignContext上下文得到Targeter,通过Targeter.target方法创建实例,底层通过Feign.newInstance方法完成实例创建,默认实现是ReflectiveFeign完成创建

protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
			HardCodedTarget<T> target) {
			//从上下文对象FeignContext中获取Client,默认使用的是LoadBalancerFeignClient
		Client client = getOptional(context, Client.class);
		if (client != null) {
			//client设置给builder
			builder.client(client);
			
			Targeter targeter = get(context, Targeter.class);
			//targeter的target方法调用了Feign的target方法
			//Feign的默认实现是ReflectiveFeign类,最终调用了ReflectiveFeign.newInstance方法创建实例
			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?");
	}

2.2.4 通过ReflectiveFeign创建Feign代理

public class ReflectiveFeign extends Feign {
	...省略...
	//target是HardCodedTarget,封装了客户端的type,name,url
	public <T> T newInstance(Target<T> target) {
	//得到Feign接口中的所有的方法MethodHandler
        Map<String, MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
        //把 Map<String, MethodHandler>变成Map<Method, MethodHandler> 
        Map<Method, MethodHandler> methodToHandler = new LinkedHashMap();
        List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList();
        Method[] var5 = target.type().getMethods();
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getDeclaringClass() != Object.class) {
                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 = this.factory.create(target, methodToHandler);
        //生成代理
        T proxy = Proxy.newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, handler);
        Iterator var12 = defaultMethodHandlers.iterator();

        while(var12.hasNext()) {
        	//代理类绑定默认的方法

            DefaultMethodHandler defaultMethodHandler = (DefaultMethodHandler)var12.next();
            defaultMethodHandler.bindTo(proxy);
        }
		//返回代理
        return proxy;
    }
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`feign-spring-mvc-starter` 是一个 Feign 的扩展,它支持使用 Spring MVC 注解来定义和调用 REST 服务。使用 `feign-spring-mvc-starter`,你可以像使用 Spring MVC 控制器一样定义 Feign 客户端,从而更方便地进行 REST 服务的开发。 在使用 `feign-spring-mvc-starter` 之前,你需要先了解 FeignSpring MVC 的基本概念和用法。 Feign 是一个声明式的 Web 服务客户端,它可以帮助你更方便地定义和调用 REST 服务。Feign 的基本使用方法是定义一个接口,用于描述 REST 服务的 API,然后使用 Feign 注解来声明这个接口。 Spring MVC 是一个基于 Java 的 Web 框架,它提供了一组注解和 API,用于处理 Web 请求和响应。 `feign-spring-mvc-starter` 将 FeignSpring MVC 结合起来,使你可以使用 Spring MVC 注解来定义和调用 REST 服务。使用 `feign-spring-mvc-starter`,你可以更方便地使用 Feign 来调用 REST 服务。 以下是一个使用 `feign-spring-mvc-starter` 的示例: 1. 添加 Maven 依赖 在 pom.xml 文件中添加以下依赖项: ```xml <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-spring-mvc</artifactId> <version>5.3.1</version> </dependency> ``` 2. 定义 Feign 接口 定义一个 Feign 接口,用于描述 REST 服务的 API。例如: ```java @FeignClient(name = "example-service") public interface ExampleClient { @GetMapping("/example") String getExample(); } ``` 在这个接口中,我们使用了 `@FeignClient` 注解来声明这个接口是一个 Feign 客户端,并指定了服务的名称。然后,我们定义了一个 `getExample()` 方法,用于调用 example-service 服务的 /example 路径。 3. 定义 Spring MVC 控制器 定义一个 Spring MVC 控制器,用于处理来自客户端的请求。例如: ```java @RestController public class ExampleController { private final ExampleClient exampleClient; public ExampleController(ExampleClient exampleClient) { this.exampleClient = exampleClient; } @GetMapping("/") public String index() { return exampleClient.getExample(); } } ``` 在这个控制器中,我们注入了 `ExampleClient`,并在 `index()` 方法中使用它来调用 example-service 服务的 /example 路径。 4. 运行应用程序 现在,你可以运行应用程序并访问 http://localhost:8080/ ,你应该会看到来自 example-service 服务的响应。 这就是一个使用 `feign-spring-mvc-starter` 的示例。使用 `feign-spring-mvc-starter`,你可以更方便地使用 Feign 来调用 REST 服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值