关于java:SpringBoot如何内置tomcat

52次阅读

共计 14287 个字符,预计需要花费 36 分钟才能阅读完成。

咱们晓得,springBoot 我的项目只需运行 main 函数即可,那么 tomcat 是怎么启动的,springmvc 又是如何和 tomcat 绑定的呢?

1. 如何启动 tomcat

从 SpringBoot 主类 run 办法进入,看 context = createApplicationContext();

    /**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {long startTime = System.nanoTime();
        DefaultBootstrapContext bootstrapContext = createBootstrapContext();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);
        try {ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            // 创立 web 环境
            context = createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);
            prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
            if (this.logStartupInfo) {new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
            }
            listeners.started(context, timeTakenToStartup);
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }
        try {Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
            listeners.ready(context, timeTakenToReady);
        }
        catch (Throwable ex) {handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }


    /**
     * Strategy method used to create the {@link ApplicationContext}. By default this
     * method will respect any explicitly set application context class or factory before
     * falling back to a suitable default.
     * @return the application context (not yet refreshed)
     * @see #setApplicationContextFactory(ApplicationContextFactory)
     */
    protected ConfigurableApplicationContext createApplicationContext() {return this.applicationContextFactory.create(this.webApplicationType);
    }


    private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;

@FunctionalInterface
public interface ApplicationContextFactory {

    /**
     * A default {@link ApplicationContextFactory} implementation that will create an
     * appropriate context for the {@link WebApplicationType}.
     */
    ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            // 从 spring.factories 中获取 ApplicationFactory 的实现类
            for (ApplicationContextFactory candidate : SpringFactoriesLoader
                    .loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
             // 获取到 AnnotationConfigServletWebServerApplicationContext 对象
                ConfiguraAnnotationConfigServletWebServerApplicationContext 对象 bleApplicationContext context = candidate.create(webApplicationType);
                if (context != null) {return context;}
            }
            return new AnnotationConfigApplicationContext();}
        catch (Exception ex) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance,"
                    + "you may need a custom ApplicationContextFactory", ex);
        }
    };

能够看到 ApplicationContextFactory 是一个函数式接口,那么 applicationContextFactory.create 的时候会调用 lambda 表达式,lamdba 表达式里从 spring.factories 中获取 ApplicationFactory 的实现类,那么 candidate.create(webApplicationType) 只能获取到 AnnotationConfigServletWebServerApplicationContext

再回到后面,看 refreshContext(context)->refresh(context)->applicationContext.refresh();;
AnnotationConfigServletWebServerApplicationContext 没有 refresh 办法,调用的是父类 ServletWebServerApplicationContext 的 refresh 办法:

    @Override
    public final void refresh() throws BeansException, IllegalStateException {
        try {super.refresh();
        }
        catch (RuntimeException ex) {
            WebServer webServer = this.webServer;
            if (webServer != null) {webServer.stop();
            }
            throw ex;
        }
    }

ServletWebServerApplicationContext 调用的也是父类 AbstractApplicationContext 的 refresh 办法:

    @Override
    public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();}

            catch (BeansException ex) {if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization -" +
                            "cancelling refresh attempt:" + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
                contextRefresh.end();}
        }
    }

看 onRefresh() 办法:

    /**
     * Template method which can be overridden to add context-specific refresh work.
     * Called on initialization of special beans, before instantiation of singletons.
     * <p>This implementation is empty.
     * @throws BeansException in case of errors
     * @see #refresh()
     */
    protected void onRefresh() throws BeansException {// For subclasses: do nothing by default.}

空办法,交由子类实现,也就是 ServletWebServerApplicationContext:

    @Override
    protected void onRefresh() {
      // 父类办法初始化了 web 我的项目的 ui 主题
        super.onRefresh();
        try {
        // 创立 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) {
        // 创立一个 DefaultStartupStep
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            // 获取 TomcatServletWebServerFactory
            ServletWebServerFactory factory = getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            // 获取 TomcatWebServer
            this.webServer = factory.getWebServer(getSelfInitializer());
            createWebServer.end();
            getBeanFactory().registerSingleton("webServerGracefulShutdown",
                    new WebServerGracefulShutdownLifecycle(this.webServer));
            getBeanFactory().registerSingleton("webServerStartStop",
                    new WebServerStartStopLifecycle(this, this.webServer));
        }
        else if (servletContext != null) {
            try {getSelfInitializer().onStartup(servletContext);
            }
            catch (ServletException ex) {throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        initPropertySources();}

首先看 getWebServerFactory 是如何获取 TomcatServletWebServerFactory 的:

    /**
     * Returns the {@link ServletWebServerFactory} that should be used to create the
     * embedded {@link WebServer}. By default this method searches for a suitable bean in
     * the context itself.
     * @return a {@link ServletWebServerFactory} (never {@code null})
     */
    protected ServletWebServerFactory getWebServerFactory() {
        // Use bean names so that we don't consider the hierarchy
   // 从 beanFactory 获取 ServletWebServerFactory 的名称
        String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
// 只能有一个
        if (beanNames.length == 0) {throw new MissingWebServerFactoryBeanException(getClass(), ServletWebServerFactory.class,
                    WebApplicationType.SERVLET);
        }
        if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple"
                    + "ServletWebServerFactory beans :" + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
// 获取该 beanFactory
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }

能够看到 ServletWebServerFactory 的实现类有很多个,springBoot 默认的实现是 TomcatServletWebServerFactory,

它是什么时候退出 bean 工厂的呢?通过搜寻得悉它是在 ServletWebServerFactoryConfiguration 中被注册的:

咱们再看 this.webServer = factory.getWebServer(getSelfInitializer());

    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {if (this.disableMBeanRegistry) {Registry.disableRegistry();
        }
        Tomcat tomcat = new Tomcat();
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        for (LifecycleListener listener : this.serverLifecycleListeners) {tomcat.getServer().addLifecycleListener(listener);
        }
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        configureEngine(tomcat.getEngine());
        for (Connector additionalConnector : this.additionalTomcatConnectors) {tomcat.getService().addConnector(additionalConnector);
        }
        prepareContext(tomcat.getHost(), initializers);
        return getTomcatWebServer(tomcat);
    }



    /**
     * Factory method called to create the {@link TomcatWebServer}. Subclasses can
     * override this method to return a different {@link TomcatWebServer} or apply
     * additional processing to the Tomcat server.
     * @param tomcat the Tomcat server.
     * @return a new {@link TomcatWebServer} instance
     */
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {return new TomcatWebServer(tomcat, getPort() >= 0);
    }


    /**
     * Create a new {@link TomcatWebServer} instance.
     * @param tomcat the underlying Tomcat server
     * @param autoStart if the server should be started
     */
    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
                // 启动 tomcat
                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);
            }
        }
    }

