Spring Cloud源码分析:第一篇
莫要嘲笑看源码的朋友,现在面试仅仅是八股文是靠不住了,更多是问项目问题以及源码及问题。我也是逼不得已,不然谁想造轮子,很累且枯燥!
个人觉得看源码的前提是得会用,用熟了可以猜猜别人是怎么实现的,如果有相关官方文档那就在看看官方文档。
不过,可惜的是很多官方文档写得很烂,让你看了会那种云里雾里的。
最近我在研究openfeign
源码的时候,发现在源码中有个关键注解:@Import
。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")
项目启动类:🎜
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 {}; }登录后复制登录后复制feignclient
接口:🎜🎜使用案例:🎜@Import(FeignClientsRegistrar.class)登录后复制登录后复制🎜从上面的代码中,我们可以看出/** * @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); }登录后复制登录后复制openfeign
关键代码有:🎜
@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属性,该属性类型为Class<?>[]
,表示可以传入一个或多个Class对象。通过注释可以看出,该注解有如下作用:
可以导入一个或多个组件类(通常是@Configuration配置类)该注解的功能与Spring XML中的
<import/>
元素相同。可以导入@Configuration
配置类、ImportSelect
和ImportBeanDefinitionRegistrar
的实现类。从spring 4.2版本开始,还可以引用常规组件类(普通类),该功能类似于
AnnotationConfigApplicationContext.register()
方法。该注解可以在类中声明,也可以在元注解中声明。如果需要导入XML或其他非
@Configuration
定义的资源,可以使用@ImportResource
🎜
@Import()
注解是在spring 3.0版本中引入的,字面意义就是导入.@Import
注解的全类名是org.springframework.context.annotation.Import
。其只有一个默认的value属性,该属性类型为Class<?>[]
,表示可以传入一个或多个Class对象。🎜🎜通过注释可以看出,该注解有如下作用:🎜🎜可以导入一个或多个组件类(通常是@Configuration配置类)该注解的功能与Spring XML中的<import/>
元素相同。可以导入@Configuration
配置类、ImportSelect
和ImportBeanDefinitionRegistrar
的实现类。🎜🎜从spring 4.2版本开始,还可以引用常规组件类(普通类),该功能类似于AnnotationConfigApplicationContext.register()
方法。🎜🎜该注解可以在类中声明,也可以在元注解中声明。如果需要导入XML或其他非@Configuration
定义的资源,可以使用@ImportResource
注释。🎜🎜通常有三种使用方式:🎜
@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); } } } }登录后复制代码一行一行看是不是觉得很累,我给你总结好了。
上面的方法分为以下七个步骤:
首先获取 @EnableFeignClients
注解的所有属性,主要为了拿到扫描包路径(basePackages
);@EnableFeignClients
注解的所有属性,主要为了拿到扫描包路径(basePackages
);因为一般不会在 @EnableFeignClients
注解中配置clients属性,所以会进入到clients属性为空时的逻辑;然后通过 getScanner()
方法获取扫描器:ClassPathScanningCandidateComponentProvider
,并将上下文AnnotationConfigServletWebServerApplicationContext
作为扫描器的ResourceLoader
;接着给扫描器 ClassPathScanningCandidateComponentProvider
添加一个注解过滤器(AnnotationTypeFilter),只过滤出包含@FeignClient
注解的BeanDefinition
;- 🎜因为一般不会在
再通过 getBasePackages(metadata)
方法获取@EnableFeingClients
@EnableFeignClients
注解中配置clients属性,所以会进入到clients属性为空时的逻辑;🎜🎜🎜🎜然后通过getScanner()
方法获取扫描器:ClassPathScanningCandidateComponentProvider
,并将上下文AnnotationConfigServletWebServerApplicationContext
作为扫描器的ResourceLoader
;🎜🎜🎜🎜接着给扫描器ClassPathScanningCandidateComponentProvider
添加一个注解过滤器(AnnotationTypeFilter),只过滤出包含@FeignClient
注解的BeanDefinition
;🎜🎜🎜🎜再通过getBasePackages(metadata)
方法获取@EnableFeingClients
注解中的指定的包扫描路径 或 扫描类;如果没有获取到,则默认扫描启动类所在的包路径;🎜🎜然后进入到核心逻辑:通过 scanner.findCandidateComponents(basePackage)
方法从包路径下扫描出所有标注了@FeignClient
注解并符合条件装配的接口;scanner.findCandidateComponents(basePackage)
方法从包路径下扫描出所有标注了@FeignClient
注解并符合条件装配的接口;
FeignClientConfiguration
在BeanDefinitionRegistry
中注册一下,再对FeignClient
做真正的注册操作。总结
在openfeign
源码中的@Import注解在这里的作用就是将扫描到带有FeignClient
FeignClientConfiguration
在BeanDefinitionRegistry
中注册一下,再对FeignClient
做真正的注册操作。总结
在openfeign
源码中的@Import注解在这里的作用就是将扫描到带有FeignClient
注解的全部接口类以bean的形式注册到spring IOC容器中。
@Import
一个普通类 spring会将该类加载到spring容器中@Import
一个普通类 spring会将该类加载到spring容器中@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册beanDefinition
@Import
一个类 该类实现了ImportSelector
重写selectImports
@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry
的注册器,能手工往beanDefinitionMap
中注册 beanDefinition
以上是Spring Cloud源码分析:第一篇的详细内容。更多信息请关注PHP中文网其他相关文章!

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

随着企业应用的复杂度不断增加,越来越多的企业开始将应用拆分为多个微服务,通过微服务之间的协作完成整个业务流程。这种架构方式能够使应用更加稳定、扩展性更强,但同时也带来了一些新的问题,例如负载均衡,服务发现等。本文将介绍如何通过SpringCloud来解决微服务架构下的负载均衡问题。什么是负载均衡?负载均衡(LoadBalancing)是指在多个服务器、网

随着互联网的发展和科技的不断更新,传统的单体应用已经无法满足用户需求,微服务的概念应运而生。SpringCloud是一个由Pivotal公司推出的微服务开发工具包,它为开发人员提供了一种极为方便的方式来构建、部署和管理微服务架构应用。本文将对面向服务的SpringCloud微服务开发进行详细介绍,包括SpringCloud的概念与架构、微服务开发流程以

如何使用Java开发一个基于SpringCloudKubernetes的容器编排应用随着容器技术的发展和广泛应用,容器编排工具也成为了开发人员不可或缺的一部分。Kubernetes作为目前最流行的容器编排工具之一,已经成为了业界的标准。在此背景下,结合SpringCloud和Kubernetes,我们可以很方便地开发出基于容器编排的应用。本文将详细介绍

随着互联网的快速发展,企业级应用的复杂度日益增加。针对这种情况,微服务架构应运而生。它以模块化、独立部署、可扩展性高等特点,成为当今企业级应用开发的首选。作为一种优秀的微服务架构,SpringCloud在实际应用中展现出了极大的优势。本文将介绍SpringCloud微服务架构的部署与运维。一、部署SpringCloud微服务架构SpringCloud

Java语言中的SpringCloud框架介绍随着云计算和微服务的流行,SpringCloud框架成为了Java语言中构建云原生应用的首选框架之一。本文将介绍SpringCloud框架的概念和特点,以及如何使用SpringCloud构建微服务架构。SpringCloud简介SpringCloud框架是基于SpringBoot的微服务框架。它为

随着微服务架构的流行,越来越多的企业开发团队开始使用SpringCloud构建自己的微服务系统。在分布式环境下,实现分布式锁是一项重要的技术挑战。本文将介绍在SpringCloud框架下,如何实现分布式锁的微服务实践。首先,我们需要了解什么是分布式锁。分布式锁是一种用于保护共享资源的访问的技术,它可以保证在分布式环境下多个节点不会同时对同一资源进行修改或

随着互联网技术的不断发展,越来越多的企业都开始采用微服务架构来构建其系统。而SpringCloud则是在这一背景下迅速崛起的一款微服务框架。在此基础上,这篇文章将对SpringCloud微服务和组件化的结合进行探讨,分析其优势和实现方式。一、SpringCloud微服务的简介SpringCloud是SpringBoot项目的升级版,它提供了大量的工
