Spring boot 启动 tomact 流程源码

Source

spring boot tomcat 自动装配

springboot 入口ServletWebServerFactoryAutoConfiguration,如下图,不再详细介绍。
在这里插入图片描述

  • TomcatServletWebServerFactoryCustomizer
public class TomcatServletWebServerFactoryCustomizer
		implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
    
      

	private final ServerProperties serverProperties;

	public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    
      
		this.serverProperties = serverProperties;
	}

	@Override
	public int getOrder() {
    
      
		return 0;
	}

	@Override
	public void customize(TomcatServletWebServerFactory factory) {
    
      
		// 设置自定义属性 
		ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
		if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
    
      
			factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());
		}
		if (tomcatProperties.getRedirectContextRoot() != null) {
    
      
			customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());
		}
		customizeUseRelativeRedirects(factory, tomcatProperties.getUseRelativeRedirects());
		factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());
	}

	private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, boolean redirectContextRoot) {
    
      
		factory.addContextCustomizers((context) -> context.setMapperContextRootRedirectEnabled(redirectContextRoot));
	}

	private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory,
			boolean useRelativeRedirects) {
    
      
		factory.addContextCustomizers((context) -> context.setUseRelativeRedirects(useRelativeRedirects));
	}

}
  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
    
      

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    
      
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
    
      
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
    
      
			if (this.beanFactory == null) {
    
      
				return;
			}
			// 注册 bean 的后置处理器
			registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
    
      
			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
    
      
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}
  • WebServerFactoryCustomizerBeanPostProcessor
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    
      

	private ListableBeanFactory beanFactory;

	private List<WebServerFactoryCustomizer<?>> customizers;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
    
      
		Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
				"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
		this.beanFactory = (ListableBeanFactory) beanFactory;
	}

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
      
		if (bean instanceof WebServerFactory) {
    
      
			postProcessBeforeInitialization((WebServerFactory) bean);
		}
		return bean;
	}

	@SuppressWarnings("unchecked")
	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
    
      
		LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
				// 触发tomcat设置自定义属性 
				.invoke((customizer) -> customizer.customize(webServerFactory));
	}

	private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
    
      
		if (this.customizers == null) {
    
      
			// Look up does not include the parent context
			this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
			this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}

	@SuppressWarnings({
    
       "unchecked", "rawtypes" })
	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
    
      
		return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
	}

}

tomcat 主要组件启动流程

@SpringBootApplication
public class JobApplication {
    
      
    public static void main(String [] args) {
    
      
        SpringApplication.run(JobApplication.class, args);
    }
}

SpringApplication.java

	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    
      
		return new SpringApplication(primarySources).run(args);
	}
	public ConfigurableApplicationContext run(String... args) {
    
      
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
    
      
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] {
    
       ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
    
      
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
    
      
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
    
      
			listeners.running(context);
		}
		catch (Throwable ex) {
    
      
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}
	
	private void refreshContext(ConfigurableApplicationContext context) {
    
      
		refresh(context);
		if (this.registerShutdownHook) {
    
      
			try {
    
      
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
    
      
				// Not allowed in some environments.
			}
		}
	}
	
	protected void refresh(ApplicationContext applicationContext) {
    
      
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}

ServletWebServerApplicationContext.java


	@Override
	public final void refresh() throws BeansException, IllegalStateException {
    
      
		try {
    
      
			// 调用父类AbstractApplicationContext方法,其实是spring 容器初始化过程, 这里不在展开介绍,
			super.refresh();
		}
		catch (RuntimeException ex) {
    
      
			stopAndReleaseWebServer();
			throw ex;
		}
	}
	
	@Override
	protected void onRefresh() {
    
      
		super.onRefresh();
		try {
    
      
			// 创建tomcat web服务器开始
			createWebServer();
		}
		catch (Throwable ex) {
    
      
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}
private void createWebServer() {
    
      
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
    
      
			ServletWebServerFactory factory = getWebServerFactory();
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
    
      
			try {
    
      
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
    
      
				throw new ApplicationContextException("Cannot initialize servlet context", ex);
			}
		}
		initPropertySources();
	}

TomcatServletWebServerFactory

public WebServer getWebServer(ServletContextInitializer... initializers) {
    
      
		if (this.disableMBeanRegistry) {
    
      
			Registry.disableRegistry();
		}
		// 创建 一个tomcat 
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		// tomcat 核心组件 连接器Connector
		Connector connector = new Connector(this.protocol);
		connector.setThrowOnFailure(true);
		//获取service[0] 并添加 连接器Connector
		tomcat.getService().addConnector(connector);
		// 自定义配置连接器Connector属性
		customizeConnector(connector);
		// 添加连接器Connector
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		// 获取 执行容器 container, 绑定到service[0] ,并配置容器的pipeline 处理链
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
    
      
			tomcat.getService().addConnector(additionalConnector);
		}
		// 初始化TomcatEmbeddedContext
		prepareContext(tomcat.getHost(), initializers);
		// TomcatWebServer Start the Tomcat server to trigger initialization listeners 并开启守护线程
		return getTomcatWebServer(tomcat);
	}

