Ne vous moquez pas des amis qui lisent le code source. De nos jours, les entretiens ne portent que sur des essais en huit parties et ne sont pas fiables. Ils consistent davantage à poser des questions sur le projet, le code source et les questions. Je n'ai pas d'autre choix que de fabriquer des roues, sinon ce serait très fatiguant et ennuyeux !
Personnellement, je pense que la condition préalable pour lire le code source est de pouvoir l'utiliser. Une fois que vous le connaissez, vous pouvez deviner comment les autres l'implémentent. S'il existe des documents officiels pertinents, alors lisez les documents officiels.
Cependant, il est dommage que de nombreux documents officiels soient mal rédigés, ce qui vous laisse perplexe après les avoir lus pendant un moment.
Récemment, j'étudie 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);">openfeign code source À ce moment-là, j'ai trouvé une annotation clé dans le code source : @Importer
. openfeign
源码的时候,发现在源码中有个关键注解:@Import
。
项目启动类:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @EnableFeignClients(basePackages = {"com.tian.feign"}) @SpringBootApplication() public class MqApplication { public static void main(String[] args) { SpringApplication.run(MqApplication.class, args); } }
然后,就是我们的feignclient
接口:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @FeignClient(contextId = "userFeignClient", value = "charge-user-service") public interface UserFeignClient { /** * 邀请成功增加收益 * * @param invitedDto 邀请增加收益 * @return 邀请成功 */ @PostMapping("/user/invited/register") CommonResult<Boolean> invitedRegister(@RequestBody InvitedDto invitedDto); }
使用案例:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @RestController @RequestMapping("/user") public class UserController { @Resource UserFeignClient userFeignClient; @PostMapping("/invited") public CommonResult invitedRegister(){ //省略不想搞的代码 return userFeignClient.invitedRegister(invitedDto); } }
从上面的代码中,我们可以看出openfeign
关键代码有:
@EnableFeignClients(basePackages = {"com.tian.feign"})
@FeignClient(contextId = "userFeignClient", value = "charge-user-service")
Classe de démarrage du projet : 🎜
userFeignClient.invitedRegister(invitedDto);
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {}; }🎜Ensuite, voici notrefeignclient
Interface : 🎜@Import(FeignClientsRegistrar.class)🎜Cas d'utilisation : 🎜/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }🎜À partir du code ci-dessus, nous pouvons voir queopenfeign
Les codes clés sont : 🎜@EnableFeignClients( basePackages = {"com.tian.feign"})
🎜
@FeignClient(contextId = "userFeignClient", value = " charge-user-service")
🎜
userFeignClient.invitedRegister(invitedDto);
🎜@
EnableFeignClients
@EnableFeignClients
这个注解在启动类上,我们肯定要重点关注。小技巧:凡是以
@Enable
开头的各种注解基本上都是开启xxxx
。比如:@EnableFeignClients
表示开启feign客户端。我们进入
@EnableFeignClients
中/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] defaultConfiguration() default {}; Class<?>[] clients() default {}; }我们通常只需要关心属性
basePackages
,表示我们需要扫描的包目录。如果既没有指定
basePackages
,也没有指定basePackageClasses
,则采用启动类所在的目录作为包扫描路径。默认是这种情况。本文重点来了,在这个注解
@EnableFeignClients
上有个注解@Import(FeignClientsRegistrar.class)
,这里到底是有什么作用?
@Import()
@Import()
@Import()
注解是在spring 3.0版本中引入的,字面意义就是导入.
@Import
注解的全类名是org.springframework.context.annotation.Import
。其只有一个默认的value属性,该属性类型为Class6b3d0130bba23ae47fe2b8e8cddf0195[]
,表示可以传入一个或多个Class对象。通过注释可以看出,该注解有如下作用:
可以导入一个或多个组件类(通常是@Configuration配置类)该注解的功能与Spring XML中的
de3db5173ba162146856f74f89e5f81e
元素相同。可以导入@Configuration
配置类、ImportSelect
和ImportBeanDefinitionRegistrar
的实现类。从spring 4.2版本开始,还可以引用常规组件类(普通类),该功能类似于
AnnotationConfigApplicationContext.register()
方法。该注解可以在类中声明,也可以在元注解中声明。如果需要导入XML或其他非
@Configuration
定义的资源,可以使用@ImportResource
🎜
@Import()
l'annotation a été introduite dans la version Spring 3.0, et sa signification littérale est import.Le nom complet de la classe de l'annotation @Import
estorg.springframework.context.annotation.Import
. Il n'a qu'un seul attribut de valeur par défaut, qui est de typeClass< ;?>[]
, indiquant qu'un ou plusieurs objets Class peuvent être transmis. 🎜🎜Comme le montre l'annotation, l'annotation a les fonctions suivantes : 🎜🎜Vous pouvez importer une ou plusieurs classes de composants (généralement la classe de configuration @Configuration). La fonction de cette annotation est la même que celle dede3db5173ba162146856f74f89e5f81e
les éléments sont les mêmes . Vous pouvez importer@Configuration
Classe de configuration, ImportSelect etClasse d'implémentation ImportBeanDefinitionRegistrar
. 🎜🎜À partir du printemps 4.2, vous pouvez également référencer des classes de composants régulières (classes ordinaires). Cette fonction est similaire àMéthode AnnotationConfigApplicationContext.register()
. 🎜🎜Cette annotation peut être déclarée dans la classe ou dans la méta-annotation. Si vous devez importer du XML ou d'autres fichiers non-, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96 );">@Configuration Pour les ressources définies, vous pouvez utiliser@ImportResource
annotation . 🎜🎜Il existe généralement trois façons de l'utiliser : 🎜
@Import
一个普通类 spring会将该类加载到spring容器中@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册beanDefinition
@Import
一个类 该类实现了ImportSelector
重写selectImports
方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中。下面我们来聊聊
@Import
在openfeign的这里是起到什么作用。
openfeign
中作用回答上面的代码里
@Import(FeignClientsRegistrar.class)这里导入的是
FeignClientsRegistrar
类,我们再来看看他的类关系图:从类关系图来看,
FeignClientsRegistrar
实现了ImportBeanDefinitionRegistrar
接口。再结合@Import
的三种使用方式中的第二种方式,能手工往beanDefinitionMap
中注册beanDefinition
。/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:<a href="http://woaijava.cc/">博客地址</a> */ @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { registerDefaultConfiguration(metadata, registry); registerFeignClients(metadata, registry); }这个方法
registerBeanDefinitions()
是feign的核心入口方法,其中会做两件事:注册默认的配置和注册所有的FeignClient。
registerDefaultConfiguration(metadata, registry)
这个方法是负责注册
OpenFeign
的默认配置 ,逻辑相对简单:private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取@EnableFeignClients的全部属性 //@EnableFeignClients(basePackages = {"com.tian.feign"}) //这里的basePackages就是我们指定的熟悉 Map<String, Object> defaultAttrs = metadata .getAnnotationAttributes(EnableFeignClients.class.getName(), true); if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) { String name; if (metadata.hasEnclosingClass()) { name = "default." + metadata.getEnclosingClassName(); } else { name = "default." + metadata.getClassName(); } registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration")); } }
defaultAttrs
中内容如下:但是这里我们只关注defaultConfiguration,我们并有对其进行设置,所以我们可以忽略他。重点是下面这个方法。
registerFeignClients(metadata, registry)
这里就是项目启动时和openfeign相关的核心代码,这也是
@EnableFeignClients
和@FeignClient
两个注解关联起来的地方。我们进入源码中:
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); final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients"); if (clients == null || clients.length == 0) { scanner.addIncludeFilter(annotationTypeFilter); basePackages = getBasePackages(metadata); } else { 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); 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); } } } }代码一行一行看是不是觉得很累,我给你总结好了。
上面的方法分为以下七个步骤:
Obtenez d'abord @EnableFeignClients
annoté Tous attributs, principalement pour obtenir le chemin du package d'analyse (basePackages); <code style='font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);'>@EnableFeignClients
注解的所有属性,主要为了拿到扫描包路径(basePackages
);因为一般不会在 @EnableFeignClients
注解中配置clients属性,所以会进入到clients属性为空时的逻辑;然后通过 getScanner()
方法获取扫描器:ClassPathScanningCandidateComponentProvider
,并将上下文AnnotationConfigServletWebServerApplicationContext
作为扫描器的ResourceLoader
;接着给扫描器 ClassPathScanningCandidateComponentProvider
添加一个注解过滤器(AnnotationTypeFilter),只过滤出包含@FeignClient
注解的BeanDefinition
;- 🎜 car il n'est généralement pas utilisé dans
再通过 getBasePackages(metadata)
方法获取@EnableFeingClients
@EnableFeignClients
configurez l'attribut clients dans l'annotation, donc il entrera dans la logique lorsque l'attribut clients est vide ; 🎜🎜🎜🎜 Passez ensuitegetScanner()
obtient le scanner :ClassPathScanningCandidateComponentProvider
et changez le contexteAnnotationConfigServletWebServerApplicationContext
En tant que scannerResourceLoader
;🎜🎜🎜🎜Donnez ensuite au scannerClassPathScanningCandidateComponentProvider
Ajoutez un filtre d'annotation (AnnotationTypeFilter) pour filtrer uniquement les fichiers contenant@FeignClient
annotationBeanDefinition
; 🎜🎜🎜🎜 puis passezgetBasePackages(metadata)
obtientLe chemin d'analyse du package ou la classe d'analyse spécifiée dans l'annotation @EnableFeingClients
; s'il n'est pas obtenu, le chemin d'accès au package où se trouve la classe de démarrage sera analysé par défaut ; 🎜🎜Ensuite, entrez la logique de base : passez scanner.findCandidateComponents( basePackage)
analyse toutes les annotations du chemin du package@FeignClient
Interface d'assemblage annotée et qualifiée;scanner.findCandidateComponents(basePackage)
方法从包路径下扫描出所有标注了@FeignClient
注解并符合条件装配的接口;
FeignClientConfiguration
在BeanDefinitionRegistry
中注册一下,再对FeignClient
做真正的注册操作。在openfeign
源码中的@Import注解在这里的作用就是将扫描到带有FeignClient
FeignClientConfiguration
dans Enregistrez-le dans BeanDefinitionRegistry
, puis FeignClient
effectue la véritable opération d'enregistrement. Dansopenfeign
L'annotation @Import dans le code source est utilisée ici pour analyser les fichiers avec FeignClient
Toutes les classes d'interface annotées sont enregistrées sous forme de beans dans le conteneur IOC à ressort.
@Import
Un ressort de classe commun Cette classe sera chargée dans le conteneur Spring@Import
一个普通类 spring会将该类加载到spring容器中@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册 beanDefinition
@Import
一个类 该类实现了ImportSelector
重写selectImports
@Import
Une classe qui implémente ImportBeanDefinitionRegistrar code>, dans l'interface réécrite <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba (27, 31 , 35, 0.05);famille de polices : " operator mono consolas monaco menlo monospace de mot break-all rgb>registerBeanDefinitionsIn la méthode, vous pouvez obtenir <code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27 , 31, 35, 0,05);famille de polices : " operator mono consolas monaco menlo monospace de mot break-all rgb>BeanDefinitionRegistry
register, vous pouvez définir manuellement beanDefinitionMap
RegisterbeanDefinition
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!