当前位置: 首页 > article >正文

SpringCloud源码:客户端分析(一)- SpringBootApplication注解类加载流程

d7ff2a7b73efb4479d3640f21159b9bb.jpeg

总结一句话

用@EnableDiscoveryClient注解客户端-启动类,配合@springbootapplication,完成两个步骤:

  • 自动读取spring-factories文件的全限定类名内容

  • 通过selectImport对这些类进行初始化

背景

spring.factories作用

在maven依赖:

spring-cloud-netflix-eureka-client/2.2.6.RELEASE/spring-cloud-netflix-eureka-client-2.2.6.RELEASE.jar!的 META-INF 目录下,有一个spring.factories文件:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration,\
org.springframework.cloud.netflix.eureka.reactive.EurekaReactiveDiscoveryClientConfiguration,\
org.springframework.cloud.netflix.eureka.loadbalancer.LoadBalancerEurekaAutoConfiguration


org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaConfigServerBootstrapConfiguration

@SpringBootApplication为一个组合注解,通过@EnableAutoConfiguration开启自动装配。

spring.factories文件位于JAR包的META-INF目录下。

当Spring Boot应用启动时,它会自动扫描所有的spring.factories文件,并根据里面的配置加载相应的类。

在上述例子中,当应用启动时,EurekaClientAutoConfiguration 类将会被自动注册为一个bean,并可以通过依赖注入在其他组件中使用。

@SpringBootApplication注解

@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}

@EnableAutoConfiguration

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}

AutoConfigurationImportSelector资源类

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
      ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {


    // ....
    protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
       List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
             getBeanClassLoader());
       Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
             + "are using a custom packaging, make sure that file is correct.");
       return configurations;
    }
}

分析:

  • getCandidateConfigurations:此处是自动导入spring.factories文件的全部实现类

我们从debug可以看到,已经读出了spring.factories的配置类,包括了EurekaClientAutoConfiguration和EurekaDiscoveryClientConfiguration 两个自动装配类。

76ba7229984bcc9d74452b2db24b6e4e.png

小结

EurekaClientAutoConfiguration和EurekaDiscoveryClientConfiguration在Spring Cloud Eureka中扮演着不同的角色,它们的主要区别在于功能和职责的不同。

自动化配置类
功能职责
EurekaClientAutoConfiguration配置EurekaClient确保了Eureka客户端能够正确地:
- 注册到Eureka服务端
- 周期性地发送心跳信息来更新服务租约
- 下线时通知Eureka服务端
- 获取服务实例列表;

更侧重于Eureka客户端的基本配置和功能实现
EurekaDiscoveryClientConfiguration配置EurekaDiscoveryClient创建RefreshScopeRefreshedEvent事件的监听类,用于重启注册;
更多地涉及到服务的自动注册、健康检查以及事件处理等方面

这两个配置类共同工作,确保了Spring Boot应用能够顺利地与Eureka服务注册中心进行交互,实现服务的注册和发现。

所以下文我们讨论客户端的4个功能(注册租约、更新租约、下线租约、获取服务列表),都是倾向于分析EurekaClient的代码。

EurekaClientAutoConfiguration自动配置类

自动化装配EurekaClient的相关bean,

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
      CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = {
      "org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration",
      "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
      "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
      "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })
public class EurekaClientAutoConfiguration {


    // 【1】EurekaClientConfigBean:Eureka客户端bean,保存了环境配置信息
    @Bean
    @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
    public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
       return new EurekaClientConfigBean();
    }


    //【2】EurekaInstanceConfigBean:Eureka客户端实例bean,保存了实例信息
    @Bean
    @ConditionalOnMissingBean(value = EurekaInstanceConfig.class,
          search = SearchStrategy.CURRENT)
    public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
          ManagementMetadataProvider managementMetadataProvider) {
       String hostname = getProperty("eureka.instance.hostname");
       boolean preferIpAddress = Boolean
             .parseBoolean(getProperty("eureka.instance.prefer-ip-address"));
       String ipAddress = getProperty("eureka.instance.ip-address");
       boolean isSecurePortEnabled = Boolean
             .parseBoolean(getProperty("eureka.instance.secure-port-enabled"));


       String serverContextPath = env.getProperty("server.servlet.context-path", "/");
       int serverPort = Integer.parseInt(
             env.getProperty("server.port", env.getProperty("port", "8080")));


       Integer managementPort = env.getProperty("management.server.port", Integer.class);
       String managementContextPath = env
             .getProperty("management.server.servlet.context-path");
       Integer jmxPort = env.getProperty("com.sun.management.jmxremote.port",
             Integer.class);
       EurekaInstanceConfigBean instance = new EurekaInstanceConfigBean(inetUtils);


       instance.setNonSecurePort(serverPort);
       instance.setInstanceId(getDefaultInstanceId(env));
       instance.setPreferIpAddress(preferIpAddress);
       instance.setSecurePortEnabled(isSecurePortEnabled);
       if (StringUtils.hasText(ipAddress)) {
          instance.setIpAddress(ipAddress);
       }


       if (isSecurePortEnabled) {
          instance.setSecurePort(serverPort);
       }


       if (StringUtils.hasText(hostname)) {
          instance.setHostname(hostname);
       }
       String statusPageUrlPath = getProperty("eureka.instance.status-page-url-path");
       String healthCheckUrlPath = getProperty("eureka.instance.health-check-url-path");


       // ....省略很多配置项赋值
       setupJmxPort(instance, jmxPort);
       return instance;
    }
}

