发布文章网站源码分享java(文章转发源码)

大家好,关于发布文章网站源码分享java很多朋友都还不太明白,今天小编就来为大家分享关于文章转发源码的知识,希望对各位有所帮助!

前面时候我发布两篇关于nacos源码的文章,一篇是聊一聊nacos是如何进行服务注册的,另一篇是一文带你看懂nacos是如何整合springcloud–注册中心篇。今天就继续接着剖析SpringCloud中OpenFeign组件的源码,来聊一聊OpenFeign是如何工作的。

一、@EnableFeignClinets作用源码剖析

我们都知道,要使用feign,必须要使用@EnableFeignClinets来激活,这个注解其实就是整个feign的入口,接下来我们着重分析一下这个注解干了什么事。

@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.TYPE)\n@Documented\n@Import(FeignClientsRegistrar.class)\npublic@interfaceEnableFeignClients{\n}

这个注解通过@Import注解导入一个配置类FeignClientsRegistrar.class,FeignClientsRegistrar实现了ImportBeanDefinitionRegistrar接口,所以SpringBoot在启动的时候,会去调用FeignClientsRegistrar类中的registerBeanDefinitions来动态往spring容器中注入bean。如果有不懂小伙伴可以看一下我以前写过的一篇文章看Spring源码不得不会的@Enable模块驱动实现原理讲解,这里详细讲解了@Import注解的作用。

接下来看一下registerBeanDefinitions的实现

@Override\npublicvoidregisterBeanDefinitions(AnnotationMetadatametadata,\nBeanDefinitionRegistryregistry)\n//这个方式是注入一些配置,就是对EnableFeignClients注解属性的解析\nregisterDefaultConfiguration(metadata,registry);\n//这个方法是扫秒加了@FeignClient注解\nregisterFeignClients(metadata,registry);\n}

这里我们着重分析registerFeignClients,看一看是如何扫描@FeignClient注解的,然后扫描到之后又做了什么。