借一张图片
在这里插入图片描述

TomcatWebServer

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    
      
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		initialize();
	}

	private void initialize() throws WebServerException {
    
      
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
    
      
			try {
    
      
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
    
      
					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
    
      
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
    
      
					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
				}
				catch (NamingException ex) {
    
      
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
    
      
				stopSilently();
				destroySilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

Start the server to trigger initialization listeners
如图:this.tomcat.start()
在这里插入图片描述
到这里,ServletWebServerApplicationContext.createWebServer()已经分析完了,接下来startWebServer();


	@Override
	protected void onRefresh() {
    
      
		super.onRefresh();
		try {
    
      
			// 创建tomcat web服务器开始
			createWebServer();
		}
		catch (Throwable ex) {
    
      
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}
	@Override
	protected void finishRefresh() {
    
      
		super.finishRefresh();
		WebServer webServer = startWebServer();
		if (webServer != null) {
    
      
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}
	
	private WebServer startWebServer() {
    
      
		WebServer webServer = this.webServer;
		if (webServer != null) {
    
      
			webServer.start();
		}
		return webServer;
	}

TomcatWebServer


	@Override
	public void start() throws WebServerException {
    
      
		synchronized (this.monitor) {
    
      
			if (this.started) {
    
      
				return;
			}
			try {
    
      
				addPreviouslyRemovedConnectors();
				Connector connector = this.tomcat.getConnector();
				if (connector != null && this.autoStart) {
    
      
					//相关依赖启动TomcatEmbeddedContext->StartupWrappers
					performDeferredLoadOnStartup();
				}
				//检查是否启动,如果失败跑出异常
				checkThatConnectorsHaveStarted();
				this.started = true;
				logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
						+ getContextPath() + "'");
			}
			catch (ConnectorStartFailedException ex) {
    
      
				stopSilently();
				throw ex;
			}
			catch (Exception ex) {
    
      
				PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
				throw new WebServerException("Unable to start embedded Tomcat server", ex);
			}
			finally {
    
      
				Context context = findContext();
				ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
			}
		}
	}

Connector 组件获取网络数据流程

Connector

/**
     * Defaults to using HTTP/1.1 NIO implementation.
     */
    public Connector() {
    
      
        this("org.apache.coyote.http11.Http11NioProtocol");
    }
@Override
    protected void initInternal() throws LifecycleException {
    
      

        super.initInternal();

        if (protocolHandler == null) {
    
      
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"));
        }

        // Initialize adapter
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);
        if (service != null) {
    
      
            protocolHandler.setUtilityExecutor(service.getServer().getUtilityExecutor());
        }

        // Make sure parseBodyMethodsSet has a default
        if (null == parseBodyMethodsSet) {
    
      
            setParseBodyMethods(getParseBodyMethods());
        }

        if (protocolHandler.isAprRequired() && !AprLifecycleListener.isInstanceCreated()) {
    
      
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprListener",
                    getProtocolHandlerClassName()));
        }
        if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
    
      
            throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoAprLibrary",
                    getProtocolHandlerClassName()));
        }
        if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
                protocolHandler instanceof AbstractHttp11JsseProtocol) {
    
      
            AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                    (AbstractHttp11JsseProtocol<?>) protocolHandler;
            if (jsseProtocolHandler.isSSLEnabled() &&
                    jsseProtocolHandler.getSslImplementationName() == null) {
    
      
                // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
                jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
            }
        }

        try {
    
      
            protocolHandler.init();
        } catch (Exception e) {
    
      
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }


    /**
     * Begin processing requests via this Connector.
     *
     * @exception LifecycleException if a fatal startup error occurs
     */
    @Override
    protected void startInternal() throws LifecycleException {
    
      

        // Validate settings before starting
        if (getPortWithOffset() < 0) {
    
      
            throw new LifecycleException(sm.getString(
                    "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
        }

        setState(LifecycleState.STARTING);

        try {
    
      
            protocolHandler.start();
        } catch (Exception e) {
    
      
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
        }
    }

如图
在这里插入图片描述

Http11NioProtocol


    public Http11NioProtocol() {
    
      
        super(new NioEndpoint());
    }

	@Override
    public void init() throws Exception {
    
      
        if (getLog().isInfoEnabled()) {
    
      
            getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
            logPortOffset();
        }

        if (oname == null) {
    
      
            // Component not pre-registered so register it
            oname = createObjectName();
            if (oname != null) {
    
      
                Registry.getRegistry(null, null).registerComponent(this, oname, null);
            }
        }

        if (this.domain != null) {
    
      
            rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
            Registry.getRegistry(null, null).registerComponent(
                    getHandler().getGlobal(), rgOname, null);
        }

        String endpointName = getName();
        endpoint.setName(endpointName.substring(1, endpointName.length()-1));
        endpoint.setDomain(domain);

        endpoint.init();
    }
@Override
    public void start() throws Exception {
    
      
        if (getLog().isInfoEnabled()) {
    
      
            getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
            logPortOffset();
        }

        endpoint.start();
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
                new Runnable() {
    
      
                    @Override
                    public void run() {
    
      
                        if (!isPaused()) {
    
      
                            startAsyncTimeout();
                        }
                    }
                }, 0, 60, TimeUnit.SECONDS);
    }

NioEndpoint 继承 => AbstractJsseEndpoint 继承 => AbstractEndpoint。
AbstractEndpoint


    private void bindWithCleanup() throws Exception {
    
      
        try {
    
      
            bind();
        } catch (Throwable t) {
    
      
            // Ensure open sockets etc. are cleaned up if something goes
            // wrong during bind
            ExceptionUtils.handleThrowable(t);
            unbind();
            throw t;
        }
    }


    public final void init() throws Exception {
    
      
        if (bindOnInit) {
    
      
            bindWithCleanup();
            bindState = BindState.BOUND_ON_INIT;
        }
        if (this.domain != null) {
    
      
            // Register endpoint (as ThreadPool - historical name)
            oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
            Registry.getRegistry(null, null).registerComponent(this, oname, null);

            ObjectName socketPropertiesOname = new ObjectName(domain +
                    ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
            socketProperties.setObjectName(socketPropertiesOname);
            Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

            for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
    
      
                registerJmx(sslHostConfig);
            }
        }
    }

    public final void start() throws Exception {
    
      
        if (bindState == BindState.UNBOUND) {
    
      
            bindWithCleanup();
            bindState = BindState.BOUND_ON_START;
        }
        startInternal();
    }


    protected void startAcceptorThread() {
    
      
        acceptor = new Acceptor<>(this);
        String threadName = getName() + "-Acceptor";
        acceptor.setThreadName(threadName);
        Thread t = new Thread(acceptor, threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }

NioEndpoint


    /**
     * Initialize the endpoint.
     */
    @Override
    public void bind() throws Exception {
    
      
        initServerSocket();

        setStopLatch(new CountDownLatch(1));

        // Initialize SSL if needed
        initialiseSsl();

        selectorPool.open(getName());
    }

    // Separated out to make it easier for folks that extend NioEndpoint to
    // implement custom [server]sockets
    protected void initServerSocket() throws Exception {
    
      
        if (!getUseInheritedChannel()) {
    
      
            serverSock = ServerSocketChannel.open();
            socketProperties.setProperties(serverSock.socket());
            InetSocketAddress addr = new InetSocketAddress(getAddress(), getPortWithOffset());
            serverSock.socket().bind(addr,getAcceptCount());
        } else {
    
      
            // Retrieve the channel provided by the OS
            Channel ic = System.inheritedChannel();
            if (ic instanceof ServerSocketChannel) {
    
      
                serverSock = (ServerSocketChannel) ic;
            }
            if (serverSock == null) {
    
      
                throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
            }
        }
        serverSock.configureBlocking(true); //mimic APR behavior
    }


    /**
     * Start the NIO endpoint, creating acceptor, poller threads.
     */
    @Override
    public void startInternal() throws Exception {
    
      

        if (!running) {
    
      
            running = true;
            paused = false;

            if (socketProperties.getProcessorCache() != 0) {
    
      
                processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getProcessorCache());
            }
            if (socketProperties.getEventCache() != 0) {
    
      
                eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
            }
            if (socketProperties.getBufferPool() != 0) {
    
      
                nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getBufferPool());
            }

            // Create worker collection
            if (getExecutor() == null) {
    
      
                createExecutor();
            }

            initializeConnectionLatch();

            // Start poller thread
            poller = new Poller();
            Thread pollerThread = new Thread(poller, getName() + "-ClientPoller");
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();

            startAcceptorThread();
        }
    }

