SpringBoot源码分析(三) -- Tomcat启动流程

1,009 阅读3分钟

SpringBoot源码分析(三) -- Tomcat启动流程

onRefresh 方法

//这段代码位置在:org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
@Override
protected void onRefresh() {
    //在父类的onRefresh方法中初始化了主题资源
    super.onRefresh();
    try {
        //创建web服务
        createWebServer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start web server", ex);
    }
}

createWebServer 方法

//这段代码位置在:org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#createWebServer
private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    //第一次进来,webServer和servletContext都为null
    if (webServer == null && servletContext == null) {
        //getWebServerFactory方法直接从beanFactory中获取ServletWebServerFactory类
        //getWebServerFactory方法会创建DispatcherServlet对象,并添加到beanFactory中去,对应的beanName为dispatcherServlet
        ServletWebServerFactory factory = getWebServerFactory();
        //这个方法为wrapper设置了servletClass为DispatcherServlet
        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();
}

//获取WebServerFactory
protected ServletWebServerFactory getWebServerFactory() {
    // Use bean names so that we don't consider the hierarchy
    String[] beanNames = getBeanFactory()
        .getBeanNamesForType(ServletWebServerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException(
            "Unable to start ServletWebServerApplicationContext due to missing "
            + "ServletWebServerFactory bean.");
    }
    if (beanNames.length > 1) {
        throw new ApplicationContextException(
            "Unable to start ServletWebServerApplicationContext due to multiple "
            + "ServletWebServerFactory beans : "
            + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}


//获取ServletContextInitializer
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    return this::selfInitialize;
}

getWebServer 方法

//这段代码位置在:org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
public WebServer getWebServer(ServletContextInitializer... initializers) {
    //创建tomcat,直接new出来的
    Tomcat tomcat = new Tomcat();
    //设置工作目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory
        : createTempDir("tomcat");
    //设置安装目录
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    //初始化tomcat的连接器
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    //设置自动部署为false
    tomcat.getHost().setAutoDeploy(false);
    //配置引擎
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    //准备context,这个重要,跟进去
    prepareContext(tomcat.getHost(), initializers);
    return getTomcatWebServer(tomcat);
}

//初始化链接器
protected void customizeConnector(Connector connector) {
    int port = (getPort() >= 0) ? getPort() : 0;
    connector.setPort(port);
    if (StringUtils.hasText(this.getServerHeader())) {
        connector.setAttribute("server", this.getServerHeader());
    }
    if (connector.getProtocolHandler() instanceof AbstractProtocol) {
        customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
    }
    if (getUriEncoding() != null) {
        connector.setURIEncoding(getUriEncoding().name());
    }
    // Don't bind to the socket prematurely if ApplicationContext is slow to start
    connector.setProperty("bindOnInit", "false");
    if (getSsl() != null && getSsl().isEnabled()) {
        customizeSsl(connector);
    }
    TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(
        getCompression());
    compression.customize(connector);
    for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
        customizer.customize(connector);
    }
}

//配置引擎,添加阀
private void configureEngine(Engine engine) {
    engine.setBackgroundProcessorDelay(this.backgroundProcessorDelay);
    for (Valve valve : this.engineValves) {
        engine.getPipeline().addValve(valve);
    }
}

prepareContext 方法

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    //这里为null
    File documentRoot = getValidDocumentRoot();
    //创建TomcatEmbeddedContext对象
    TomcatEmbeddedContext context = new TomcatEmbeddedContext();
    if (documentRoot != null) {
        context.setResources(new LoaderHidingResourceRoot(context));
    }
    //为TomcatEmbeddedContext对象设置属性
    context.setName(getContextPath());
    context.setDisplayName(getDisplayName());
    context.setPath(getContextPath());
    File docBase = (documentRoot != null) ? documentRoot
        : createTempDir("tomcat-docbase");
    context.setDocBase(docBase.getAbsolutePath());
    context.addLifecycleListener(new FixContextListener());
    context.setParentClassLoader(
        (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
        : ClassUtils.getDefaultClassLoader());
    resetDefaultLocaleMapping(context);
    addLocaleMappings(context);
    context.setUseRelativeRedirects(false);
    configureTldSkipPatterns(context);
    WebappLoader loader = new WebappLoader(context.getParentClassLoader());
    loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    loader.setDelegate(true);
    context.setLoader(loader);
    if (isRegisterDefaultServlet()) {
        //这里添加默认的servlet
        addDefaultServlet(context);
    }
    if (shouldRegisterJspServlet()) {
        addJspServlet(context);
        addJasperInitializer(context);
    }
    context.addLifecycleListener(new StaticResourceConfigurer(context));
    ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    //把context添加到host中
    host.addChild(context);
    //配置tomcat
    configureContext(context, initializersToUse);
    //空实现,等以后子类实现
    postProcessContext(context);
}

