莫要嘲笑看源码的朋友,现在面试仅仅是八股文是靠不住了,更多是问项目问题以及源码及问题。我也是逼不得已,不然谁想造轮子,很累且枯燥!
个人觉得看源码的前提是得会用,用熟了可以猜猜别人是怎么实现的,如果有相关官方文档那就在看看官方文档。
不过,可惜的是很多官方文档写得很烂,让你看了会那种云里雾里的。
最近我在研究openfeign源码的时候,发现在源码中有个关键注解:@Import。
项目启动类:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:博客地址 */@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+篇干货文章:博客地址 */@FeignClient(contextId = "userFeignClient", value = "charge-user-service")public interface UserFeignClient { /** * 邀请成功增加收益 * * @param invitedDto 邀请增加收益 * @return 邀请成功 */ @PostMapping("/user/invited/register") CommonResult invitedRegister(@RequestBody InvitedDto invitedDto);}
登录后复制
使用案例:
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:博客地址 */@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);
@EnableFeignClients
@EnableFeignClients这个注解在启动类上,我们肯定要重点关注。
小技巧:凡是以@Enable开头的各种注解基本上都是开启xxxx。比如:@EnableFeignClients表示开启feign客户端。
我们进入@EnableFeignClients中
/** * @author tianwc 公众号:java后端技术全栈、面试专栏 * @version 1.0.0 * @date 2023年07月07日 16:47 * 在线刷题 1200+题和1000+篇干货文章:博客地址 */@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()注解是在spring 3.0版本中引入的,字面意义就是导入.
@Import注解的全类名是org.springframework.context.annotation.Import。其只有一个默认的value属性,该属性类型为Class>[],表示可以传入一个或多个Class对象。
通过注释可以看出,该注解有如下作用:
可以导入一个或多个组件类(通常是@Configuration配置类)该注解的功能与Spring XML中的元素相同。可以导入@Configuration配置类、ImportSelect和ImportBeanDefinitionRegistrar的实现类。
从spring 4.2版本开始,还可以引用常规组件类(普通类),该功能类似于AnnotationConfigApplicationContext.register()方法。
该注解可以在类中声明,也可以在元注解中声明。如果需要导入XML或其他非@Configuration定义的资源,可以使用@ImportResource注释。
通常有三种使用方式:
下面我们来聊聊@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+篇干货文章:博客地址 */@Overridepublic 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 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 basePackages; Map 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 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 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 attributes = annotationMetadata .getAnnotationAttributes( FeignClient.class.getCanonicalName()); String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration")); registerFeignClient(registry, annotationMetadata, attributes); } } }}
登录后复制
代码一行一行看是不是觉得很累,我给你总结好了。
上面的方法分为以下七个步骤:
总结
在openfeign源码中的@Import注解在这里的作用就是将扫描到带有FeignClient注解的全部接口类以bean的形式注册到spring IOC容器中。
再来强调一下@Import注解使用方式:
好了,今天就分享这么多。这个openfeign里还有很多很有意思的地方,我们下次再分享吧!
以上就是Spring Cloud源码分析:第一篇的详细内容,更多请关注【创想鸟】其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。
发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2624592.html