Endpoint - Acceptor

已绑定
serverSock = ServerSocketChannel.open();
serverSock.socket().bind(addr,getAcceptCount());

单线程接受客户端socket

public void run() {
    
      

        int errorDelay = 0;

        // Loop until we receive a shutdown command
        while (endpoint.isRunning()) {
    
      

            // Loop if endpoint is paused
            while (endpoint.isPaused() && endpoint.isRunning()) {
    
      
                state = AcceptorState.PAUSED;
                try {
    
      
                    Thread.sleep(50);
                } catch (InterruptedException e) {
    
      
                    // Ignore
                }
            }

            if (!endpoint.isRunning()) {
    
      
                break;
            }
            state = AcceptorState.RUNNING;

            try {
    
      
                //if we have reached max connections, wait
                endpoint.countUpOrAwaitConnection();

                // Endpoint might have been paused while waiting for latch
                // If that is the case, don't accept new connections
                if (endpoint.isPaused()) {
    
      
                    continue;
                }

                U socket = null;
                try {
    
      
                    // Accept the next incoming connection from the server
                    // socket
                    socket = endpoint.serverSocketAccept();
                } catch (Exception ioe) {
    
      
                    // We didn't get a socket
                    endpoint.countDownConnection();
                    if (endpoint.isRunning()) {
    
      
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    } else {
    
      
                        break;
                    }
                }
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                if (endpoint.isRunning() && !endpoint.isPaused()) {
    
      
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    if (!endpoint.setSocketOptions(socket)) {
    
      
                        endpoint.closeSocket(socket);
                    }
                } else {
    
      
                    endpoint.destroySocket(socket);
                }
            } catch (Throwable t) {
    
      
                ExceptionUtils.handleThrowable(t);
                String msg = sm.getString("endpoint.accept.fail");
                // APR specific.
                // Could push this down but not sure it is worth the trouble.
                if (t instanceof Error) {
    
      
                    Error e = (Error) t;
                    if (e.getError() == 233) {
    
      
                        // Not an error on HP-UX so log as a warning
                        // so it can be filtered out on that platform
                        // See bug 50273
                        log.warn(msg, t);
                    } else {
    
      
                        log.error(msg, t);
                    }
                } else {
    
      
                        log.error(msg, t);
                }
            }
        }
        state = AcceptorState.ENDED;
    }