//添加默认的servlet
private void addDefaultServlet(Context context) {
	//创建wrapper对象,servlet包裹在Wrapper对象中,详见tomcat源码系列
    Wrapper defaultServlet = context.createWrapper();
    defaultServlet.setName("default");
    //设置servlet的全路径,这里用的是DefaultServlet。最后会通过反射创建servlet类
    defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
    defaultServlet.addInitParameter("debug", "0");
    defaultServlet.addInitParameter("listings", "false");
    defaultServlet.setLoadOnStartup(1);
    // Otherwise the default location of a Spring DispatcherServlet cannot be set
    defaultServlet.setOverridable(true);
    //把wrapper添加到context中去
    context.addChild(defaultServlet);
    //添加servlet映射
    context.addServletMappingDecoded("/", "default");
}


//配置tomcat
protected void configureContext(Context context,
                                ServletContextInitializer[] initializers) {
    //创建一个TomcatStarter类,这个类是为了初始化时在它的onStartup方法中调用ServletContextInitializer
    //它的内部维护了一个ServletContextInitializer的数组,构造方法初始化了这个数组
    //onStartup方法中遍历执行数组
    TomcatStarter starter = new TomcatStarter(initializers);
    if (context instanceof TomcatEmbeddedContext) {
        TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
        embeddedContext.setStarter(starter);
        embeddedContext.setFailCtxIfServletStartFails(true);
    }
    context.addServletContainerInitializer(starter, NO_CLASSES);
    for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
        context.addLifecycleListener(lifecycleListener);
    }
    //在context的pipeline中添加valve
    for (Valve valve : this.contextValves) {
        context.getPipeline().addValve(valve);
    }
    //配置错误页面
    for (ErrorPage errorPage : getErrorPages()) {
        new TomcatErrorPage(errorPage).addToContext(context);
    }
    //配置mime映射
    for (MimeMappings.Mapping mapping : getMimeMappings()) {
        context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
    }
    //配置session
    configureSession(context);
    for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
        customizer.customize(context);
    }
}

getTomcatWebServer 方法

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
    //直接创建了一个TomcatWebServer类
    return new TomcatWebServer(tomcat, getPort() >= 0);
}

TomcatWebServer 类

//构造方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
    Assert.notNull(tomcat, "Tomcat Server must not be null");
    //维护了一个tomcat的实例
    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
            //启动tomcat
            //这里面会为Wrapper设置servletClass为dispatcherServlet,详见流程图
            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();
            throw new WebServerException("Unable to start embedded Tomcat", ex);
        }
    }
}

DispatcherServlet 的创建

//当前方法位置在:org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration.DispatcherServletConfiguration#dispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet() {
    //创建DispatcherServlet
    DispatcherServlet dispatcherServlet = new DispatcherServlet();
    dispatcherServlet.setDispatchOptionsRequest(
        this.webMvcProperties.isDispatchOptionsRequest());
    dispatcherServlet.setDispatchTraceRequest(
        this.webMvcProperties.isDispatchTraceRequest());
    dispatcherServlet.setThrowExceptionIfNoHandlerFound(
        this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
    dispatcherServlet.setEnableLoggingRequestDetails(
        this.httpProperties.isLogRequestDetails());
    return dispatcherServlet;
}

注意:DispatcherServlet的初始化在第一次请求访问时进行初始化