publicvoidregisterFeignClients(AnnotationMetadatametadata,\nBeanDefinitionRegistryregistry){\nClassPathScanningCandidateComponentProviderscanner=getScanner();\nscanner.setResourceLoader(this.resourceLoader);\nSet<String>basePackages;\nMap<String,Object>attrs=metadata\n.getAnnotationAttributes(EnableFeignClients.class.getName());\nAnnotationTypeFilterannotationTypeFilter=newAnnotationTypeFilter(\nFeignClient.class);\nfinalClass<?>[]clients=attrs==null?null\n:(Class<?>[])attrs.get(&34;);\nif(clients==null||clients.length==0){\nscanner.addIncludeFilter(annotationTypeFilter);\nbasePackages=getBasePackages(metadata);\n}\nelse{\nfinalSet<String>clientClasses=newHashSet<>();\nbasePackages=newHashSet<>();\nfor(Class<?>clazz:clients){\nbasePackages.add(ClassUtils.getPackageName(clazz));\nclientClasses.add(clazz.getCanonicalName());\n}\nAbstractClassTestingTypeFilterfilter=newAbstractClassTestingTypeFilter(){\n@Override\nprotectedbooleanmatch(ClassMetadatametadata){\nStringcleaned=metadata.getClassName().replaceAll(&34;,&34;);\nreturnclientClasses.contains(cleaned);\n}\n};\nscanner.addIncludeFilter(\nnewAllTypeFilter(Arrays.asList(filter,annotationTypeFilter)));\n}\nfor(StringbasePackage:basePackages){\nSet<BeanDefinition>candidateComponents=scanner\n.findCandidateComponents(basePackage);\nfor(BeanDefinitioncandidateComponent:candidateComponents){\nif(candidateComponentinstanceofAnnotatedBeanDefinition){\n//verifyannotatedclassisaninterface\nAnnotatedBeanDefinitionbeanDefinition=(AnnotatedBeanDefinition)candidateComponent;\nAnnotationMetadataannotationMetadata=beanDefinition.getMetadata();\nAssert.isTrue(annotationMetadata.isInterface(),\n&34;);\nMap<String,Object>attributes=annotationMetadata\n.getAnnotationAttributes(\nFeignClient.class.getCanonicalName());\nStringname=getClientName(attributes);\nregisterClientConfiguration(registry,name,\nattributes.get(&34;));\nregisterFeignClient(registry,annotationMetadata,attributes);\n}\n}\n}\n}

这段代码我分析一下,先获取到了一个ClassPathScanningCandidateComponentProvider这个对象,这个对象是按照一定的规则来扫描指定目录下的类的,符合这个规则的每个类,会生成一个BeanDefinition,不知道BeanDefinition的小伙伴可以看我之前写的关于bean生命周期的文章Springbean到底是如何创建的?(上)和Springbean到底是如何创建的?(下),里面有过对BeanDefinition的描述。

获取到ClassPathScanningCandidateComponentProvider对象,配置这个对象,指定这个对象需要扫描出来标有@FeignClient注解的类;随后解析EnableFeignClients注解,获取内部的属性,获取到指定的需要扫描包路径下,如果没有指定的,那么就默认是当前注解所在类的所在目录及子目录。

然后就遍历每个目录,找到每个标有@FeignClient注解的类,对每个类就生成一个BeanDefinition,可以把BeanDefinition看成对每个标有@FeignClient注解的类信息的封装。

拿到一堆BeanDefinition之后,会遍历BeanDefinition,然后调用registerClientConfiguration和registerFeignClient方法。

接下来我分别剖析一下这两个方法的作用

registerClientConfiguration:

privatevoidregisterClientConfiguration(BeanDefinitionRegistryregistry,Objectname,\nObjectconfiguration){\nBeanDefinitionBuilderbuilder=BeanDefinitionBuilder\n.genericBeanDefinition(FeignClientSpecification.class);\nbuilder.addConstructorArgValue(name);\nbuilder.addConstructorArgValue(configuration);\nregistry.registerBeanDefinition(\nname+&34;+FeignClientSpecification.class.getSimpleName(),\nbuilder.getBeanDefinition());\n}

这里的作用就是拿出你再@FeignClient指定的配置类,也就是configuration属性,然后构建一个beanclass为FeignClientSpecification,传入配置。这个类的最主要作用就是将每个Feign的客户端的配置类封装成一个FeignClientSpecification的BeanDefinition,注册到spring容器中。记住这个FeignClientSpecification,后面会有用。

registerFeignClient:

privatevoidregisterFeignClient(BeanDefinitionRegistryregistry,\nAnnotationMetadataannotationMetadata,Map<String,Object>attributes){\nStringclassName=annotationMetadata.getClassName();\nBeanDefinitionBuilderdefinition=BeanDefinitionBuilder\n.genericBeanDefinition(FeignClientFactoryBean.class);\nvalidate(attributes);\ndefinition.addPropertyValue(&34;,getUrl(attributes));\ndefinition.addPropertyValue(&34;,getPath(attributes));\nStringname=getName(attributes);\ndefinition.addPropertyValue(&34;,name);\nStringcontextId=getContextId(attributes);\ndefinition.addPropertyValue(&34;,contextId);\ndefinition.addPropertyValue(&34;,className);\ndefinition.addPropertyValue(&34;,attributes.get(&34;));\ndefinition.addPropertyValue(&34;,attributes.get(&34;));\ndefinition.addPropertyValue(&34;,attributes.get(&34;));\ndefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);\nStringalias=contextId+&34;;\nAbstractBeanDefinitionbeanDefinition=definition.getBeanDefinition();\nbooleanprimary=(Boolean)attributes.get(&34;);//hasadefault,won&34;feign&34;feign.client.name&34;default.&34;prototype&34;prototype&34;feign.hystrix.enabled&34;feign.hystrix.enabled&34;http&34;http://&34;http&34;http://&34;http&34;http://&34;NoFeignClientforloadBalancingdefined.Didyouforgettoincludespring-cloud-starter-netflix-ribbon?&34;unchecked”)\n@Override\npublic<T>TnewInstance(Target<T>target){\nMap<String,MethodHandler>nameToHandler=targetToHandlersByName.apply(target);\nMap<Method,MethodHandler>methodToHandler=newLinkedHashMap<Method,MethodHandler>();\nList<DefaultMethodHandler>defaultMethodHandlers=newLinkedList<DefaultMethodHandler>();\nfor(Methodmethod:target.type().getMethods()){\nif(method.getDeclaringClass()==Object.class){\ncontinue;\n}elseif(Util.isDefault(method)){\nDefaultMethodHandlerhandler=newDefaultMethodHandler(method);\ndefaultMethodHandlers.add(handler);\nmethodToHandler.put(method,handler);\n}else{\nmethodToHandler.put(method,nameToHandler.get(Feign.configKey(target.type(),method)));\n}\n}\nInvocationHandlerhandler=factory.create(target,methodToHandler);\nTproxy=(T)Proxy.newProxyInstance(target.type().getClassLoader(),\nnewClass<?>[]{target.type()},handler);\nfor(DefaultMethodHandlerdefaultMethodHandler:defaultMethodHandlers){\ndefaultMethodHandler.bindTo(proxy);\n}\nreturnproxy;\n}\n

这个方法我来解释一下是来干什么的,其实很简单,通过Target拿到接口的类型,然后获取到所有的方法,遍历每个方法,处理之后放入methodToHandler中,然后通过InvocationHandlerFactory的create方法,传入methodToHandler和Target,获取到一个InvocationHandler,之后通过jdk的动态代理,生成一个代理对象,然后返回回去。InvocationHandler默认是ReflectiveFeign.FeignInvocationHandler,这里我就不再继续翻下去了。\n走到这里,我们终于看到了Feign客户端动态代理的生成,整个构造过程还是很复杂的。这里我总结一下代理对象生成的过程,每个Feign客户端都有对应的一个spring容器,用来解析配置类,根据配置从容器获取到一个Feign.Builder,然后再从容器中获取每个组件,填充到Feign.Builder中,最后通过Feign.Builder的build方法来构造动态代理,构造的过程其实是属于feign包底下的。

三、总结

本文主要是讲述了,在SpringCloud环境下,OpenFeign对于Feign客户端动态代理的的构造过程。最开始讲解了@EnableFeignClinets注解的作用开始,随后剖析了FeignAutoConfiguration和FeignClientsConfiguration配置类,同时提到了Feign对每个客户端都进行了配置的隔离,最后通过剖析FeignClientFactoryBean的getObject方法,来一步一步屡清楚动态代理的构建过程。

至于OpenFeign是如何跟ribbon整合的,以及其他SpringCloud组件的原理,我会单独再写几篇文章来剖析。

最后画一张图,来总结一下

以上就是本篇文章的全部内容,如果你有什么不懂或者想要交流的地方,欢迎关注我的个人的微信公众号程序员黑哥联系我,我们下篇文章再见。

如果觉得这篇文章对你有所帮助,还请帮忙点赞、在看、转发一下,码字不易,非常感谢!

好了,本文到此结束,如果可以帮助到大家,还望关注本站哦!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平