openFeign作为一个http客户端,可以让开发人员像调用一个本地接口一样调用一个http接口,而spring cloud也将openFeign集成到了spring cloud全家桶中,成为了spring cloud体系中微服务接口调用的标准实现,同时spring cloud也扩展的openFeign,使之支持springMVC注解。feign的使用我想大部分人都已经太熟悉了,下面直接进入源码。源码版本:spring-cloud-openfeign-3.1.1
源码入口就是EnableFeignClients这个注解,当开启这个注解,spring就会扫描所有打了FeignClient注解的类,并为之创建动态代理,封装为一个http客户端:
- @Import(FeignClientsRegistrar.class)
- public @interface EnableFeignClients
可以看到EnableFeignClients引入了FeignClientsRegistrar这个类,它实现了ImportBeanDefinitionRegistrar,具有注册bean定义的功能:
- public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
- //注册默认的feign客户端配置类,所有的feign客户端可以公用这些配置
- registerDefaultConfiguration(metadata, registry);
- //注册feign客户端
- registerFeignClients(metadata, registry);
- }
-
-
- public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
-
- LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
- Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
- final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
- //注解上的clients属性如果为空,则需要根据扫描的包去扫描带有FeignClient注解的类
- if (clients == null || clients.length == 0) {
- ClassPathScanningCandidateComponentProvider scanner = getScanner();
- scanner.setResourceLoader(this.resourceLoader);
- scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
- Set<String> basePackages = getBasePackages(metadata);
- for (String basePackage : basePackages) {
- candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
- }
- }
- else {
- //如果clients属性有值,则直接把指定的clients加入候选者
- for (Class<?> clazz : clients) {
- candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
- }
- }
- //将每个候选者注册为FeignClient
- 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);
- //注册该FeignClient的配置类,其实是注册一个FeignClientSpecification,FeignClient注解上的configuration属性
- //将会作为构造器参数传入。注意每个FeignClient都有自己的spring上下文。
- registerClientConfiguration(registry, name, attributes.get("configuration"));
- //注册FeignClient,其实就是注册一个FactoryBean
- registerFeignClient(registry, annotationMetadata, attributes);
- }
- }
- }
-
-
- private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
- Map<String, Object> attributes) {
- String className = annotationMetadata.getClassName();
- Class clazz = ClassUtils.resolveClassName(className, null);
- ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
- ? (ConfigurableBeanFactory) registry : null;
- String contextId = getContextId(beanFactory, attributes);
- String name = getName(attributes);
- //创建FeignClientFactoryBean
- FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
- factoryBean.setBeanFactory(beanFactory);
- factoryBean.setName(name);
- factoryBean.setContextId(contextId);
- factoryBean.setType(clazz);
- factoryBean.setRefreshableClient(isClientRefreshEnabled());
- //设置bean的instanceSupplier,指定spring bean实例化策略为supplier方式
- BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
- //下面是一些参数的赋值
- factoryBean.setUrl(getUrl(beanFactory, attributes));
- factoryBean.setPath(getPath(beanFactory, attributes));
- factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
- Object fallback = attributes.get("fallback");
- if (fallback != null) {
- factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
- : ClassUtils.resolveClassName(fallback.toString(), null));
- }
- Object fallbackFactory = attributes.get("fallbackFactory");
- if (fallbackFactory != null) {
- factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
- : ClassUtils.resolveClassName(fallbackFactory.toString(), null));
- }
- //直接使用了getObject方法拿到真正的bean
- return factoryBean.getObject();
- });
- //指定按类型注入
- definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
- //指定懒加载
- definition.setLazyInit(true);
- validate(attributes);
-
- AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
- beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
- beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
-
- // has a default, won't be null
- boolean primary = (Boolean) attributes.get("primary");
- //指定为首选bean
- beanDefinition.setPrimary(primary);
-
- String[] qualifiers = getQualifiers(attributes);
- if (ObjectUtils.isEmpty(qualifiers)) {
- qualifiers = new String[] { contextId + "FeignClient" };
- }
-
- BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
- //注册bean定义
- BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
- //注册可选的Request.Options,可以动态刷新Request配置,貌似不太常用
- registerOptionsBeanDefinition(registry, contextId);
- }
这里一个最为重要的类:FeignClientFactoryBean,详细看下这个FactoryBean的getObject方法是如何创建feign客户端的:
- public Object getObject() {
- return getTarget();
- }
-
-
- <T> T getTarget() {
- //获取FeignContext,这其实是一个feign的上下文工厂,继承了Spring的NamedContextFactory
- //可以为每个feignClient创建一个独立的Spring上下文,FeignContext在FeignAutoConfiguration中自动装配
- FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
- : applicationContext.getBean(FeignContext.class);
- //获取一个Feign.Builder
- Feign.Builder builder = feign(context);
-
- if (!StringUtils.hasText(url)) {
-
- if (LOG.isInfoEnabled()) {
- LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
- }
- if (!name.startsWith("http")) {
- url = "http://" + name;
- }
- else {
- url = name;
- }
- url += cleanPath();
- //创建feign客户端
- return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
- }
- if (StringUtils.hasText(url) && !url.startsWith("http")) {
- url = "http://" + url;
- }
- String url = this.url + cleanPath();
- Client client = getOptional(context, Client.class);
- if (client != null) {
- 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();
- }
- if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
- // not load balancing because we have a url,
- // but Spring Cloud LoadBalancer is on the classpath, so unwrap
- client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
- }
- builder.client(client);
- }
-
- applyBuildCustomizers(context, builder);
-
- Targeter targeter = get(context, Targeter.class);
- return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
- }
看一下创建Feign.Builder的逻辑,Feign.Builder用来创建一个Feign(ReflectiveFeign)对象,Feign对象用来为我们写的Feign接口创建动态代理:
- protected Feign.Builder feign(FeignContext context) {
- FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
- Logger logger = loggerFactory.create(type);
-
- // @formatter:off
- //从feignClient上下文获取Feign.Builder,在FeignClientsConfiguration中自动装配
- //如果没有引入断路器或是其它扩展组件,默认就是Feign.Builder
- //如果引入了circuitbreaker,则为FeignCircuitBreaker.Builder
- 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
- //根据父容器的客户端配置配置feignClient
- //feignClient的配置优先级,默认情况下是
- //FeignClientProperties>FeignClientSpecification>自定义配置类>FeignClientsConfiguration
- //当然也可以用feign.client.defaultToProperties设置
- configureFeign(context, builder);
-
- return builder;
- }
然后看loadBalance(builder, context, new HardCodedTarget<>(type, name, url)),在创建feign客户端的时候会加入负载均衡器:
- protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
- //获取feign客户端,会在Feign.Builder中创建,默认是Client.Default
- //可以替换为像ApacheHttpClient等。
- //当然这里一般是FeignBlockingLoadBalancerClient,因为引入了spring cloud负载均衡器
- //FeignBlockingLoadBalancerClient是对Client.Default的包装,使客户端具有了负载均衡的能力
- Client client = getOptional(context, Client.class);
- if (client != null) {
- builder.client(client);
- //用FeignBuilderCustomizer配置Feign.Builder,如果有的话
- applyBuildCustomizers(context, builder);
- //从容器中获取Targeter,默认是DefaultTargeter
- //如果引入了断路器,则为FeignCircuitBreakerTargeter,会加入降级的逻辑fallback
- Targeter targeter = get(context, Targeter.class);
- //创建feign客户端
- return targeter.target(this, builder, context, target);
- }
-
- throw new IllegalStateException(
- "No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
- }
-
-
- public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
- Target.HardCodedTarget<T> target) {
- if (!(feign instanceof FeignCircuitBreaker.Builder)) {
- return feign.target(target);
- }
- //因为引入了断路器,把Feign.Builder强转为FeignCircuitBreaker.Builder
- FeignCircuitBreaker.Builder builder = (FeignCircuitBreaker.Builder) feign;
- String name = !StringUtils.hasText(factory.getContextId()) ? factory.getName() : factory.getContextId();
- //获取降级类或降级工厂
- Class<?> fallback = factory.getFallback();
- if (fallback != void.class) {
- //以fallback class为例
- return targetWithFallback(name, context, target, builder, fallback);
- }
- Class<?> fallbackFactory = factory.getFallbackFactory();
- if (fallbackFactory != void.class) {
- return targetWithFallbackFactory(name, context, target, builder, fallbackFactory);
- }
- return builder(name, builder).target(target);
- }
-
-
- private <T> T targetWithFallback(String feignClientName, FeignContext context, Target.HardCodedTarget<T> target,
- FeignCircuitBreaker.Builder builder, Class<?> fallback) {
- //获取fallback对象
- T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
- //FeignCircuitBreaker.Builder#target
- return builder(feignClientName, builder).target(target, fallbackInstance);
- }
使用Builder的build方法创建Feign对象:
- public Feign build(final FallbackFactory<?> nullableFallbackFactory) {
- //创建Feign对象(ReflectiveFeign)
- //设置InvocationHandler工厂为FeignCircuitBreakerInvocationHandler
- super.invocationHandlerFactory((target, dispatch) -> new FeignCircuitBreakerInvocationHandler(
- circuitBreakerFactory, feignClientName, target, dispatch, nullableFallbackFactory,
- circuitBreakerGroupEnabled, circuitBreakerNameResolver));
- //调用父类创建ReflectiveFeign
- return super.build();
- }
下面看一下创建ReflectiveFeign逻辑,ReflectiveFeign用来创建动态代理:
- public Feign build() {
- //在FeignClientFactoryBean#configureFeign中会把容器中的Capability设置进来
- //Capability对象可以增强Feign的各个组件,一般就是对原有的组件的包装
- Client client = Capability.enrich(this.client, capabilities);
- Retryer retryer = Capability.enrich(this.retryer, capabilities);
- List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
- .map(ri -> Capability.enrich(ri, capabilities))
- .collect(Collectors.toList());
- Logger logger = Capability.enrich(this.logger, capabilities);
- Contract contract = Capability.enrich(this.contract, capabilities);
- Options options = Capability.enrich(this.options, capabilities);
- Encoder encoder = Capability.enrich(this.encoder, capabilities);
- Decoder decoder = Capability.enrich(this.decoder, capabilities);
- InvocationHandlerFactory invocationHandlerFactory =
- Capability.enrich(this.invocationHandlerFactory, capabilities);
- QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
- //SynchronousMethodHandler.Factory,用来创建MethodHandler的工厂
- SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
- new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
- logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
- //ParseHandlersByName,用来创建方法名与对应MethodHandler的映射关系
- ParseHandlersByName handlersByName =
- new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
- errorDecoder, synchronousMethodHandlerFactory);
- return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
- }
- }
Feign#newInstance创建feign接口的动态代理:
- public <T> T newInstance(Target<T> target) {
- //这里的target为HardCodedTarget,包含了该feign客户端的接口类型,name,url
- //用ParseHandlersByName创建方法名和MethodHandler的映射map
- Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
- //Method对象和MethodHandler的映射map
- 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)) {
- //如果是default方法,则用DefaultMethodHandler处理,这种方法会使用java7的MethodHandle调用
- DefaultMethodHandler handler = new DefaultMethodHandler(method);
- defaultMethodHandlers.add(handler);
- methodToHandler.put(method, handler);
- } else {
- //如果是自定义的方法,也就是我们自己写的方法则从nameToHandler里面获取对应的SynchronousMethodHandler
- methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
- }
- }
- //jdk动态代理的InvocationHandler,这里为FeignCircuitBreakerInvocationHandler
- InvocationHandler handler = factory.create(target, methodToHandler);
- //创建动态代理
- T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
- new Class<?>[] {target.type()}, handler);
-
- for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
- //将MethodHandle绑定到代理对象
- defaultMethodHandler.bindTo(proxy);
- }
- return proxy;
- }
动态代理这下就创建完了,接下来是feign接口的调用过程。
未完待续。。。