本篇主要分析open feign client组件。
OpenFeign是微服务(Eureka Client)之间进行HTTP请求调用的组件。不适用于外部和服务之间的调用。
分析OpenFeign有两条主线,一静一动。静态主线,即OpenFeign配置加载,FeignClient初始化等;动态主线,即http调用。静态主线是动态主线的根基。
本文分析静态主线,从@EnableFeignClients注解
——>BeanDefinition
——>实例初始化
。
BeanDefinition
要分析feign,入手点肯定是@EnableFeignClients
注解了。因为这是使用在Application上的,是所有feign的起点。下面是EnableFeignClients .java
的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//FeignClientsRegistrar是ImportBeanDefinitionRegistrar的子类,用来处理@FeignClient注解
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* basePackages()属性的别名
*/
String[] value() default {};
/**
* 扫描基本包的注解组件,允许string类型
* @return 基本包名数组
*/
String[] basePackages() default {};
/**
* 和上面的basePackages()一样,只允许Class类型
* @return 基本类名数组
*/
Class>[] basePackageClasses() default {};
/**
* 一个给所有feign client自定义的@Configuration,能够包含@Bean的作用,例如feign.codec.Decoder,feign.codec.Encoder,feign.Contract。
* 默认为 FeignClientsConfiguration。
*/
Class>[] defaultConfiguration() default {};
/**
* 所有带@FeignClient注解的类。如果不为空,关闭路径扫描机制
* @return
*/
Class>[] clients() default {};
}
上面的代码中,FeignClientsRegistrar是ImportBeanDefinitionRegistrar的子类,Spring用ImportBeanDefinitionRegistrar来动态注册BeanDefinition。而OpenFeign通过FeignClientsRegistrar来处理@FeignClient修饰的FeignClient接口类。将这些接口类的BeanDefinition注册到Spring容器中,这样就可以使用@Autowired等方式来装载这些FeignClient接口类的Bean实例。
注册BeanDefinition
其中org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
方法是用来注册BeanDefinition的。BeanDefinition通俗点来讲,就是可以@Autowired来引用这个类或接口。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注册所有@EnableFeignClients提供的配置属性中相关Bean实例
registerDefaultConfiguration(metadata, registry);
//扫描package,注册带@FeignClient接口类的Bean信息
registerFeignClients(metadata, registry);
}
下面来看一下这两个方法:
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
Map defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//如果EnableFeignClients配置了defaultConfiguration,才会走这里。一般不进行任何特殊配置,都是有的。如果没有,会使用默认的FeignConfiguration。
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
//是否是嵌套类/内部类
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
入参metadata来源于Spring的importBeanDefinitionRegistrars(它是个Map)的value属性,存放着所有的注解。这个方法在下面注册到FeignClient时也会用到。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
//生成BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
//注册到register
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
其中BeanDefinitionRegistry是Spring框架中,用于动态注册BeanDefinition信息的接口,调用其registerBeanDefinition方法可以将BeanDefinition注册到Spring容器中,其第一个属性就是beanName,eg:default.com.example.feignclient.FeignClientApplication.FeignClientSpecification
。
FeignClientSpecification类实现了NamedContextFactory.Specification
接口,它是OpenFeign组件实例化的重要一环,它持有自定义配置类提供的组件实例,供OpenFeign使用。SpringCloud框架使用NamedContextFactory创建一系列的运行上下文(ApplicationContext),来让对应的Specification在这些上下文中创建实例对象,使得各个上下文中的对象相互独立。
下面是上面提及的注册到register的部分代码:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//此处断言入参不能为空,已省略
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//校验是否有方法覆盖或者静态工厂方法类
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition oldBeanDefinition;
oldBeanDefinition = this.beanDefinitionMap.get(beanName);
if (oldBeanDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
//此处为重复覆盖,上面的标志为false,此处抛错
}
else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (this.logger.isWarnEnabled()) {
this.logger.warn("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
oldBeanDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(oldBeanDefinition)) {
if (this.logger.isInfoEnabled()) {
this.logger.info("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + oldBeanDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
//... 省略日志输出
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//检查工厂Bean是否已经创建
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//将注册的BeanDefinition放到beanDefinitionMap中,同时也放到beanDefinitionNames中
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//manualSingletonNames是手动添加的类的set集合
if (this.manualSingletonNames.contains(beanName)) {
Set updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}
//如果oldBeanDefinition不为空,或者是单例类,重置beanDefinitionMap和BeanDefinitionNames
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
注册FeignClient
下面是注册FeignClient:
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//扫描classpath下所有的@FeignClient注解的类
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set basePackages;
//获取EnableFeignClients注解所有的属性
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
//新建注解过滤器
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class>[] clients = attrs == null ? null
: (Class>[]) attrs.get("clients");
//上面讲到过这个属性,如果为空,则扫描带@FeignClient注解的类
if (clients == null || clients.length == 0) {
//添加Filter,设置基本包路径
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
final Set 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)));
}
//遍历basePackages
for (String basePackage : basePackages) {
Set 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();
//获取@FeignClient中的所有属性
Map attributes = annotationMetadata.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//获取@FeignClient注解中的value属性
String name = getClientName(attributes);
//注册name到注册表中
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注册FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
实例初始化
FeignClientFactoryBean是Spring实例化所有带@FeignClient注解的类的工厂类。通过其getObject()来获取Bean实例。其代码如下所示:
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
//默认的url为""
if (!StringUtils.hasText(this.url)) {
String url;
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));
}
// ....下面测试没有走到,暂且不表
在OpenFeign中,FeignContext继承了NamedContextFactory,用于存储各类OpenFeign的组件实例。
下面说一下loadBalance方法,它负责对HTTP请求组成等:
protected T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget 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?");
}
此处的targeter为org.springframework.cloud.openfeign.HystrixTargeter
,而此处的build为feign.Feign.Builder
,处理逻辑在org.springframework.cloud.openfeign.HystrixTargeter#target
中,如下:
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
进而进入feign.Feign.Builder#target(feign.Target
方法中:
public T target(Target target) {
return build().newInstance(target);
}
下面是build方法:
public Feign build() {
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory);
}
其中contract为org.springframework.cloud.openfeign.support.SpringMvcContract
,其包含Decoder、Encoder、ErrorCoder,是对参数或请求体进行编解码。所有的请求,如果没有指定,全部默认为GET请求。
下面是feign.ReflectiveFeign#newInstance
方法,该方法生成一个api绑定在target上,最后会存入缓存中:
@Override
public T newInstance(Target target) {
//处理该类中每一个方法,返回结果中key,模板
Map nameToHandler = targetToHandlersByName.apply(target);
Map methodToHandler = new LinkedHashMap();
List defaultMethodHandlers = new LinkedList();
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)));
}
}
//动态代理部分,该部分移到最后分析
}
下面是feign.ReflectiveFeign.ParseHandlersByName#apply
方法:
public Map apply(Target key) {
//获取这个接口下所有的方法,key.type()即该接口
List metadata = contract.parseAndValidatateMetadata(key.type());
Map result = new LinkedHashMap();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
//有表单且没有body的模板
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
} else if (md.bodyIndex() != null) {
//有body的模板
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
} else {
//通用模板,一般是get用的
buildTemplate = new BuildTemplateByResolvingArgs(md);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
下面是feign.Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class>)
方法:
@Override
public List parseAndValidatateMetadata(Class> targetType) {
//检查各种属性...代码略
Map result = new LinkedHashMap();
//遍历所有方法
for (Method method : targetType.getMethods()) {
//确定该方法不是Object的方法,不是静态或者default方法
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
MethodMetadata metadata = parseAndValidateMetadata(targetType, method);
checkState(!result.containsKey(metadata.configKey()), "Overrides unsupported: %s",
metadata.configKey());
result.put(metadata.configKey(), metadata);
}
return new ArrayList(result.values());
}
下面是org.springframework.cloud.openfeign.support.SpringMvcContract#parseAndValidateMetadata
方法:
@Override
public MethodMetadata parseAndValidateMetadata(Class> targetType, Method method) {
//将要转译的方法存储起来
this.processedMethods.put(Feign.configKey(targetType, method), method);
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
RequestMapping classAnnotation = findMergedAnnotation(targetType,
RequestMapping.class);
if (classAnnotation != null) {
// produces - use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(ACCEPT)) {
parseProduces(md, method, classAnnotation);
}
// consumes -- use from class annotation only if method has not specified this
if (!md.template().headers().containsKey(CONTENT_TYPE)) {
parseConsumes(md, method, classAnnotation);
}
// headers -- class annotation is inherited to methods, always write these if
// present
parseHeaders(md, method, classAnnotation);
}
return md;
}
其中Feign.configKey(targetType, method)的值格式为UserClient#getSth(String),
下面是feign.Contract.BaseContract#parseAndValidateMetadata
方法:
protected MethodMetadata parseAndValidateMetadata(Class> targetType, Method method) {
MethodMetadata data = new MethodMetadata();
//设置返回值
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
//设置方法的key,格式为UserClient#getSth(String)
data.configKey(Feign.configKey(targetType, method));
if(targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
//处理类上的注解
processAnnotationOnClass(data, targetType);
for (Annotation methodAnnotation : method.getAnnotations()) {
//处理方法上的注解
processAnnotationOnMethod(data, methodAnnotation, method);
}
//检查方法状态 代码略
Class>[] parameterTypes = method.getParameterTypes();
Type[] genericParameterTypes = method.getGenericParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
int count = parameterAnnotations.length;
for (int i = 0; i < count; i++) {
boolean isHttpAnnotation = false;
if (parameterAnnotations[i] != null) {
isHttpAnnotation = processAnnotationsOnParameter(data, parameterAnnotations[i], i);
}
if (parameterTypes[i] == URI.class) {
data.urlIndex(i);
} else if (!isHttpAnnotation) {
//检查表单参数和body
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
//检查head和queries参数
return data;
}
处理类上注解部分,期间会拆分合成注解,如GetMapping拆为RequestMapping(method=GET)。会使用Proxy.newProxyInstance动态代理生成一个SynthesizedAnnotationInvocationHandler
代理类,里面存储着RequestMapping注解的所有键值对。并将类上的路径放置第0位,方便取。下面是org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnClass
代码:
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class> clz) {
if (clz.getInterfaces().length == 0) {
//生成一个RequestMapping的代理类
RequestMapping classAnnotation = findMergedAnnotation(clz,RequestMapping.class);
if (classAnnotation != null) {
// 如果在类上有指定path,则做预置前拼接,即没有/加/
if (classAnnotation.value().length > 0) {
String pathValue = emptyToNull(classAnnotation.value()[0]);
pathValue = resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().insert(0, pathValue);
}
}
}
}
处理方法上的注解,对于RequestMapping的处理和类上是一样的。但是在最后会根据head属性转换为head属性,如produces
会转为accept
,consumes
会转为content-type
,head上的其他属性也会相应的转换过去。代码为org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnMethod
:
@Override
protected void processAnnotationOnMethod(MethodMetadata data,
Annotation methodAnnotation, Method method) {
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
.annotationType().isAnnotationPresent(RequestMapping.class)) {
return;
}
RequestMapping methodMapping = findMergedAnnotation(method, RequestMapping.class);
// HTTP Method,设定默认为get
RequestMethod[] methods = methodMapping.method();
if (methods.length == 0) {
methods = new RequestMethod[] { RequestMethod.GET };
}
checkOne(method, methods, "method");
data.template().method(methods[0].name());
// path
checkAtMostOne(method, methodMapping.value(), "value");
if (methodMapping.value().length > 0) {
String pathValue = emptyToNull(methodMapping.value()[0]);
if (pathValue != null) {
pathValue = resolve(pathValue);
// Append path from @RequestMapping if value is present on method
if (!pathValue.startsWith("/")
&& !data.template().toString().endsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().append(pathValue);
}
}
// produces
parseProduces(data, method, methodMapping);
// consumes
parseConsumes(data, method, methodMapping);
// headers
parseHeaders(data, method, methodMapping);
data.indexToExpander(new LinkedHashMap());
}
在参数处理时,会判断参数是否在head、url、queries上,如果没有,就加到formParams中。
feign.ReflectiveFeign#newInstance
中动态代理部分:
//动态代理部分
//在create方法中,methodToHandler对应dispatch。dispatch在最后进行分析
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class>[]{target.type()}, handler);
//将所有的方法与模板的关系绑定代理类上,在使用时直接从代理类中拿
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
下面是create方法,返回的是ReflectiveFeign.FeignInvocationHandler
类型:
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}