初始化了一个 tomcat 并启动。

2.tomcat 如何注册 DispatchServlet

回到 this.webServer = factory.getWebServer(getSelfInitializer());
这行代码,看 getSelfInitializer();

    /**
     * Returns the {@link ServletContextInitializer} that will be used to complete the
     * setup of this {@link WebApplicationContext}.
     * @return the self initializer
     * @see #prepareWebApplicationContext(ServletContext)
     */
    private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {return this::selfInitialize;}

    private void selfInitialize(ServletContext servletContext) throws ServletException {prepareWebApplicationContext(servletContext);
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(beanFactory);
        WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
                getServletContext());
        existingScopes.restore();
        WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
                getServletContext());
        // 获取 ServletContextInitializer 实现类
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
// 注册 dispatchServlet
            beans.onStartup(servletContext);
        }
    }

    @SafeVarargs
    public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
            Class<? extends ServletContextInitializer>... initializerTypes) {this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0)
                ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        // 这里实例化了 dispatchServlet
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List<ServletContextInitializer> sortedInitializers = this.initializers.values()
                .stream()
                .flatMap((value) -> value.stream()
                        .sorted(AnnotationAwareOrderComparator.INSTANCE))
                .collect(Collectors.toList());
        this.sortedList = Collections.unmodifiableList(sortedInitializers);
    }


    private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
// 实例化 ServletContextInitializer 的实现类
        for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)) {addServletContextInitializerBean(initializerBean.getKey(),
                    initializerBean.getValue(), beanFactory);
        }
    }

ServletContextInitializer 的实现类中有个 dispatcherServletRegistration,就是讲 dispatchServlet 注册到 tomcat 的;
再回到 initializer.onStartup 办法:

@Override
    public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
            throws ServletException {
        try {for (ServletContextInitializer initializer : this.initializers) {
              // 注册 dispatchServlet
                initializer.onStartup(servletContext);
            }
        }
        catch (Exception ex) {
            this.startUpException = ex;
            // Prevent Tomcat from logging and re-throwing when we know we can
            // deal with it in the main thread, but log for information here.
            if (logger.isErrorEnabled()) {
                logger.error("Error starting Tomcat context. Exception:"
                        + ex.getClass().getName() + ". Message:" + ex.getMessage());
            }
        }
    }

这里的 initializer 就是 dispatcherServletRegistration,然而他没有 onStartup 办法,它调用的是它的父类 RegistrationBean 这个实现类:

    @Override
    public final void onStartup(ServletContext servletContext) throws ServletException {String description = getDescription();
        if (!isEnabled()) {logger.info(StringUtils.capitalize(description) + "was not registered (disabled)");
            return;
        }
         // 注册
        register(description, servletContext);
    }

由子类 DynamicRegistrationBean 实现:

    @Override
    protected final void register(String description, ServletContext servletContext) {
           // 注册
        D registration = addRegistration(description, servletContext);
        if (registration == null) {logger.info(StringUtils.capitalize(description) + "was not registered (possibly already registered?)");
            return;
        }
        configure(registration);
    }

addRegistration 由子类 ServletRegistrationBean 实现:

能够看到,这里将 dispatchServlet 注册进了 tomcat 的 servlet。

正文完
 0