调用NioEndpoint.setSocketOptions,封装NioSocketWrapper,将其以事件注册到Poller队列中

protected boolean setSocketOptions(SocketChannel socket) {
    
      
        NioSocketWrapper socketWrapper = null;
        try {
    
      
            // Allocate channel and wrapper
            NioChannel channel = null;
            if (nioChannels != null) {
    
      
                channel = nioChannels.pop();
            }
            if (channel == null) {
    
      
                SocketBufferHandler bufhandler = new SocketBufferHandler(
                        socketProperties.getAppReadBufSize(),
                        socketProperties.getAppWriteBufSize(),
                        socketProperties.getDirectBuffer());
                if (isSSLEnabled()) {
    
      
                    channel = new SecureNioChannel(bufhandler, selectorPool, this);
                } else {
    
      
                    channel = new NioChannel(bufhandler);
                }
            }
            NioSocketWrapper newWrapper = new NioSocketWrapper(channel, this);
            channel.reset(socket, newWrapper);
            connections.put(socket, newWrapper);
            socketWrapper = newWrapper;

            // Set socket properties
            // Disable blocking, polling will be used
            socket.configureBlocking(false);
            socketProperties.setProperties(socket.socket());

            socketWrapper.setReadTimeout(getConnectionTimeout());
            socketWrapper.setWriteTimeout(getConnectionTimeout());
            socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
            socketWrapper.setSecure(isSSLEnabled());
            poller.register(channel, socketWrapper);
            return true;
        } catch (Throwable t) {
    
      
            ExceptionUtils.handleThrowable(t);
            try {
    
      
                log.error(sm.getString("endpoint.socketOptionsError"), t);
            } catch (Throwable tt) {
    
      
                ExceptionUtils.handleThrowable(tt);
            }
            if (socketWrapper == null) {
    
      
                destroySocket(socket);
            }
        }
        // Tell to close the socket if needed
        return false;
    }

EndPoint - Poller

取出事件PollerEvent,依次执行channel 注册到Selector上;

/**
         * Processes events in the event queue of the Poller.
         *
         * @return <code>true</code> if some events were processed,
         *   <code>false</code> if queue was empty
         */
        public boolean events() {
    
      
            boolean result = false;

            PollerEvent pe = null;
            for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
    
      
                result = true;
                NioChannel channel = pe.getSocket();
                NioSocketWrapper socketWrapper = channel.getSocketWrapper();
                int interestOps = pe.getInterestOps();
                if (interestOps == OP_REGISTER) {
    
      
                    try {
    
      
                        channel.getIOChannel().register(getSelector(), SelectionKey.OP_READ, socketWrapper);
                    } catch (Exception x) {
    
      
                        log.error(sm.getString("endpoint.nio.registerFail"), x);
                    }
                } else {
    
      
                    final SelectionKey key = channel.getIOChannel().keyFor(getSelector());
                    if (key == null) {
    
      
                        // The key was cancelled (e.g. due to socket closure)
                        // and removed from the selector while it was being
                        // processed. Count down the connections at this point
                        // since it won't have been counted down when the socket
                        // closed.
                        socketWrapper.close();
                    } else {
    
      
                        final NioSocketWrapper attachment = (NioSocketWrapper) key.attachment();
                        if (attachment != null) {
    
      
                            // We are registering the key to start with, reset the fairness counter.
                            try {
    
      
                                int ops = key.interestOps() | interestOps;
                                attachment.interestOps(ops);
                                key.interestOps(ops);
                            } catch (CancelledKeyException ckx) {
    
      
                                cancelledKey(key, socketWrapper);
                            }
                        } else {
    
      
                            cancelledKey(key, attachment);
                        }
                    }
                }
                if (running && !paused && eventCache != null) {
    
      
                    pe.reset();
                    eventCache.push(pe);
                }
            }

            return result;
        }
