本文分两个部分:
1、项目启动,openfeign如何加载远程接口实例化并且注入到容器中
2、远程调用实现原理
1、openfeign加载
1.1、在启动类加入开启feign注解
@EnableFeignClients
1.2、远程调用接口
1.3、在使用的地方注入
1.4、openfeign工作原理
我们知道openfeign是一个rpc远程调用框架,其本质上还是http请求访问。
openFeign是通过jdk代理来实现这个操作的,下面我们将通过源码一步步探寻它的运作原理。
我们的目的很简单,因为我们在使用的时候是直接进行注入的,我们只需要知道每一个接口是如何被注入到Spring容器的,找到代理所做的事情即可。
1.5、@EnableFeignClients
我们先看最开始的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};
Class<?>[] clients() default {};
}
发现有个@Import(FeignClientsRegistrar.class) ,这其实就是将FeignClientsRegistrar类注入,启动类运行会先引入此类。
1.6 FeignClientsRegistrar
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar
并重写了里面的方法registerBeanDefinitions
,会自动调用这个方法进行初始化。所以我们在看看重写的registerBeanDefinitions做了什么。
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//注册默认配置
this.registerDefaultConfiguration(metadata, registry);
//注册feign客户端
this.registerFeignClients(metadata, registry);
}
1.7 registerFeignClients
此方法扫描使用了@FeignClient注解的接口并进行注入
这里有两种方式,如果你使用了@EnableFeignClients 的 clients那它就直接去获取对应的class,不通过类路径自动扫描
@EnableFeignClients(clients = {SchedualServiceHi.class, SchedualServiceHi2.class})
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 获取@EnableFeignClients注解里面的参数
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
Class<?>[] clients = attrs == null ? null : (Class[])((Class[])attrs.get("clients"));
Object basePackages;
// 如果使用了clients 就根据class加载
if (clients != null && clients.length != 0) {
((Set)basePackages).add(ClassUtils.getPackageName(clazz));
} else {
// 否则使用类路径加载
basePackages = this.getBasePackages(metadata);
}
// 迭代全部的接口
Iterator var17 = ((Set)basePackages).iterator();
while(var17.hasNext()) {
String basePackage = (String)var17.next();
Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
Iterator var21 = candidateComponents.iterator();
while(var21.hasNext()) {
BeanDefinition candidateComponent = (BeanDefinition)var21.next();
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// 进行下一步的注册
this.registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
1.8 registerFeignClient
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
//...
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
//...
}
可以看到registerFeignClient方法里面生成了一个BeanDefinitionBuilder,而入参是FeignClientFactoryBean
1.9 FeignClientFactoryBean
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {}
可以看到它是实现了FactoryBean
里面有一个getObject
是获取bean实例的,FeignClientFactoryBean 重写了这个 getObject
public Object getObject() throws Exception {
return this.getTarget();
}
<T> T getTarget() {
FeignContext context = (FeignContext)this.applicationContext.getBean(FeignContext.class);
Builder builder = this.feign(context);
// url 为 null 、""、" " StringUtils.hasText 就返回false
if (!StringUtils.hasText(this.url)) {
// ......
} else {
// ......
Targeter targeter = (Targeter)this.get(context, Targeter.class);
return targeter.target(this, builder, context, new HardCodedTarget(this.type, this.name, url));
}
}
Targeter 是一个接口,它有两个实现类,我们来看默认的实现类
这个FactoryBean接口,可以理解成是Spring的一个钩子,它的getObject方法,是在初始化bean的时候会去调用。
target 最终调用的是 Feign.class
方法
public <T> T target(Target<T> target) {
return this.build().newInstance(target);
}
public Feign build() {
Client client = (Client)Capability.enrich(this.client, this.capabilities);
Retryer retryer = (Retryer)Capability.enrich(this.retryer, this.capabilities);
List<RequestInterceptor> requestInterceptors = (List)this.requestInterceptors.stream().map((ri) -> {
return (RequestInterceptor)Capability.enrich(ri, this.capabilities);
}).collect(Collectors.toList());
Logger logger = (Logger)Capability.enrich(this.logger, this.capabilities);
Contract contract = (Contract)Capability.enrich(this.contract, this.capabilities);
Request.Options options = (Request.Options)Capability.enrich(this.options, this.capabilities);
Encoder encoder = (Encoder)Capability.enrich(this.encoder, this.capabilities);
Decoder decoder = (Decoder)Capability.enrich(this.decoder, this.capabilities);
InvocationHandlerFactory invocationHandlerFactory = (InvocationHandlerFactory)Capability.enrich(this.invocationHandlerFactory, this.capabilities);
QueryMapEncoder queryMapEncoder = (QueryMapEncoder)Capability.enrich(this.queryMapEncoder, this.capabilities);
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory = new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger, this.logLevel, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding);
ReflectiveFeign.ParseHandlersByName handlersByName = new ReflectiveFeign.ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder, this.errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
build()返回一个ReflectiveFeign最后创建一个新实例
public <T> T newInstance(Target<T> target) {
Map<String, InvocationHandlerFactory.MethodHandler> nameToHandler = this.targetToHandlersByName.apply(target);
Map<Method, InvocationHandlerFactory.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;
}
在newInstance
方法里面有一个JDK代理,所以openfeign其实是基于JDK代理来实现的。JDK代理是基于接口代理的,所以主要还是我们的InvocationHandler
,它代表了我们代理对象最终指向的内容,所以下面我们就要来看看这个handler是如何产生的。
1.10 this.targetToHandlersByName.apply(target);
public Map<String, InvocationHandlerFactory.MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = this.contract.parseAndValidateMetadata(target.type());
Map<String, InvocationHandlerFactory.MethodHandler> result = new LinkedHashMap();
Iterator var4 = metadata.iterator();
while(var4.hasNext()) {
MethodMetadata md = (MethodMetadata)var4.next();
Object buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
} else if (md.bodyIndex() == null && !md.alwaysEncodeBody()) {
buildTemplate = new BuildTemplateByResolvingArgs(md, this.queryMapEncoder, target);
} else {
buildTemplate = new BuildEncodedTemplateFromArgs(md, this.encoder, this.queryMapEncoder, target);
}
if (md.isIgnored()) {
result.put(md.configKey(), (args) -> {
throw new IllegalStateException(md.configKey() + " is not a method handled by feign");
});
} else {
result.put(md.configKey(), this.factory.create(target, md, (RequestTemplate.Factory)buildTemplate, this.options, this.decoder, this.errorDecoder));
}
}
return result;
}
this.contract.parseAndValidateMetadata(target.type())表示获取当前对象的方法
最后返回 类名#方法名
为key,MethodHandler
为value的map
存入map的时候调用的方法是
this.factory.create(key, md, (Factory)buildTemplate, this.options, this.decoder, this.errorDecoder))
点进去这个 create
方法,我们看到是new了一个对象,这个类是实现了MethodHandler
return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode, this.propagationPolicy);
而这一个 apply我们会走很多遍,一个远程接口每个方法都会走一遍
1.11 this.factory.create(target, methodToHandler)
我们再返回newInstance 方法,可以看到最终是调用了一个 this.factory.create 来创建 InvocationHandler,InvocationHandlerFactory 的 create 里面自己就对这个接口进行实现 return new FeignInvocationHandler(target, dispatch);
1.12 FeignInvocationHandler
FeignInvocationHandler 实现了 InvocationHandler 接口,重写了里面的 invoke
方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
return "toString".equals(method.getName()) ? this.toString() : ((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args);
}
} else {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return this.equals(otherHandler);
} catch (IllegalArgumentException var5) {
return false;
}
}
}
可以看到它最终是调用MethodHandler的 invoke方法。至此我们的第一部分算是走完了。
2、远程调用实现原理
2.1 SynchronousMethodHandler
由于上面FeignInvocationHandler.invoke()最后返回的是((InvocationHandlerFactory.MethodHandler)this.dispatch.get(method)).invoke(args),而这个MethodHandler实际上就是SynchronousMethodHandler
发现最后方法参数会在this.buildTemplateFromArgs.create(argv);返回请求模板,最后去访问executeAndDecode方法,也就是真正执行http请求的方法执行并且解码。
执行真正的http请求,至此feign调用原理走完了