1. 背景

Spring 事件的使用场景很多,接下来我们就讲讲如何分析和验证我们的猜测,以及如何利用Spring事件完成服务注册。

2. 分析一下

2.1 问题分析过程

应用启动Nacos注册中心没有发现注册的服务,初步分析肯定是注册有问题了, 那就去找是服务是如何注册的。

关键是从哪找呢?具体步骤:

  1. 知道服务启动方式。如Nacos,就是通过properties,那就找哪有用到启动的属性文件
  2. 项目用SpringBoot集成,那核心类的加载就在Spring.factories中。(这个文件会被springSPI加载到)
  3. 找到关键类

很凑巧,我们在spring.factories中找到了名字很像启动注册类的类。如下图:

在找类的时候不要怕错,大胆猜,只要验证了自己的想法即可,不对就在接着找。

com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration类我们找到了,接下来就是去看这个类中的代码去验证自己的想法了。

2.2 Nacos是如何利用Spring事件来实现服务注册的?

com.alibaba.cloud.nacos.registry.NacosServiceRegistryAutoConfiguration代码如下:

public class NacosServiceRegistryAutoConfiguration {// 注册类实现,验证我们的想法,需要用到NacosDiscoverProperties@Beanpublic NacosServiceRegistry nacosServiceRegistry(NacosDiscoveryProperties nacosDiscoveryProperties) {return new NacosServiceRegistry(nacosDiscoveryProperties);}// 构建注册事例,依然要用到NacosDiscoverProperties@Bean@ConditionalOnBean(AutoServiceRegistrationProperties.class)public NacosRegistration nacosRegistration(NacosDiscoveryProperties nacosDiscoveryProperties,ApplicationContext context) {return new NacosRegistration(nacosDiscoveryProperties, context);}// 服务自动注册,通过上面实例化的两个类@Bean@ConditionalOnBean(AutoServiceRegistrationProperties.class)public NacosAutoServiceRegistration nacosAutoServiceRegistration(NacosServiceRegistry registry,AutoServiceRegistrationProperties autoServiceRegistrationProperties,NacosRegistration registration) {return new NacosAutoServiceRegistration(registry,autoServiceRegistrationProperties, registration);}
}

通过查看代码,发现和我们猜想没有出入,是通过NacosDiscoverProperties来进行初始化,和官方给的Demo也相似(只是没继承SpringBoot,名字不叫这个),前面两个类我们先不用关注(好奇的可以先点进去看看),我们直接进入第三个方法NacosAutoServiceRegistration中,通过名字我们也可以看出,这个是自动注册的,参数中包含了Registry和Registration。我们进入类中接着查看。

public class NacosAutoServiceRegistrationextends AbstractAutoServiceRegistration<Registration> {private NacosRegistration registration;// 构造参数,关键public NacosAutoServiceRegistration(ServiceRegistry<Registration> serviceRegistry,AutoServiceRegistrationProperties autoServiceRegistrationProperties,NacosRegistration registration) {super(serviceRegistry, autoServiceRegistrationProperties);this.registration = registration;}// 注册方法@Overrideprotected void register() {if (!this.registration.getNacosDiscoveryProperties().isRegisterEnabled()) {log.debug("Registration disabled.");return;}if (this.registration.getPort() < 0) {this.registration.setPort(getPort().get());}// 调用了父类的注册方法super.register();}
}

进入类中,我们发现了register()方法,更进一步验证我们想法,是通过这个类来进行服务注册,但是在register()方法中,调用了父类的register()方法, 这点就要引起我们的好奇,父类都没都啥东西,调用父类的方法有啥用呢? 这个时候就想到了肯定有构造参数,或者方法初始化父类的东西啦!

我们观察构造参数,发现把serviceRegistry传入到了父类构造参数中,我们直接查看父类代码。

// 类
public abstract class AbstractAutoServiceRegistration<R extends Registration>implements AutoServiceRegistration, ApplicationContextAware,ApplicationListener<WebServerInitializedEvent> {}

