目录
@EnableFeignClients" >@EnableFeignClients
@Import()@Import()" >@Import()@Import()
openfeign中作用" >openfeign中作用
registerDefaultConfiguration(metadata, registry)" >registerDefaultConfiguration(metadata, registry)
registerFeignClients(metadata, registry)" >registerFeignClients(metadata, registry)
首页 Java java教程 Spring Cloud源码分析:第一篇

Spring Cloud源码分析:第一篇

Aug 15, 2023 pm 04:04 PM
spring cloud

莫要嘲笑看源码的朋友,现在面试仅仅是八股文是靠不住了,更多是问项目问题以及源码及问题。我也是逼不得已,不然谁想造轮子,很累且枯燥!

个人觉得看源码的前提是得会用,用熟了可以猜猜别人是怎么实现的,如果有相关官方文档那就在看看官方文档。

不过,可惜的是很多官方文档写得很烂,让你看了会那种云里雾里的。

最近我在研究openfeign源码的时候,发现在源码中有个关键注解:@Importopenfeign源码的时候,发现在源码中有个关键注解:@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配置类、ImportSelectImportBeanDefinitionRegistrar的实现类。

从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配置类、ImportSelectImportBeanDefinitionRegistrar的实现类。🎜🎜从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类,我们再来看看他的类关系图:

Spring Cloud源码分析:第一篇

从类关系图来看,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中内容如下:

Spring Cloud源码分析:第一篇

但是这里我们只关注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注解并符合条件装配的接口;
  • 最后将FeignClientConfigurationBeanDefinitionRegistry中注册一下,再对FeignClient做真正的注册操作。
  • 总结

    openfeign源码中的@Import注解在这里的作用就是将扫描到带有FeignClient

    最后将FeignClientConfigurationBeanDefinitionRegistry中注册一下,再对FeignClient做真正的注册操作。

    总结

    openfeign源码中的@Import注解在这里的作用就是将扫描到带有FeignClient注解的全部接口类以bean的形式注册到spring IOC容器中。

    🎜再来强调一下@Import注解使用方式:🎜
    • @Import一个普通类 spring会将该类加载到spring容器中@Import一个普通类 spring会将该类加载到spring容器中
    • @Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry的注册器,能手工往beanDefinitionMap中注册 beanDefinition
    • @Import一个类 该类实现了ImportSelector 重写selectImports
    @Import一个类,该类实现了ImportBeanDefinitionRegistrar接口,在重写的registerBeanDefinitions方法里面,能拿到BeanDefinitionRegistry的注册器,能手工往beanDefinitionMap中注册 beanDefinition

    @Import一个类 该类实现了ImportSelector 重写selectImports方法该方法返回了String[]数组的对象,数组里面的类都会注入到spring容器当中。

    好了,今天就分享这么多。这个openfeign里还有很多很有意思的地方,我们下次再分享吧!🎜🎜🎜🎜🎜🎜🎜

    以上是Spring Cloud源码分析:第一篇的详细内容。更多信息请关注PHP中文网其他相关文章!

    本站声明
    本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

    热AI工具

    Undresser.AI Undress

    Undresser.AI Undress

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

    AI Clothes Remover

    AI Clothes Remover

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

    Undress AI Tool

    Undress AI Tool

    免费脱衣服图片

    Clothoff.io

    Clothoff.io

    AI脱衣机

    AI Hentai Generator

    AI Hentai Generator

    免费生成ai无尽的。

    热门文章

    R.E.P.O.能量晶体解释及其做什么(黄色晶体)
    3 周前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.最佳图形设置
    3 周前 By 尊渡假赌尊渡假赌尊渡假赌
    R.E.P.O.如果您听不到任何人,如何修复音频
    3 周前 By 尊渡假赌尊渡假赌尊渡假赌
    WWE 2K25:如何解锁Myrise中的所有内容
    4 周前 By 尊渡假赌尊渡假赌尊渡假赌

    热工具

    记事本++7.3.1

    记事本++7.3.1

    好用且免费的代码编辑器

    SublimeText3汉化版

    SublimeText3汉化版

    中文版,非常好用

    禅工作室 13.0.1

    禅工作室 13.0.1

    功能强大的PHP集成开发环境

    Dreamweaver CS6

    Dreamweaver CS6

    视觉化网页开发工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神级代码编辑软件(SublimeText3)

    巧用Spring Cloud解决微服务架构下的负载均衡问题 巧用Spring Cloud解决微服务架构下的负载均衡问题 Jun 23, 2023 pm 01:40 PM

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

    Spring Cloud源码分析:第一篇 Spring Cloud源码分析:第一篇 Aug 15, 2023 pm 04:04 PM

    个人觉得看源码的前提是得会用,用熟了可以猜猜别人是怎么实现的,如果有相关官方文档那就在看看官方文档。

    面向服务的Spring Cloud微服务开发 面向服务的Spring Cloud微服务开发 Jun 23, 2023 pm 12:30 PM

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

    如何使用Java开发一个基于Spring Cloud Kubernetes的容器编排应用 如何使用Java开发一个基于Spring Cloud Kubernetes的容器编排应用 Sep 20, 2023 am 11:15 AM

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

    Spring Cloud微服务架构部署与运维 Spring Cloud微服务架构部署与运维 Jun 23, 2023 am 08:19 AM

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

    Java语言中的Spring Cloud框架介绍 Java语言中的Spring Cloud框架介绍 Jun 09, 2023 pm 10:54 PM

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

    实现分布式锁的Spring Cloud微服务实践 实现分布式锁的Spring Cloud微服务实践 Jun 22, 2023 pm 11:28 PM

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

    Spring Cloud微服务与组件化的结合 Spring Cloud微服务与组件化的结合 Jun 23, 2023 am 10:21 AM

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

    See all articles