" Spring 到底是春天的来临万物复苏,还是春转夏的干燥又炎热呢?" Spring的来临让JavaEE走向了另一个高度。便捷的开发,完美的生态。物极必反,学习Spring的成本越来越低,导致Java程序员越来越密集,越来越廉价…… 那么又有多少 Springer 耐下性子去研究Spring生态的架构和底层实现的细节呢??
版本信息如下:
Spring:5.3.23
Spring Boot:2.6.13
Spring Cloud:3.1.5
Spring Cloud Openfeign:3.1.5
Feign:11.3.0
既然是研究Spring Cloud源码,那么没有Spring、Spring Boot的前置知识,只会让你永远迈不出第一步。下面是对于Spring Cloud的架构图。
既然是学习OpenFeign源码,我们第一步是不是应该清楚Openfeign的架构,由上图我们得知Spring Cloud 组件名 承上启下,向下兼容组件,向上实现规范和扩展。那么把Spring Cloud Openfeign带入进去就很好理解了,向下兼容Feign这个组件,向上实现规范和扩展。
反观,我们连Feign这个组件的原理都不知道,就去学习Spring Cloud Openfeign那怎么学得懂呢?下面是Feign这个组件的架构思想图。一言以蔽之:简化Http客户端的操作,兼容众多Http客户端框架,向上暴露出接口+注解+配置的简易操作方式。
先学会使用,再来研究其源码,Spring Cloud Feign的使用很简单,其实单独使用Feign这个组件跟其大同小异。接口+注解的使用方法,底层帮开发者自动去调用服务名为deptmanagecloud-provider其中的/provider/pt接口。
//@Service
@FeignClient(value = "deptmanagecloud-provider")
public interface FeignConsumer {
@GetMapping("/provider/pt")
String provider();
}
那么,接下来就是去研究其源码,到底如何帮开发者自动去调用的过程。
项目中要使用Spring Cloud OpenFeign就需要使用@EnableFeignClients注解。 所以这将是我们的入手点。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
此注解配合@Configuration注解一起使用,一般是配合@SpringBootApplication注解(也是一个@Configuration)使用,而@EnableFeignClients注解又使用了@Import注解。所以需要找到FeignClientsRegistrar类的registerBeanDefinitions回掉方法。
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
registerDefaultConfiguration(metadata, registry);
registerFeignClients(metadata, registry);
}
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
LinkedHashSet candidateComponents = new LinkedHashSet<>();
Map attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class>[] clients = attrs == null ? null : (Class>[]) attrs.get("clients");
// 扫描当前目录下所有的@FeignClient类。
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// 遍历当前目录下所有的@FeignClient类生成的BeanDefinition
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 attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name, attributes.get("configuration"));
// 给当前目录下所有的@FeignClient类生成的BeanDefinition添加Feign动态代理后的代理Class
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
}
Spring Cloud Openfein的核心原理大致就在这里了,所以对以上做个总结:
看到这里,能力比较强的读者就能明白,Spring Cloud Openfeign作为中间层,向上实现规范和扩展接口,向下兼容组件本身。拿这里举例子,Spring Cloud Openfein使用Spring的扩展点@Import来解析@FeignClient注解,把@FeignClient注解元数据信息作为燃料,填充给Feign组件本身,Feign组件获取到燃料后动态代理带有@FeignClient注解的类。在后续的依赖注入和方法调用都是走代理对象,此时我们只需要把Feign组件中动态代理的流程和代理回掉弄明白即可。
那么,接着看到registerFeignClient方法的实现。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
Map 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());
// 这里是Spring的一个扩展点,最终创建Bean对象的时候,会回掉此处。
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();
});
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, qualifiers);
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
这里很简单,拿到了@FeginClient注解的元数据信息,创建了FeignClientFactortyBean对象,把数据全部注入到FeignClientFactortyBean对象中,并且这里使用了Spring的instanceSupplier扩展点,也即最终创建Bean对象的时候,会把创建的权利交给开发者自定义。所以最终创建的对象就是FeignClientFactortyBean对象的getObject方法。
public class FeignClientFactoryBean
implements FactoryBean
这里非常的简单,创建出Feign.Builder对象,此对象用于构建出Feign对象,而Feign对象作为请求的整个上下文。而我们需要把之前@FeignClient注解的燃料交给Feign,还有Spring boot帮我们自动配置的燃料也一同交给Feign,所以这里feign()方法从Spring的工厂中拿到Spring boot自动配置的组件。下面图来自Spring Cloud Openfeign中spring.factory自动配置文件自动配置的类。所以再再再再再一次证明了Spring Cloud Openfeign是承上启下。向上对接规范和扩展接口,向下对接组件本身
接下来,我们直接看向重点,如何动态代理和方法拦截的即可。
public abstract class Feign {
// build方法构建出ReflectiveFeign类
// 然后调用ReflectiveFeign类的newInstance方法
public T target(Target target) {
return build().newInstance(target);
}
public Feign build() {
super.enrich();
SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
responseInterceptor, logger, logLevel, dismiss404, closeAfterDecode,
propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
return new ReflectiveFeign(handlersByName, invocationHandlerFactory, queryMapEncoder);
}
}
这里把所有Feign中组件准备好,构建出ReflectiveFeign类,然后调用newInstance方法。
@Override
public T newInstance(Target target) {
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)));
}
}
// 创建出JDK动态代理中的拦截器对象
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;
}
这里可以看到创建出JDK动态代理中所需的拦截器对象InvocationHandler,然后调用Proxy.newProxyInstance方法创建出代理类的实例对象。所以接下来看到如何拦截并且处理的就完美的闭环了。
@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);
}
@Override
public Object invoke(Object[] argv) throws Throwable {
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;
}
}
}
// 执行,也即调用某个Http客户端的http请求
Object executeAndDecode(RequestTemplate template, Options options) throws Throwable {
Request request = targetRequest(template);
…………
response = client.execute(request, options);
…………
}
最终回掉到SynchronousMethodHandler的invoke方法中,可以很清楚的看到内部就开始使用某个Http客户端,发送Http请求了。
能够理解,框架的定位,框架向上兼容什么,向下兼容什么,这对于理解源码是非常重要的一个部分。
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!