本篇主要分析open feign client組件。
OpenFeign是微服務(wù)(Eureka Client)之間進(jìn)行HTTP請(qǐng)求調(diào)用的組件。不適用于外部和服務(wù)之間的調(diào)用。
分析OpenFeign有兩條主線(xiàn),一靜一動(dòng)。靜態(tài)主線(xiàn),即OpenFeign配置加載,F(xiàn)eignClient初始化等;動(dòng)態(tài)主線(xiàn),即http調(diào)用。靜態(tài)主線(xiàn)是動(dòng)態(tài)主線(xiàn)的根基。
本文分析靜態(tài)主線(xiàn),從@EnableFeignClients注解
——>BeanDefinition
——>實(shí)例初始化
。
BeanDefinition
要分析feign,入手點(diǎn)肯定是@EnableFeignClients
注解了。因?yàn)檫@是使用在Application上的,是所有feign的起點(diǎn)。下面是EnableFeignClients .java
的源碼:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//FeignClientsRegistrar是ImportBeanDefinitionRegistrar的子類(lèi),用來(lái)處理@FeignClient注解
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
/**
* basePackages()屬性的別名
*/
String[] value() default {};
/**
* 掃描基本包的注解組件,允許string類(lèi)型
* @return 基本包名數(shù)組
*/
String[] basePackages() default {};
/**
* 和上面的basePackages()一樣,只允許Class類(lèi)型
* @return 基本類(lèi)名數(shù)組
*/
Class<?>[] basePackageClasses() default {};
/**
* 一個(gè)給所有feign client自定義的@Configuration,能夠包含@Bean的作用,例如feign.codec.Decoder,feign.codec.Encoder,feign.Contract。
* 默認(rèn)為 FeignClientsConfiguration。
*/
Class<?>[] defaultConfiguration() default {};
/**
* 所有帶@FeignClient注解的類(lèi)。如果不為空,關(guān)閉路徑掃描機(jī)制
* @return
*/
Class<?>[] clients() default {};
}
上面的代碼中,F(xiàn)eignClientsRegistrar是ImportBeanDefinitionRegistrar的子類(lèi),Spring用ImportBeanDefinitionRegistrar來(lái)動(dòng)態(tài)注冊(cè)BeanDefinition。而OpenFeign通過(guò)FeignClientsRegistrar來(lái)處理@FeignClient修飾的FeignClient接口類(lèi)。將這些接口類(lèi)的BeanDefinition注冊(cè)到Spring容器中,這樣就可以使用@Autowired等方式來(lái)裝載這些FeignClient接口類(lèi)的Bean實(shí)例。
注冊(cè)BeanDefinition
其中org.springframework.cloud.openfeign.FeignClientsRegistrar#registerBeanDefinitions
方法是用來(lái)注冊(cè)BeanDefinition的。BeanDefinition通俗點(diǎn)來(lái)講,就是可以@Autowired來(lái)引用這個(gè)類(lèi)或接口。
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//注冊(cè)所有@EnableFeignClients提供的配置屬性中相關(guān)Bean實(shí)例
registerDefaultConfiguration(metadata, registry);
//掃描package,注冊(cè)帶@FeignClient接口類(lèi)的Bean信息
registerFeignClients(metadata, registry);
}
下面來(lái)看一下這兩個(gè)方法:
private void registerDefaultConfiguration(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
Map<String, Object> defaultAttrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
//如果EnableFeignClients配置了defaultConfiguration,才會(huì)走這里。一般不進(jìn)行任何特殊配置,都是有的。如果沒(méi)有,會(huì)使用默認(rèn)的FeignConfiguration。
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
String name;
//是否是嵌套類(lèi)/內(nèi)部類(lèi)
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name,
defaultAttrs.get("defaultConfiguration"));
}
}
入?yún)etadata來(lái)源于Spring的importBeanDefinitionRegistrars(它是個(gè)Map)的value屬性,存放著所有的注解。這個(gè)方法在下面注冊(cè)到FeignClient時(shí)也會(huì)用到。
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
//生成BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
//注冊(cè)到register
registry.registerBeanDefinition(
name + "." + FeignClientSpecification.class.getSimpleName(),
builder.getBeanDefinition());
}
其中BeanDefinitionRegistry是Spring框架中,用于動(dòng)態(tài)注冊(cè)BeanDefinition信息的接口,調(diào)用其registerBeanDefinition方法可以將BeanDefinition注冊(cè)到Spring容器中,其第一個(gè)屬性就是beanName,eg:default.com.example.feignclient.FeignClientApplication.FeignClientSpecification
。
FeignClientSpecification類(lèi)實(shí)現(xiàn)了NamedContextFactory.Specification
接口,它是OpenFeign組件實(shí)例化的重要一環(huán),它持有自定義配置類(lèi)提供的組件實(shí)例,供OpenFeign使用。SpringCloud框架使用NamedContextFactory創(chuàng)建一系列的運(yùn)行上下文(ApplicationContext),來(lái)讓對(duì)應(yīng)的Specification在這些上下文中創(chuàng)建實(shí)例對(duì)象,使得各個(gè)上下文中的對(duì)象相互獨(dú)立。
下面是上面提及的注冊(cè)到register的部分代碼:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//此處斷言入?yún)⒉荒転榭眨咽÷? if (beanDefinition instanceof AbstractBeanDefinition) {
try {
//校驗(yàn)是否有方法覆蓋或者靜態(tài)工廠方法類(lèi)
((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()) {
//此處為重復(fù)覆蓋,上面的標(biāo)志為false,此處拋錯(cuò)
}
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是否已經(jīng)創(chuàng)建
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
//將注冊(cè)的BeanDefinition放到beanDefinitionMap中,同時(shí)也放到beanDefinitionNames中
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
//manualSingletonNames是手動(dòng)添加的類(lèi)的set集合
if (this.manualSingletonNames.contains(beanName)) {
Set<String> 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不為空,或者是單例類(lèi),重置beanDefinitionMap和BeanDefinitionNames
if (oldBeanDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
注冊(cè)FeignClient
下面是注冊(cè)FeignClient:
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//掃描classpath下所有的@FeignClient注解的類(lèi)
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
//獲取EnableFeignClients注解所有的屬性
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
//新建注解過(guò)濾器
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class);
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
//上面講到過(guò)這個(gè)屬性,如果為空,則掃描帶@FeignClient注解的類(lèi)
if (clients == null || clients.length == 0) {
//添加Filter,設(shè)置基本包路徑
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else {
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)));
}
//遍歷basePackages
for (String basePackage : basePackages) {
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();
//獲取@FeignClient中的所有屬性
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(
FeignClient.class.getCanonicalName());
//獲取@FeignClient注解中的value屬性
String name = getClientName(attributes);
//注冊(cè)name到注冊(cè)表中
registerClientConfiguration(registry, name,
attributes.get("configuration"));
//注冊(cè)FeignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
實(shí)例初始化
FeignClientFactoryBean是Spring實(shí)例化所有帶@FeignClient注解的類(lèi)的工廠類(lèi)。通過(guò)其getObject()來(lái)獲取Bean實(shí)例。其代碼如下所示:
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
//默認(rèn)的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));
}
// ....下面測(cè)試沒(méi)有走到,暫且不表
在OpenFeign中,F(xiàn)eignContext繼承了NamedContextFactory,用于存儲(chǔ)各類(lèi)OpenFeign的組件實(shí)例。
下面說(shuō)一下loadBalance方法,它負(fù)責(zé)對(duì)HTTP請(qǐng)求組成等:
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> 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);
}
進(jìn)而進(jìn)入feign.Feign.Builder#target(feign.Target<T>)
方法中:
public <T> T target(Target<T> 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,是對(duì)參數(shù)或請(qǐng)求體進(jìn)行編解碼。所有的請(qǐng)求,如果沒(méi)有指定,全部默認(rèn)為GET請(qǐng)求。
下面是feign.ReflectiveFeign#newInstance
方法,該方法生成一個(gè)api綁定在target上,最后會(huì)存入緩存中:
@Override
public <T> T newInstance(Target<T> target) {
//處理該類(lèi)中每一個(gè)方法,返回結(jié)果中key,模板
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 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)));
}
}
//動(dòng)態(tài)代理部分,該部分移到最后分析
}
下面是feign.ReflectiveFeign.ParseHandlersByName#apply
方法:
public Map<String, MethodHandler> apply(Target key) {
//獲取這個(gè)接口下所有的方法,key.type()即該接口
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type());
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
//有表單且沒(méi)有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<MethodMetadata> parseAndValidatateMetadata(Class<?> targetType) {
//檢查各種屬性...代碼略
Map<String, MethodMetadata> result = new LinkedHashMap<String, MethodMetadata>();
//遍歷所有方法
for (Method method : targetType.getMethods()) {
//確定該方法不是Object的方法,不是靜態(tài)或者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<MethodMetadata>(result.values());
}
下面是org.springframework.cloud.openfeign.support.SpringMvcContract#parseAndValidateMetadata
方法:
@Override
public MethodMetadata parseAndValidateMetadata(Class<?> targetType, Method method) {
//將要轉(zhuǎn)譯的方法存儲(chǔ)起來(lái)
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();
//設(shè)置返回值
data.returnType(Types.resolve(targetType, targetType, method.getGenericReturnType()));
//設(shè)置方法的key,格式為UserClient#getSth(String)
data.configKey(Feign.configKey(targetType, method));
if(targetType.getInterfaces().length == 1) {
processAnnotationOnClass(data, targetType.getInterfaces()[0]);
}
//處理類(lèi)上的注解
processAnnotationOnClass(data, targetType);
for (Annotation methodAnnotation : method.getAnnotations()) {
//處理方法上的注解
processAnnotationOnMethod(data, methodAnnotation, method);
}
//檢查方法狀態(tài) 代碼略
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) {
//檢查表單參數(shù)和body
data.bodyIndex(i);
data.bodyType(Types.resolve(targetType, targetType, genericParameterTypes[i]));
}
}
//檢查head和queries參數(shù)
return data;
}
處理類(lèi)上注解部分,期間會(huì)拆分合成注解,如GetMapping拆為RequestMapping(method=GET)。會(huì)使用Proxy.newProxyInstance動(dòng)態(tài)代理生成一個(gè)SynthesizedAnnotationInvocationHandler
代理類(lèi),里面存儲(chǔ)著RequestMapping注解的所有鍵值對(duì)。并將類(lèi)上的路徑放置第0位,方便取。下面是org.springframework.cloud.openfeign.support.SpringMvcContract#processAnnotationOnClass
代碼:
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
if (clz.getInterfaces().length == 0) {
//生成一個(gè)RequestMapping的代理類(lèi)
RequestMapping classAnnotation = findMergedAnnotation(clz,RequestMapping.class);
if (classAnnotation != null) {
// 如果在類(lèi)上有指定path,則做預(yù)置前拼接,即沒(méi)有/加/
if (classAnnotation.value().length > 0) {
String pathValue = emptyToNull(classAnnotation.value()[0]);
pathValue = resolve(pathValue);
if (!pathValue.startsWith("/")) {
pathValue = "/" + pathValue;
}
data.template().insert(0, pathValue);
}
}
}
}
處理方法上的注解,對(duì)于RequestMapping的處理和類(lèi)上是一樣的。但是在最后會(huì)根據(jù)head屬性轉(zhuǎn)換為head屬性,如produces
會(huì)轉(zhuǎn)為accept
,consumes
會(huì)轉(zhuǎn)為content-type
,head上的其他屬性也會(huì)相應(yīng)的轉(zhuǎn)換過(guò)去。代碼為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,設(shè)定默認(rèn)為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<Integer, Param.Expander>());
}
在參數(shù)處理時(shí),會(huì)判斷參數(shù)是否在head、url、queries上,如果沒(méi)有,就加到formParams中。
feign.ReflectiveFeign#newInstance
中動(dòng)態(tài)代理部分:
//動(dòng)態(tài)代理部分
//在create方法中,methodToHandler對(duì)應(yīng)dispatch。dispatch在最后進(jìn)行分析
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
//將所有的方法與模板的關(guān)系綁定代理類(lèi)上,在使用時(shí)直接從代理類(lèi)中拿
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
下面是create方法,返回的是ReflectiveFeign.FeignInvocationHandler
類(lèi)型:
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}