OpenFeign使用@EnableFeignClients
开启服务,该注解标有@Import(FeignClientsRegistrar.class)
,该ImportBeanDefinitionRegistrar
会利用扫描路径的方式扫描java文件中带有的@FeignClient(...)
的接口,关于这种扫描注解的方式,我仿照写了简化实现:mini-explore
注意,@FeignClient
只能注解在接口上。且该接口的所有方法都必须有@GetMapping
/@PostMapping
注解,此处由feign.Contract
接口的实现类SpringMvcContract
控制。
OpenFeign通过对注解了@FeignClient
的类生成代理该类的BeanDefinition
,并默认设置该BeanDefinition.setPrimary(true)
,所以即使存在重复类型的bean,依然会走OpenFeign的代理。
代理该类的BeanDefinition
通过如下方式获取:
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,Map<String, Object> attributes) {
...
// 虽然为FactroyBean接口实现类
// 但该FactroyBean并未注入容器
// 只是为了获取代理,完全可以不用实现FactoryBean接口
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
// 支持${}占位符,其中会使用Environment extends PropertyResolver#resolvePlaceholders
// url也支持占位符
String name = getName(attributes);
definition.addPropertyValue("name", name);
...
// 提供Supplier>
// 在spring依赖注入时,会走这个匿名类的Supplier获取代理类对象
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
...
// *********获取代理对象*********
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
// 获取beanDefinition
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
// 注册beanDefinition
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
由于实现了懒加载,所以并不会在应用启动时就开始OpenFeign生成代理的流程。当用到@FeignClient
的接口中的方法时,spring会自动依赖注入动态代理接口的类,此时会使用BeanDefinitionBuilder.genericBeanDefinition(Class,Supplier)
方法提供的Supplier>
来返回对象,该逻辑在AbstractAutowireCapableBeanFactory
中,如下:
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
implements AutowireCapableBeanFactory {
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
...
Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
if (instanceSupplier != null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
...
}
}
最终会到FeignClientFactoryBean#getObject
中拿代理对象,动态代理和负载均衡实现的入口。
public class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
@Override
public Object getObject() {
return getTarget();
}
<T> T getTarget() {
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
// BUILDER
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
// 没有提供url,则根据name进行负载均衡
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
// 提供了url,则直接连接url
Client client = getOptional(context, Client.class);
builder.client(client);
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
}
Targeter#target
最终会调用ReflectiveFeign#newInstance
public class ReflectiveFeign extends Feign {
@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
// 是否是接口的default的方法
if (Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
}
// 加入MethodHandler的实现类SynchronousMethodHandler
// SynchronousMethodHandler委托Client最终执行远程调用
else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// JDK动态代理
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
...
return proxy;
}
}
MethodHandler
的实现类SynchronousMethodHandler
委托给Client#execute
接口实现类最终执行远程调用,Client接口实现类中可以实现负载均衡,注意OpenFeign的负载均衡需要引入spring-cloud-starter-loadbalancer
依赖。
上面的LoadBalanceClient用于代理一个Client做负载均衡和重试,最终调用的被代理的Client实现了Http远程调用的功能,而Http远程调用工具有多种,OpenFeign提供了4种Http调用的Client:
Client.Default
ApacheHttpClient
ApacheHttp5Client
OkHttpClient
所以上面的FeignBlockingLoadBalancerClient
和RetryableFeignBlockingLoadBalancerClient
可以代理以上的4种,共有4*2=8种配置。
if Spring Cloud LoadBalancer is in the classpath,
FeignBlockingLoadBalancerClient
is used. If none of them is in the classpath, the default feign client is used
同时,Feign也提供了重试操作,具体的调用多层代理过程如下图(实现类方便举例,可配置更改):
关于openFeign中的Retryer重试,可在@FeignClient中设置configuration参数,但是在企业中,OpenFeign的接口通常定义为二方包供其他服务调用,不进行复杂的设置,至于OpenFeign调用的重试机制可整合spring-retry
实现。
OpenFeign需要依赖loadbalancer
的org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory
实现负载均衡(OpenFeign老版本通过Ribbon实现负载均衡)