springboot之WebServerInitializedEvent事件

前言
请先闲暇时间阅读文章springboot-tomcat服务启动

  1. 从链接文章中,我们可以发现tomcat启动之后,会发布一个ServletWebServerInitializedEvent事件。
  2. 熟悉spring事件机制的朋友都会知道,事件event,和监听器Listener是共同存在的,通过观察者模式来完成spring的事件机制。
  3. 今天,我们就来聊聊这个事件,能涉及到哪些我们想知道的事情

ServletWebServerInitializedEvent

  1. 看源码得知,就是WebServerInitializedEvent的子类

  2. 并且在ServletWebServerApplicationContext中,tomcat启动之后,发布事件

    @Override
     protected void finishRefresh() {
     	super.finishRefresh();
     	WebServer webServer = startWebServer();
     	if (webServer != null) {
     		publishEvent(new ServletWebServerInitializedEvent(webServer, this));
     	}
     }
    
  3. 获取到真实的tomcat端口

    WebServer  webServer;
    int port = webServer.getPort();
    

    服务启动之后的webServer对象,从该对象#getPort()可以直接获取真实启动的端口。
    可以从ServletWebServerInitializedEvent事件中获取到该对象

事件对应的监听器

1. ServerPortInfoApplicationContextInitializer

是springboot2.0.0之后,添加一个类

@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.addApplicationListener(this);
	}

	@Override
	public void onApplicationEvent(WebServerInitializedEvent event) {
		String propertyName = "local." + getName(event.getApplicationContext()) + ".port";
		setPortProperty(event.getApplicationContext(), propertyName, event.getWebServer().getPort());
	}
  • 从类名可以推测出,该类是一个ApplicationContextInitializer上下文初始化器,(spring或者springboot在创建出AppcationContext对象之后,都会去获取到容器中所有的ApplicationContextInitializer,然后进行初始化)
  • 从该类实现逻辑看,就是将自身添加到applicationContext的监听器中,然后监听WebServerInitializedEvent 事件。并且将实际的tomcat端口存储到local.server.port属性中

2. AbstractAutoServiceRegistration
在这里插入图片描述
**可以看出该类,自身是一个WebServerInitializedEvent监听器**

该类是spring-cloud-common中的一个类,作用是: 将自身服务注册到注册中心的Abstract模板类。
从以下代码可以实现功能:就是获取WebServer的端口,然后进行服务注册

	@Override
	@SuppressWarnings("deprecation")
	public void onApplicationEvent(WebServerInitializedEvent event) {
		bind(event);
	}

	@Deprecated
	public 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());
		this.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 below
		if (!this.running.get()) {
			this.context.publishEvent(
					new InstancePreRegisteredEvent(this, getRegistration()));
			register();
			if (shouldRegisterManagement()) {
				registerManagement();
			}
			this.context.publishEvent(
					new InstanceRegisteredEvent<>(this, getConfiguration()));
			this.running.compareAndSet(false, true);
		}

	}

AbstractAutoServiceRegistration的子类在这里插入图片描述

  1. 从图中我们可以看出,有zookeepernacos作为注册中心的子类实现。
  2. 但是有的同学可能会说,之前常用的Eureak注册中心,不存在吗?当然不是,看如下
    在这里插入图片描述
  3. 从上图可以得出,Eureka并没有继承spring-cloud-common的服务注册的规范,而是自己搞了一遍。
    熟悉spring的朋友都知道,上图标记的两个接口,SmartLifecycle和SmartApplicationListener都是spring中的拥有生命周期的接口

所以要么是SmartLifecycle中的start进行注册,还是在SmartApplicationListener监听器中进行注册。不过从EurekaAutoServiceRegistration中得出,其实该类是做了服务注册两次调用

	@Override
	public void start() {
		// only set the port if the nonSecurePort or securePort is 0 and this.port != 0
		if (this.port.get() != 0) {
			if (this.registration.getNonSecurePort() == 0) {
				this.registration.setNonSecurePort(this.port.get());
			}

			if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
				this.registration.setSecurePort(this.port.get());
			}
		}

		// only initialize if nonSecurePort is greater than 0 and it isn't already running
		// because of containerPortInitializer below
		if (!this.running.get() && this.registration.getNonSecurePort() > 0) {

			this.serviceRegistry.register(this.registration);

			this.context.publishEvent(new InstanceRegisteredEvent<>(this,
					this.registration.getInstanceConfig()));
			this.running.set(true);
		}
	}
	
	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof WebServerInitializedEvent) {
			onApplicationEvent((WebServerInitializedEvent) event);
		}
		else if (event instanceof ContextClosedEvent) {
			onApplicationEvent((ContextClosedEvent) event);
		}
	}
	public void onApplicationEvent(WebServerInitializedEvent event) {
		// TODO: take SSL into account
		String contextName = event.getApplicationContext().getServerNamespace();
		if (contextName == null || !contextName.equals("management")) {
			int localPort = event.getWebServer().getPort();
			if (this.port.get() == 0) {
				log.info("Updating port to " + localPort);
				this.port.compareAndSet(0, localPort);
				start();
			}
		}
	}
  • 得出结论:
    • EurekaAutoServiceRegistration所做之事和AbstractAutoServiceRegistration基本一模一样,
      到底是谁先,谁后,谁抄袭了谁,不得而知,留给你们自己思考?
    • 另一点就是EurekaAutoServiceRegistration服务注册借用spring声明周期调用两次,
      只不过第一次port为空,条件不满足。
      第二次事件监听去注册,此时port获取到了,条件满足,去执行了注册。
      各位可以思考,这样是否有意义,让我们猜测下当时作者开发的思路?
  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值