/**
         * The background thread that adds sockets to the Poller, checks the
         * poller for triggered events and hands the associated socket off to an
         * appropriate processor as events occur.
         */
        @Override
        public void run() {
    
      
            // Loop until destroy() is called
            while (true) {
    
      

                boolean hasEvents = false;

                try {
    
      
                    if (!close) {
    
      
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
    
      
                            // If we are here, means we have other stuff to do
                            // Do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
    
      
                            keyCount = selector.select(selectorTimeout);
                        }
                        wakeupCounter.set(0);
                    }
                    if (close) {
    
      
                        events();
                        timeout(0, false);
                        try {
    
      
                            selector.close();
                        } catch (IOException ioe) {
    
      
                            log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                        }
                        break;
                    }
                } catch (Throwable x) {
    
      
                    ExceptionUtils.handleThrowable(x);
                    log.error(sm.getString("endpoint.nio.selectorLoopError"), x);
                    continue;
                }
                // Either we timed out or we woke up, process events first
                if (keyCount == 0) {
    
      
                    hasEvents = (hasEvents | events());
                }

                Iterator<SelectionKey> iterator =
                    keyCount > 0 ? selector.selectedKeys().iterator() : null;
                // Walk through the collection of ready keys and dispatch
                // any active event.
                while (iterator != null && iterator.hasNext()) {
    
      
                    SelectionKey sk = iterator.next();
                    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
                    // Attachment may be null if another thread has called
                    // cancelledKey()
                    if (socketWrapper == null) {
    
      
                        iterator.remove();
                    } else {
    
      
                        iterator.remove();
                        processKey(sk, socketWrapper);
                    }
                }

                // Process timeouts
                timeout(keyCount,hasEvents);
            }

            getStopLatch().countDown();
        }
         protected void processKey(SelectionKey sk, NioSocketWrapper socketWrapper) {
    
      
            try {
    
      
                if (close) {
    
      
                    cancelledKey(sk, socketWrapper);
                } else if (sk.isValid() && socketWrapper != null) {
    
      
                    if (sk.isReadable() || sk.isWritable()) {
    
      
                        if (socketWrapper.getSendfileData() != null) {
    
      
                            processSendfile(sk, socketWrapper, false);
                        } else {
    
      
                            unreg(sk, socketWrapper, sk.readyOps());
                            boolean closeSocket = false;
                            // Read goes before write
                            if (sk.isReadable()) {
    
      
                                if (socketWrapper.readOperation != null) {
    
      
                                    if (!socketWrapper.readOperation.process()) {
    
      
                                        closeSocket = true;
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
    
      
                                    closeSocket = true;
                                }
                            }
                            if (!closeSocket && sk.isWritable()) {
    
      
                                if (socketWrapper.writeOperation != null) {
    
      
                                    if (!socketWrapper.writeOperation.process()) {
    
      
                                        closeSocket = true;
                                    }
                                } else if (!processSocket(socketWrapper, SocketEvent.OPEN_WRITE, true)) {
    
      
                                    closeSocket = true;
                                }
                            }
                            if (closeSocket) {
    
      
                                cancelledKey(sk, socketWrapper);
                            }
                        }
                    }
                } else {
    
      
                    // Invalid key
                    cancelledKey(sk, socketWrapper);
                }
            } catch (CancelledKeyException ckx) {
    
      
                cancelledKey(sk, socketWrapper);
            } catch (Throwable t) {
    
      
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("endpoint.nio.keyProcessingError"), t);
            }
        }

AbstractEndpoint