AbstractAutoServiceRegistration是继承了ApplicationListener<WebServerInitializedEvent>,看到这,不就是我们熟悉的领域了吗?继承了ApplicationListener那就不多BB,直接看onApplicationEvent()方法就好啦。(这个事件是WebServerInitializedEvent,这点还是要知道的哈)

 @Override@SuppressWarnings("deprecation")public void onApplicationEvent(WebServerInitializedEvent event) {// 调用了bind方法bind(event);}@Deprecatedpublic void bind(WebServerInitializedEvent event) {ApplicationContext context = event.getApplicationContext();if (context instanceof ConfigurableWebServerApplicationContext) {if ("management".equals(((ConfigurableWebServerApplicationContext) context).getServerNamespace())) {return;}}this.port.compareAndSet(0, event.getWebServer().getPort());// 调用了start()方法this.start();}

我们直接进入start()方法:

 public void start() {if (!isEnabled()) {if (logger.isDebugEnabled()) {logger.debug("Discovery Lifecycle disabled. Not starting");}return;}// only initialize if nonSecurePort is greater than 0 and it isn't already running// because of containerPortInitializer belowif (!this.running.get()) {// 时间注册之前事件this.context.publishEvent(new InstancePreRegisteredEvent(this, getRegistration()));// 调用了注册方法register();// 服务注册事件this.context.publishEvent(new InstanceRegisteredEvent<>(this, getConfiguration()));this.running.compareAndSet(false, true);}}

start()方法里面还发布了两个事件,可见Spring中,服务的注册前后都有一些监听器来处理服务信息。先跳过这些,我们直接关注我们的核心——注册,直接进入register()方法中。

 protected void register() {// this.serviceRegistry 是我们传递进来的,// getRegistration() 是在NacosAutoServiceRegistration中实现的this.serviceRegistry.register(getRegistration());}

到这我们基本知道是怎么回事了,this.serviceRegistry是传递进来的,那调用的register()方法则是Nacos自己实现的注册,getRegistration()NacosServiceRegistryAutoConfiguration中通过nacosDiscoveryProperties生成的。那我们就看一下register()方法就好啦。

 @Overridepublic void register(Registration registration) {if (StringUtils.isEmpty(registration.getServiceId())) {log.warn("No service to register for nacos client...");return;}String serviceId = registration.getServiceId();String group = nacosDiscoveryProperties.getGroup();// 通过registration构建Instance实例Instance instance = getNacosInstanceFromRegistration(registration);try {// 向服务端发送注册请求namingService.registerInstance(serviceId, group, instance);log.info("nacos registry, {} {} {}:{} register finished", group, serviceId,instance.getIp(), instance.getPort());}catch (Exception e) {log.error("nacos registry, {} register failed...{},", serviceId,registration.toString(), e);// rethrow a RuntimeException if the registration is failed.// issue : https://github.com/alibaba/spring-cloud-alibaba/issues/1132rethrowRuntimeException(e);}}

到这我们就看懂了,也知道大概流程了。流程图如下

3. 代码经验分享

Spring 在实现的时候,通过设计模式(装饰)实现流程,又把关键部分丢给开发自己实现,提高了拓展性,然后巧妙的结合了观察者模式(变种),在合适的时间注册服务,妙啊~~

Nacos在实现的时候,深知Spring各种属性,各种用法,一定是对Spring有个深入了解的人,结合AutoRegistry和事件的结合。

4. 总结

总的看下来,这个启动,只能有Web容器启动的时候才能注册。

如何通过Spring的ApplicationListener事件注册服务相关推荐

  1. 七.Spring之ApplicationListener事件监听、@EventListener

    看看注释:由应用事件监听器实现的接口,基于观察者设计模式. 方法是处理应用事件. /**由应用事件监听器实现的接口,基于观察者设计模式* Interface to be implemented by ...

  2. Spring Boot 构建war 部署到tomcat下无法在Nacos中注册服务

    文章目录 1. 问题 2. 分析 3. 解决方案 1. 问题 使用Nacos作为注册中心的Spring Boot项目,以war包形式部署到服务器上,启动项目发现该服务无法在Nacos中注册. 2. 分 ...

  3. 从源码底层聊聊Spring Cloud是如何一统服务注册、发现编程模型

    文章目录 背景 源码版本 核心抽象接口 DiscoveryClient EnableDiscoveryClient ReactiveDiscoveryClient ServiceInstance Re ...

  4. Spring Cloud第一篇:服务注册与发现Eureka

    一.spring cloud简介 spring cloud 为开发人员提供了快速构建分布式系统的一些工具,包括配置管理.服务发现.断路器.路由.微代理.事件总线.全局锁.决策竞选.分布式会话等等.它运 ...

  5. 新 Spring Cloud (一) 之 Eureka 服务注册中心

    文章目录 一.前言 0. 之前写过两篇Spring Cloud,但是感觉不够具体,所以重新写了一份. 1. SpringCloud 2. 什么是Eureka 3. 原理图 二.基本使用实例 1. 场景 ...

  6. Spring Cloud(一)服务的注册与发现(Eureka)

    Spring Cloud是一个基于Spring Boot实现的云应用开发工具,它为基于JVM的云应用开发中涉及的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集 ...

  7. Spring Cloud 学习笔记(一) 之服务治理模块Spring Cloud Eureka 搭建注册中心

    2019独角兽企业重金招聘Python工程师标准>>> 关于springboot的学习请参考前面的文章 接下来我们会开启一系列关于springcloud的学习文章. 一.概念 首先我 ...

  8. Spring Boot————ApplicationListener实现逃课事件监听

    引言 上一篇文章转了一篇关于ApplicationListener用于在Web项目启动时做一些初始化的用法. 但是,在实际生产过程中,当一个事件产生,又是如何被onApplicationEvent() ...

  9. Spring Cloud Eureka(二)注册一个服务的提供者

    Spring Cloud Eureka(二)注册一个服务的提供者 注册一个服务的提供者 在上一篇中,当启动项目并访问localhost:1111时,发现该注册中心还没有注册任何服务.所以现在来搞一个服 ...

  10. Spring Boot快速注册服务脚本

    前言 Spring Boot项目通过JAR打包部署的时候,一般我们所采取的措施是将其注册为服务,并通过service命令管理项目.但注册服务的过程相对繁琐,不如写一个脚本来快速注册(入门Shell). ...

最新文章

  1. pushState 和 replaceState
  2. DOS调用21H存取中断向量
  3. wifi的基础知识及原理1
  4. 1000 驱动_华为海思自研OLED驱动芯片已流片:最高28nm、可完全去美化
  5. tyvj 2002 扑克牌
  6. Digimeter 软件
  7. c++ 数组的输入遇到特定字符停止输入_C语言 第4章-字符串和格式化输入/输出
  8. 《Python Cookbook 3rd》笔记(1.1):拆分序列后赋值给多个变量
  9. Mysql中把varchar类型的字段转化为tinyint类型的字段
  10. Protocol Buffers proto语言语法说明
  11. wps xml转换表格_如何转换Excel格式?学会这几招?1键即可完成转换
  12. mysql sql自动优化_SQL语句的自动优化_MySQL
  13. 初识 Hbase 数据库
  14. 二维码解码器(zbar-0.10+ opencv-2.4.10+VS2010)完整实例含源代码
  15. python调用nmap扫描局域网存活主机和端口
  16. 综合日语第一册第十一课
  17. 基于React、Typescript和Solidity的NFT完整教程
  18. linux运维笔记:动态网页资源
  19. 二进制基带信号的时域特性
  20. 【视频分析】智能视频分析技术让安防更加智慧...

热门文章

  1. 第二章:x264视频制作meGUI工具安装
  2. iOS从零开始,使用Swift:探索基础框架
  3. CSDN-markdown编辑器的使用
  4. layui模板引擎的使用1
  5. iOS 多语言本地化 完美解决方案【自动+手动】
  6. Ambiguous mapping. Cannot map ‘xxxController‘ method
  7. 物联网+安防在智慧社区建设中的发展与深度应用
  8. PCB电源 - 开关电源介绍、工作原理、电路图
  9. vue+element表格 苹果自带浏览器兼容问题
  10. LANTENNA:通过以太网电缆泄露,从物理隔离网络中窃取数据