分析:

  • 构造了EurekaInstanceConfigBean

    • EurekaClientConfigBean是一个配置类,会读取所有eureka.client打头的配置项,例如,eureka.client.serviceUrl.defaultZone=http://localhost:8881/eureka/,http://localhost2:8882/eureka/

  • 构造了EurekaClientConfigBean

EurekaClientConfiguration配置类

EurekaClientAutoConfiguration 内部有一个内部配置类EurekaClientConfiguration,它会继续初始化 CloudEurekaClient 和 ApplicationInfoManager 两个bean。

@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingRefreshScope
protected static class EurekaClientConfiguration {


    // 【3】
    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(value = EurekaClient.class,
          search = SearchStrategy.CURRENT)
    public EurekaClient eurekaClient(ApplicationInfoManager manager,
          EurekaClientConfig config) {
       return new CloudEurekaClient(manager, config, this.optionalArgs,
             this.context);
    }


    // 【4】
    @Bean
    @ConditionalOnMissingBean(value = ApplicationInfoManager.class,
          search = SearchStrategy.CURRENT)
    public ApplicationInfoManager eurekaApplicationInfoManager(
          EurekaInstanceConfig config) {
       InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
       return new ApplicationInfoManager(config, instanceInfo);
    }
}
小结

EurekaClientAutoConfiguration构造了EurekaClientConfigBean、EurekaInstanceConfigBean以及EurekaServiceRegistry,之后在这几个对象的基础上进一步构建ApplicationInfoManager、CloudEurekaClient等。

其中ApplicationInfoManager负责变更实例状态并发布StatusChangeEvent事件,而CloudEurekaClient继承了com.netflix.discovery.DiscoveryClient,里头包含了statusChangeListener用于响应StatusChangeEvent,最后触发的是DiscoveryClient.register方法,与远程的Eureka Server通信,同步实例状态。

另外,EurekaClientAutoConfiguration构建的EurekaClient-bean,会被EurekaDiscoveryClientConfiguration构建的EurekaDiscoveryClient,所注入使用

EurekaDiscoveryClientConfiguration自动配置类
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@ConditionalOnBlockingDiscoveryEnabled
public class EurekaDiscoveryClientConfiguration {


    // 【5】
    @Bean
    @ConditionalOnMissingBean
    public EurekaDiscoveryClient discoveryClient(EurekaClient client,
          EurekaClientConfig clientConfig) {
       return new EurekaDiscoveryClient(client, clientConfig);
    }


    // 【6】
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
    protected static class EurekaHealthCheckHandlerConfiguration {


        @Autowired(required = false)
        private StatusAggregator statusAggregator = new SimpleStatusAggregator();


        @Bean
        @ConditionalOnMissingBean(HealthCheckHandler.class)
        public EurekaHealthCheckHandler eurekaHealthCheckHandler() {
          return new EurekaHealthCheckHandler(this.statusAggregator);
        }
    }
}

分析:

  • 【5】构建EurekaDiscoveryClient,使用了EurekaClient 和 EurekaClientConfig

  • 【6】构建EurekaHealthCheckHandler

至此,完成了EurekaDiscoveryClient的创建


http://www.kler.cn/a/324656.html

相关文章:

  • 环境贴图选用方式
  • python面向对象基础入门
  • linux逻辑卷练习
  • 基于VUE实现语音通话:边录边转发送语言消息、 播放pcm 音频
  • 什么是项目完整性管理?
  • hive 统计各项目下排名前5的问题种类
  • 工业缺陷检测——Windows 10本地部署AnomalyGPT工业缺陷检测大模型
  • naocs注册中心,配置管理,openfeign在idea中实现模块间的调用,getway的使用
  • Python爬虫bs4的基本使用
  • Android平台如何获取CPU占用率和电池电量信息
  • Unity 与虚幻引擎对比:两大游戏开发引擎的优劣分析
  • 【工具变量】无废城市试点DID数据集(2000-2023)
  • 【C++笔记】八、结构体 [ 4 ]
  • 六练习题笔记
  • C++启动其它进程的方式
  • 【运动控制】关于GPIO通用输入口的锁存功能
  • RTX 5090、5080规格完整曝光,来看来看
  • 一起搭WPF界面之界面切换绑定
  • 深度学习之开发环境(CUDA、Conda、Pytorch)准备(4)
  • 基于SSM茶叶科普管理系统JAVA|VUE|SSM计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解
  • PREDATOR: Registration of 3D Point Clouds with Low Overlap
  • DeepSS2GO——基于 CNN 的模型可以根据化学键预测蛋白质的功能
  • JPA + Thymeleaf 增删改查
  • 【Element-UI】实现el-drawer抽屉的左右拖拽宽度
  • ​美​团​一​面​-​2​
  • 《中国电子报》报道: 安宝特AR为产线作业者的“秘密武器