SpringBoot如何内置tomcat

我们知道,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
SpringBoot如何内置tomcat_第1张图片

再回到前面,看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.
     * 

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,

SpringBoot如何内置tomcat_第2张图片

它是什么时候加入bean工厂的呢?通过搜索得知它是在ServletWebServerFactoryConfiguration中被注册的:
SpringBoot如何内置tomcat_第3张图片

我们再看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... initializerTypes) {
        this.initializers = new LinkedMultiValueMap<>();
        this.initializerTypes = (initializerTypes.length != 0)
                ? Arrays.asList(initializerTypes)
                : Collections.singletonList(ServletContextInitializer.class);
        // 这里实例化了dispatchServlet
        addServletContextInitializerBeans(beanFactory);
        addAdaptableBeans(beanFactory);
        List 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 initializerBean : getOrderedBeansOfType(
                beanFactory, ServletContextInitializer.class)) {
            addServletContextInitializerBean(initializerBean.getKey(),
                    initializerBean.getValue(), beanFactory);
        }
    }

SpringBoot如何内置tomcat_第4张图片

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

@Override
    public void onStartup(Set> 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实现:
SpringBoot如何内置tomcat_第5张图片

可以看到,这里将dispatchServlet注册进了tomcat的servlet。

你可能感兴趣的:(java)