/**
     * Process the given SocketWrapper with the given status. Used to trigger
     * processing as if the Poller (for those endpoints that have one)
     * selected the socket.
     *
     * @param socketWrapper The socket wrapper to process
     * @param event         The socket event to be processed
     * @param dispatch      Should the processing be performed on a new
     *                          container thread
     *
     * @return if processing was triggered successfully
     */
    public boolean processSocket(SocketWrapperBase<S> socketWrapper,
            SocketEvent event, boolean dispatch) {
    
      
        try {
    
      
            if (socketWrapper == null) {
    
      
                return false;
            }
            SocketProcessorBase<S> sc = null;
            if (processorCache != null) {
    
      
                sc = processorCache.pop();
            }
            if (sc == null) {
    
      
                sc = createSocketProcessor(socketWrapper, event);
            } else {
    
      
                sc.reset(socketWrapper, event);
            }
            Executor executor = getExecutor();
            if (dispatch && executor != null) {
    
      
                executor.execute(sc);
            } else {
    
      
                sc.run();
            }
        } catch (RejectedExecutionException ree) {
    
      
            getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
            return false;
        } catch (Throwable t) {
    
      
            ExceptionUtils.handleThrowable(t);
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            getLog().error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

NioEndpoint


    @Override
    protected SocketProcessorBase<NioChannel> createSocketProcessor(
            SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
    
      
        return new SocketProcessor(socketWrapper, event);
    }

Executor 开启线程 执行 NioEndpoint.SocketProcessor.run

到这里,大家可以看出Tomcat 中的 NIO 使用,其实是对 Java NIO的封装。

Processor

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
    
      

        public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
    
      
            super(socketWrapper, event);
        }

        @Override
        protected void doRun() {
    
      
            NioChannel socket = socketWrapper.getSocket();
            Poller poller = NioEndpoint.this.poller;
            if (poller == null) {
    
      
                socketWrapper.close();
                return;
            }

            try {
    
      
                int handshake = -1;
                try {
    
      
                    if (socket.isHandshakeComplete()) {
    
      
                        // No TLS handshaking required. Let the handler
                        // process this socket / event combination.
                        handshake = 0;
                    } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                            event == SocketEvent.ERROR) {
    
      
                        // Unable to complete the TLS handshake. Treat it as
                        // if the handshake failed.
                        handshake = -1;
                    } else {
    
      
                        handshake = socket.handshake(event == SocketEvent.OPEN_READ, event == SocketEvent.OPEN_WRITE);
                        // The handshake process reads/writes from/to the
                        // socket. status may therefore be OPEN_WRITE once
                        // the handshake completes. However, the handshake
                        // happens when the socket is opened so the status
                        // must always be OPEN_READ after it completes. It
                        // is OK to always set this as it is only used if
                        // the handshake completes.
                        event = SocketEvent.OPEN_READ;
                    }
                } catch (IOException x) {
    
      
                    handshake = -1;
                    if (log.isDebugEnabled()) log.debug("Error during SSL handshake",x);
                } catch (CancelledKeyException ckx) {
    
      
                    handshake = -1;
                }
                if (handshake == 0) {
    
      
                    SocketState state = SocketState.OPEN;
                    // Process the request from this socket
                    if (event == null) {
    
      
                        state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                    } else {
    
      
                        state = getHandler().process(socketWrapper, event);
                    }
                    if (state == SocketState.CLOSED) {
    
      
                        poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);
                    }
                } else if (handshake == -1 ) {
    
      
                    getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
                    poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);
                } else if (handshake == SelectionKey.OP_READ){
    
      
                    socketWrapper.registerReadInterest();
                } else if (handshake == SelectionKey.OP_WRITE){
    
      
                    socketWrapper.registerWriteInterest();
                }
            } catch (CancelledKeyException cx) {
    
      
                poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);
            } catch (VirtualMachineError vme) {
    
      
                ExceptionUtils.handleThrowable(vme);
            } catch (Throwable t) {
    
      
                log.error(sm.getString("endpoint.processing.fail"), t);
                poller.cancelledKey(socket.getIOChannel().keyFor(poller.getSelector()), socketWrapper);
            } finally {
    
      
                socketWrapper = null;
                event = null;
                //return to cache
                if (running && !paused && processorCache != null) {
    
      
                    processorCache.push(this);
                }
            }
        }
    }

