今天我们来说一下spring cloud 微服务之间远程调用组件feign。还是先从一个注解开始@EnableFeignClients,老样子引入了一个FeignClientsRegistrar,是一个注册器,看名字就知道,通过扫描某个特性的类注册成spring中的Bean。他的生命周期方法 registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
registerDefaultConfiguration注册 EnableFeignClients注解里面定义的defaultConfiguration属性我们着重要看的是 registerFeignClients
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
Set<String> basePackages;
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
FeignClient.class); //设置要扫描的注解为FeignClient
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients"); //查看EnableFeignClients注解有没有配置clients属性
if (clients == null || clients.length == 0) { //如果没有设置,那么加入要扫描的注解和扫描的包
scanner.addIncludeFilter(annotationTypeFilter);
basePackages = getBasePackages(metadata);
}
else { //如果设置了,还需要加上一个判断条件,最终扫出来的Bean必须是注解中设置的那些
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)));
}
for (String basePackage : basePackages) {
Set<BeanDefinition> candidateComponents = scanner
.findCandidateComponents(basePackage); //查找候选的Bean
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()); //查找FeignClient注解身上设置的属性
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration")); //注册FeignClient的配置文件的信息
registerFeignClient(registry, annotationMetadata, attributes); //注册Bean
}
}
}
}
设置扫描的注解为FeignClient。查看EnableFeignClients注解有没有配置clients属性,如果没有设置,那么加入要扫描的注解和扫描的包如果设置了,还需要加上一个判断条件,最终扫出来的Bean必须是注解中设置的那些.查找到候选的Bean后遍历注册FeignClient的配置文件的信息,注册FeignClient的实例。
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(FeignClientFactoryBean.class);
//logger.info("TEX do some replacement");
//attributes.put("value", ((String)attributes.get("value")).replace('_','-'));
validate(attributes);
definition.addPropertyValue("url", getUrl(attributes));
definition.addPropertyValue("path", getPath(attributes));
String name = getName(attributes);
definition.addPropertyValue("name", name);
definition.addPropertyValue("type", className);
definition.addPropertyValue("decode404", attributes.get("decode404"));
definition.addPropertyValue("fallback", attributes.get("fallback"));
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
String alias = name + "FeignClient";
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setPrimary(true);
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
new String[] { alias });
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
注册的时候无非是将注解中的信息设置进来。注意一点,生成的Bean是FeignClientFactoryBean的实例。既然是FactoryBean,那么我们就看一下他的getObject方法
@Override
public Object getObject() throws Exception {
FeignContext context = applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
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));
}
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
this.url = "http://" + this.url;
}
String url = this.url + cleanPath();
return targeter.target(this, builder, context, new HardCodedTarget<>(
this.type, this.name, url));
}
如果指定了URL那么久根据url来调用,相当于直连,那么久不需要负载均衡了。如果没有指定的话,就可以使用负载均衡,区别就在于client,使用ribbon提供的,需要引入spring-cloud-starter-ribbon的包
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-ribbon?");
}
至于targeter.target就是利用动态代理,创建出我们需要的对象,也就是factoryBean中getObject得到的Bean.
static class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
HardCodedTarget<T> target) {
if (factory.fallback == void.class
|| !(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
Object fallbackInstance = context.getInstance(factory.name, factory.fallback);
if (fallbackInstance == null) {
throw new IllegalStateException(String.format(
"No fallback instance of type %s found for feign client %s",
factory.fallback, factory.name));
}
if (!target.type().isAssignableFrom(factory.fallback)) {
throw new IllegalStateException(
String.format(
"Incompatible fallback instance. Fallback of type %s is not assignable to %s for feign client %s",
factory.fallback, target.type(), factory.name));
}
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
return builder.target(target, (T) fallbackInstance);
}
}
上面主要描述了,Hystrix对于fallback情况的处理,如果我们没有设置fallback那么我们直接执行 feign.target(target),否则我们设置一下fallback,然后执行
builder.target(target, (T) fallbackInstance),我们先看一下普通的 feign.target(target)
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
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);
}
@SuppressWarnings("unchecked")
@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 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 = 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;
}
当调用
build时我们创建了一个很有用的对象
handlersByName,主要是用来解析Feign接口中方法的。看一下
newInstance的第一行
targetToHandlersByName.apply(target)就是开始执行解析接口中的方法。
public Map<String, MethodHandler> apply(Target key) {
List<MethodMetadata> metadata = contract.parseAndValidatateMetadata(key.type()); //key的type就是接口,根据接口找到里面的方法,利用注RequestMapping
Map<String, MethodHandler> result = new LinkedHashMap<String, MethodHandler>();
for (MethodMetadata md : metadata) {
BuildTemplateByResolvingArgs buildTemplate;
if (!md.formParams().isEmpty() && md.template().bodyTemplate() == null) {
buildTemplate = new BuildFormEncodedTemplateFromArgs(md, encoder);
} else if (md.bodyIndex() != null) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md);
}
result.put(md.configKey(),
factory.create(key, md, buildTemplate, options, decoder, errorDecoder));
}
return result;
}
}
解析出每个feign接口中的方法放入map中返回,创建InvocationHandler时传入解析出来的方法,利用动态代理创建出代理对象。然后这个代理对象作为此接口所实现的Bean放入容器准备调用。
以上