前言 请先闲暇时间阅读文章springboot-tomcat服务启动
从链接文章中,我们可以发现tomcat启动之后,会发布一个ServletWebServerInitializedEvent事件。熟悉spring事件机制的朋友都会知道,事件event,和监听器Listener是共同存在的,通过观察者模式来完成spring的事件机制。今天,我们就来聊聊这个事件,能涉及到哪些我们想知道的事情看源码得知,就是WebServerInitializedEvent的子类
并且在ServletWebServerApplicationContext中,tomcat启动之后,发布事件
@Override protected void finishRefresh() { super.finishRefresh(); WebServer webServer = startWebServer(); if (webServer != null) { publishEvent(new ServletWebServerInitializedEvent(webServer, this)); } }获取到真实的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); } }所以要么是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获取到了,条件满足,去执行了注册。 各位可以思考,这样是否有意义,让我们猜测下当时作者开发的思路?