引言
本章节只看主干代码,不看分支代码,关于IOC的内容也不做出太多解释,防止打乱思路。
目标
- 找到FeignClient中真正的代理对象
- 找到代理对象如何通过动态代理发出Http请求
正片
EnableFeignClients 是启动Feign的关键,其比较重要的功能有 Scans for interfaces that declare they are feign clients,这个注解很普通,比较关键的是@Import(FeignClientsRegistrar.class),从名字大致就可以看出其作用应该是一个注册FeignClient的内容。
/**
* Scans for interfaces that declare they are feign clients (via
* {@link org.springframework.cloud.openfeign.FeignClient} <code>@FeignClient</code>).
* Configures component scanning directives for use with
* {@link org.springframework.context.annotation.Configuration}
* <code>@Configuration</code> classes.
*
* @author Spencer Gibb
* @author Dave Syer
* @since 1.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
}
点进来发现其实现了三个接口,后面的两个是Aware接口,用于一些回调设置。而ImportBeanDefinitionRegistrar从名字进行直译:引入BeanDefinition注册。
接触过IOC源码的都应该了解,Spring 通过BeanFactory+很多BeanDefinition生成注入Bean,日常我们使用的@Service、@Controller、@RestController等其实都是@Component,本质上我们也是声明了一个BeanDefinition然后由BeanFactory进行Bean的生成以及注入,这里可以认为是一种非常规的处理策略,但是本质是也是一样的。
所以我们接下来的关注点在于这个BeanDefinition列表是如何生成的,内容是什么?
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
}
接下来看ImportBeanDefinitionRegistrar的实现接口,可以看到有两个主要的方法,从名字进行直译。
- registerDefaultConfiguration 注册默认配置
- registerFeignClients 注册FeignClient
从名字就可以看出,registerFeignClients方法就是我们需要去看的内容,registerDefaultConfiguration先不进去看,记住日后再看(思考:朴实无华的代码,就是能对代码封装为方法,方法名见文思意,如果这里不是两个方法,而是一堆代码,相信也许我就会迷路)
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 注册默认配置
registerDefaultConfiguration(metadata, registry);
// 注册FeignClient
registerFeignClients(metadata, registry);
}
}
以下逻辑大致为:
- 获取需要处理的FeignClient的BeanDefinition,如果设置了直接取设置的进行包装,否则就取设置的包路径下的FeignClient进行扫描获取BeanDefinition
- 扫描到的BeanDefinition通过registerFeignClient(registry, annotationMetadata, attributes)进行注册,这里registerClientConfiguration(registry, name, attributes.get("configuration"))依然先不阅读,但是从这里可以看出,这个registerClientConfiguration是每个FeignClient不同的配置,是写在FeignClient上的,而之前的是写在EnableFeignClients上的,代表全局的。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
// 获取EnableFeignClients注解里配置的内容
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
// 如果设置了clients 属性就进行按照包扫描类,直接使用配置
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
// 如果设置了clients 属性就不进行按照包扫描类,直接使用配置
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
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());
String name = getClientName(attributes);
registerClientConfiguration(registry, name, attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
/**
* 获取扫描包的列表
*/
protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
Map<String, Object> attributes = importingClassMetadata
.getAnnotationAttributes(EnableFeignClients.class.getCanonicalName());
Set<String> basePackages = new HashSet<>();
for (String pkg : (String[]) attributes.get("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : (String[]) attributes.get("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : (Class[]) attributes.get("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果以上都没有配置的话,就获取当前类(主类)的包路径
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
}
return basePackages;
}
}
大致可以看出还是将注解上的一些内容设置到FeignClientFactoryBean对象中,利用BeanDefinitionBuilder.genericBeanDefinition注册一个类型为clazz,生成实例方法为FeignClientFactoryBean.getObject()方法的BeanDefinition并且进行注册,接下来我们需要关注的是FeignClientFactoryBean的getObject方法生成的最后对象到底是什么?(registerOptionsBeanDefinition这个方法也先不看)
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
factoryBean.setRefreshableClient(isClientRefreshEnabled());
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class ? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class ? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(), null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
registerOptionsBeanDefinition(registry, contextId);
}
}
FeignClientFactoryBean中有很多成员变量,都是来自于上一步进行设置。 仔细看getTarget()其中的逻辑,实际上针对于常见的根据服务名以及根据url的两种方式进行url调整,比如以下两种情况:
- @FeignClient(value="auth-service")
- @FeignClient(url="127.0.0.1:8088")
会转化为 http://auth-service 以及 http://127.0.0.1:8088
其中Client为Http调用时采用的Client,常用的有ApacheHttpClient,OKHttpClient以及Feign默认的Client(采用jdk net),这里的client是直接从Spring容器中获取,具体细节在Feign源码解析(四)里结合这里进行阐述。
接下来还是把重点放在 targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url))上,需要知道返回的对象到底是什么?
public class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
@Override
public Object getObject() {
return getTarget();
}
/**
* @param <T> the target type of the Feign client
* @return a {@link Feign} client created with the specified data and the context
* information
*/
<T> T getTarget() {
FeignContext context = beanFactory != null ? beanFactory.getBean(FeignContext.class)
: applicationContext.getBean(FeignContext.class);
Feign.Builder builder = feign(context);
if (!StringUtils.hasText(url)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
String url = this.url + cleanPath();
Client client = getOptional(context, Client.class);
if (client != null) {
if (client instanceof FeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((FeignBlockingLoadBalancerClient) client).getDelegate();
}
if (client instanceof RetryableFeignBlockingLoadBalancerClient) {
// not load balancing because we have a url,
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
client = ((RetryableFeignBlockingLoadBalancerClient) client).getDelegate();
}
builder.client(client);
}
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
}
}
如下,这里有多个实现,直接选取DefaultTargeter进行查看,后面章节解释为什么选择它。
可以看到都是利用传入的对象进行调用生成,主要利用Builder中的target()方法进行对象的构建
class DefaultTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
return feign.target(target);
}
}
可以看到首先对一些成员变量进行处理,这些我们都选不看,打个标记之后看。直接看new ReflectiveFeign的newInstance()方法
public abstract class Feign {
public static Builder builder() {
return new Builder();
}
public <T> T target(Target<T> target) {
return build().newInstance(target);
}
public Feign build() {
Client client = Capability.enrich(this.client, capabilities);
Retryer retryer = Capability.enrich(this.retryer, capabilities);
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Encoder encoder = Capability.enrich(this.encoder, capabilities);
Decoder decoder = Capability.enrich(this.decoder, capabilities);
InvocationHandlerFactory invocationHandlerFactory =
Capability.enrich(this.invocationHandlerFactory, capabilities);
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
}
以下代码中最显眼的代码为:
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler)
很明显,我们已经找到了动态代理的地方,最终生成的是一个代理对象,被代理的类为target.type(),其实就是我们@FeignClient标记的接口类,代理的类为InvocationHandler,根据JDK动态代理可知,我们要着重去看InvocationHandler的invork方法(defaultMethodHandlers先不看,targetToHandlersByName.apply(target) 这个方法先打个标记,下面还会返回来看 )
public class ReflectiveFeign extends Feign {
/**
* creates an api binding to the {@code target}. As this invokes reflection, care should be taken
* to cache the result.
*/
@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 {
// 接口中非默认方法,采用nameToHandler中预先设置的处理
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
InvocationHandler handler = factory.create(target, methodToHandler);
// JDK动态代理
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[] {target.type()}, handler);
for (DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
}
生成InvocationHandler的factory依然有多个实现,这里我们看默认实现
以下是默认生成InvocationHandler的实现,我们接下来需要去看new ReflectiveFeign.FeignInvocationHandler(target, dispatch)的invork()方法
public interface InvocationHandlerFactory {
InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch);
/**
* Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a
* single method.
*/
interface MethodHandler {
Object invoke(Object[] argv) throws Throwable;
}
static final class Default implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
return new ReflectiveFeign.FeignInvocationHandler(target, dispatch);
}
}
}
其中关键部分在于dispatch.get(method).invoke(args),说明最终是根据调用的方法,从dispatch获取真正的对象进行调用,dispatch是我们之前传入的,这里再返回去看一下
public class ReflectiveFeign extends Feign {
static class FeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
FeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
return dispatch.get(method).invoke(args);
}
}
直接看factory.create(target, md, buildTemplate, options, decoder, errorDecoder)生成对象的invork方法
public class ReflectiveFeign extends Feign {
public Map<String, MethodHandler> apply(Target target) {
List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type());
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, queryMapEncoder, target);
} else if (md.bodyIndex() != null || md.alwaysEncodeBody()) {
buildTemplate = new BuildEncodedTemplateFromArgs(md, encoder, queryMapEncoder, target);
} else {
buildTemplate = new BuildTemplateByResolvingArgs(md, 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(),
factory.create(target, md, buildTemplate, options, decoder, errorDecoder));
}
}
return result;
}
}
直接看SynchronousMethodHandler的invork()方法
final class SynchronousMethodHandler implements MethodHandler {
public MethodHandler create(Target<?> target,
MethodMetadata md,
RequestTemplate.Factory buildTemplateFromArgs,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, decode404, closeAfterDecode, propagationPolicy, forceDecoding);
}
}
终于找到想看到的内容啦,这里可以看到通过入参进行encode,经过client进行Http调用之后然后decode解码返回
final class SynchronousMethodHandler implements MethodHandler {
@Override
public Object invoke(Object[] argv) throws Throwable {
// 通过入参构建RequestTemplate
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
// 重试
Retryer retryer = this.retryer.clone();
while (true) {
try {
// 执行请求并且进行解码
return executeAndDecode(template, options);
} catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}
}
总结
通过以上我们实现了本章节既定的目标,可以同样的思考下,和Feign类似的Mybatis的实现,如果让你来手写代码如何去实现?
展望
接下来我们留下的疑惑有:
- encoder和decoder以及调用等实际过程(Feign源码解析(三))
- 本章节中我们忽略的标记点(Feign源码解析(三))
- FeignAutoConfiguration的解读,约定大于配置,SpringBoot为我们偷偷干的事情(Feign源码解析(四))