AbstractProtocol.ConnectionHandler.


        @Override
        public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
    
      
            if (getLog().isDebugEnabled()) {
    
      
                getLog().debug(sm.getString("abstractConnectionHandler.process",
                        wrapper.getSocket(), status));
            }
            if (wrapper == null) {
    
      
                // Nothing to do. Socket has been closed.
                return SocketState.CLOSED;
            }

            S socket = wrapper.getSocket();

            Processor processor = (Processor) wrapper.getCurrentProcessor();
            if (getLog().isDebugEnabled()) {
    
      
                getLog().debug(sm.getString("abstractConnectionHandler.connectionsGet",
                        processor, socket));
            }

            // Timeouts are calculated on a dedicated thread and then
            // dispatched. Because of delays in the dispatch process, the
            // timeout may no longer be required. Check here and avoid
            // unnecessary processing.
            if (SocketEvent.TIMEOUT == status &&
                    (processor == null ||
                    !processor.isAsync() && !processor.isUpgrade() ||
                    processor.isAsync() && !processor.checkAsyncTimeoutGeneration())) {
    
      
                // This is effectively a NO-OP
                return SocketState.OPEN;
            }

            if (processor != null) {
    
      
                // Make sure an async timeout doesn't fire
                getProtocol().removeWaitingProcessor(processor);
            } else if (status == SocketEvent.DISCONNECT || status == SocketEvent.ERROR) {
    
      
                // Nothing to do. Endpoint requested a close and there is no
                // longer a processor associated with this socket.
                return SocketState.CLOSED;
            }

            ContainerThreadMarker.set();

            try {
    
      
                if (processor == null) {
    
      
                    String negotiatedProtocol = wrapper.getNegotiatedProtocol();
                    // OpenSSL typically returns null whereas JSSE typically
                    // returns "" when no protocol is negotiated
                    if (negotiatedProtocol != null && negotiatedProtocol.length() > 0) {
    
      
                        UpgradeProtocol upgradeProtocol = getProtocol().getNegotiatedProtocol(negotiatedProtocol);
                        if (upgradeProtocol != null) {
    
      
                            processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
                            if (getLog().isDebugEnabled()) {
    
      
                                getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
                            }
                        } else if (negotiatedProtocol.equals("http/1.1")) {
    
      
                            // Explicitly negotiated the default protocol.
                            // Obtain a processor below.
                        } else {
    
      
                            // TODO:
                            // OpenSSL 1.0.2's ALPN callback doesn't support
                            // failing the handshake with an error if no
                            // protocol can be negotiated. Therefore, we need to
                            // fail the connection here. Once this is fixed,
                            // replace the code below with the commented out
                            // block.
                            if (getLog().isDebugEnabled()) {
    
      
                                getLog().debug(sm.getString("abstractConnectionHandler.negotiatedProcessor.fail",
                                        negotiatedProtocol));
                            }
                            return SocketState.CLOSED;
                            /*
                             * To replace the code above once OpenSSL 1.1.0 is
                             * used.
                            // Failed to create processor. This is a bug.
                            throw new IllegalStateException(sm.getString(
                                    "abstractConnectionHandler.negotiatedProcessor.fail",
                                    negotiatedProtocol));
                            */
                        }
                    }
                }
                if (processor == null) {
    
      
                    processor = recycledProcessors.pop();
                    if (getLog().isDebugEnabled()) {
    
      
                        getLog().debug(sm.getString("abstractConnectionHandler.processorPop", processor));
                    }
                }
                if (processor == null) {
    
      
                    processor = getProtocol().createProcessor();
                    register(processor);
                    if (getLog().isDebugEnabled()) {
    
      
                        getLog().debug(sm.getString("abstractConnectionHandler.processorCreate", processor));
                    }
                }

                processor.setSslSupport(
                        wrapper.getSslSupport(getProtocol().getClientCertProvider()));

                // Associate the processor with the connection
                wrapper.setCurrentProcessor(processor);

                SocketState state = SocketState.CLOSED;
                do {
    
      
                    state = processor.process(wrapper, status);

                    if (state == SocketState.UPGRADING) {
    
      
                        // Get the HTTP upgrade handler
                        UpgradeToken upgradeToken = processor.getUpgradeToken();
                        // Retrieve leftover input
                        ByteBuffer leftOverInput = processor.getLeftoverInput();
                        if (upgradeToken == null) {
    
      
                            // Assume direct HTTP/2 connection
                            UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
                            if (upgradeProtocol != null) {
    
      
                                processor = upgradeProtocol.getProcessor(
                                        wrapper, getProtocol().getAdapter());
                                wrapper.unRead(leftOverInput);
                                // Associate with the processor with the connection
                                wrapper.setCurrentProcessor(processor);
                            } else {
    
      
                                if (getLog().isDebugEnabled()) {
    
      
                                    getLog().debug(sm.getString(
                                        "abstractConnectionHandler.negotiatedProcessor.fail",
                                        "h2c"));
                                }
                                return SocketState.CLOSED;
                            }
                        } else {
    
      
                            HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                            // Release the Http11 processor to be re-used
                            release(processor);
                            // Create the upgrade processor
                            processor = getProtocol().createUpgradeProcessor(wrapper, upgradeToken);
                            if (getLog().isDebugEnabled()) {
    
      
                                getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
                                        processor, wrapper));
                            }
                            wrapper.unRead(leftOverInput);
                            // Mark the connection as upgraded
                            wrapper.setUpgraded(true);
                            // Associate with the processor with the connection
                            wrapper.setCurrentProcessor(processor);
                            // Initialise the upgrade handler (which may trigger
                            // some IO using the new protocol which is why the lines
                            // above are necessary)
                            // This cast should be safe. If it fails the error
                            // handling for the surrounding try/catch will deal with
                            // it.
                            if (upgradeToken.getInstanceManager() == null) {
    
      
                                httpUpgradeHandler.init((WebConnection) processor);
                            } else {
    
      
                                ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
                                try {
    
      
                                    httpUpgradeHandler.init((WebConnection) processor);
                                } finally {
    
      
                                    upgradeToken.getContextBind().unbind(false, oldCL);
                                }
                            }
                            if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) {
    
      
                                if (((InternalHttpUpgradeHandler) httpUpgradeHandler).hasAsyncIO()) {
    
      
                                    // The handler will initiate all further I/O
                                    state = SocketState.LONG;
                                }
                            }
                        }
                    }
                } while ( state == SocketState.UPGRADING);

                if (state == SocketState.LONG) {
    
      
                    // In the middle of processing a request/response. Keep the
                    // socket associated with the processor. Exact requirements
                    // depend on type of long poll
                    longPoll(wrapper, processor);
                    if (processor.isAsync()) {
    
      
                        getProtocol().addWaitingProcessor(processor);
                    }
                } else if (state == SocketState.OPEN) {
    
      
                    // In keep-alive but between requests. OK to recycle
                    // processor. Continue to poll for the next request.
                    wrapper.setCurrentProcessor(null);
                    release(processor);
                    wrapper.registerReadInterest();
                } else if (state == SocketState.SENDFILE) {
    
      
                    // Sendfile in progress. If it fails, the socket will be
                    // closed. If it works, the socket either be added to the
                    // poller (or equivalent) to await more data or processed
                    // if there are any pipe-lined requests remaining.
                } else if (state == SocketState.UPGRADED) {
    
      
                    // Don't add sockets back to the poller if this was a
                    // non-blocking write otherwise the poller may trigger
                    // multiple read events which may lead to thread starvation
                    // in the connector. The write() method will add this socket
                    // to the poller if necessary.
                    if (status != SocketEvent.OPEN_WRITE) {
    
      
                        longPoll(wrapper, processor);
                        getProtocol().addWaitingProcessor(processor);
                    }
                } else if (state == SocketState.SUSPENDED) {
    
      
                    // Don't add sockets back to the poller.
                    // The resumeProcessing() method will add this socket
                    // to the poller.
                } else {
    
      
                    // Connection closed. OK to recycle the processor.
                    // Processors handling upgrades require additional clean-up
                    // before release.
                    wrapper.setCurrentProcessor(null);
                    if (processor.isUpgrade()) {
    
      
                        UpgradeToken upgradeToken = processor.getUpgradeToken();
                        HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
                        InstanceManager instanceManager = upgradeToken.getInstanceManager();
                        if (instanceManager == null) {
    
      
                            httpUpgradeHandler.destroy();
                        } else {
    
      
                            ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null);
                            try {
    
      
                                httpUpgradeHandler.destroy();
                            } finally {
    
      
                                try {
    
      
                                    instanceManager.destroyInstance(httpUpgradeHandler);
                                } catch (Throwable e) {
    
      
                                    ExceptionUtils.handleThrowable(e);
                                    getLog().error(sm.getString("abstractConnectionHandler.error"), e);
                                }
                                upgradeToken.getContextBind().unbind(false, oldCL);
                            }
                        }
                    }
                    release(processor);
                }
                return state;
            } catch(java.net.SocketException e) {
    
      
                // SocketExceptions are normal
                getLog().debug(sm.getString(
                        "abstractConnectionHandler.socketexception.debug"), e);
            } catch (java.io.IOException e) {
    
      
                // IOExceptions are normal
                getLog().debug(sm.getString(
                        "abstractConnectionHandler.ioexception.debug"), e);
            } catch (ProtocolException e) {
    
      
                // Protocol exceptions normally mean the client sent invalid or
                // incomplete data.
                getLog().debug(sm.getString(
                        "abstractConnectionHandler.protocolexception.debug"), e);
            }
            // Future developers: if you discover any other
            // rare-but-nonfatal exceptions, catch them here, and log as
            // above.
            catch (OutOfMemoryError oome) {
    
      
                // Try and handle this here to give Tomcat a chance to close the
                // connection and prevent clients waiting until they time out.
                // Worst case, it isn't recoverable and the attempt at logging
                // will trigger another OOME.
                getLog().error(sm.getString("abstractConnectionHandler.oome"), oome);
            } catch (Throwable e) {
    
      
                ExceptionUtils.handleThrowable(e);
                // any other exception or error is odd. Here we log it
                // with "ERROR" level, so it will show up even on
                // less-than-verbose logs.
                getLog().error(sm.getString("abstractConnectionHandler.error"), e);
            } finally {
    
      
                ContainerThreadMarker.clear();
            }

            // Make sure socket/processor is removed from the list of current
            // connections
            wrapper.setCurrentProcessor(null);
            release(processor);
            return SocketState.CLOSED;
        }

Adapter

CoyoteAdapter

container

先到这里,发现贴些源代码不如借用一张图更能表现组件之间的调用关系。
在这里插入图片描述

后面重新整理一下,准备拆成四部分:

  • Springboot 怎么实现tomcat自定义属性
  • tomact 组件间启动流程
  • endpiont 中如何使用 Java NIO的
  • container中如何处理逻辑