” Spring 到底是春天的来临万物复苏,还是春转夏的干燥又炎热呢?” Spring的来临让JavaEE走向了另一个高度。便捷的开发,完美的生态。物极必反,学习Spring的成本越来越低,导致Java程序员越来越密集,越来越廉价…… 那么又有多少 Springer 耐下性子去研究Spring生态的架构和底层实现的细节呢??
版本信息如下:
Spring:5.3.23Spring Boot:2.6.13Spring Cloud:3.1.5Spring Cloud Openfeign:3.1.5Feign: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 {@Overridepublic 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 basePackages = getBasePackages(metadata);for (String basePackage : basePackages) {candidateComponents.addAll(scanner.findCandidateComponents(basePackage));}}else {for (Class clazz : clients) {candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));}}// 遍历当前目录下所有的@FeignClient类生成的BeanDefinitionfor (BeanDefinition candidateComponent : candidateComponents) {if (candidateComponent instanceof AnnotatedBeanDefinition) {// verify annotated class is an interfaceAnnotatedBeanDefinition 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动态代理后的代理ClassregisterFeignClient(registry, annotationMetadata, attributes);}}}}
Spring Cloud Openfein的核心原理大致就在这里了,所以对以上做个总结:
- 使用@EnableFeignClients注解,Spring底层会解析@Import注解,最终会回掉registerBeanDefinitions方法,此方法中调用registerFeignClients
- registerFeignClients方法中会去扫描当前路径下所有的类,如果类上带有@FeignClient注解就会生成BeanDefition。
- 遍历所有带有@FeignClient注解的类,反射拿到@FeignClient注解的元数据信息,然后调用registerFeignClient方法
- registerFeignClient方法会解析带有@FeignClient注解元数据信息当作燃料往Feign里面填充,然后Feign会去动态代理创建出带有@FeignClient注解的类的动态代理类。
看到这里,能力比较强的读者就能明白,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 FeignClientFactoryBeanimplements 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方法。
@Overridepublic 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@Overridepublic 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);}@Overridepublic 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请求了。
总结:
能够理解,框架的定位,框架向上兼容什么,向下兼容什么,这对于理解源码是非常重要的一个部分。
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!