开源框架-SpringBoot系列

SpringBoot内嵌Servlet容器启动过程

2020-03-04  本文已影响0人  一根线条

我们在启动springboot应用的时候基本都是从以下的代码开始

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

下面是SpringApplication中定义的run方法:

public class SpringApplication {
    public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        //调用下面这个静态run方法
        return run(new Class<?>[] { primarySource }, args);
    }

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        //创建实例并执行run方法
        return new SpringApplication(primarySources).run(args);
    }
    ... ...
}

可见其实例化了一个SpringApplication实例,同时执行其run方法。其执行的构造方法如下:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //就是SpringApplication.run(Application.class, args)中的第一个参数
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //根据classpath中存在的类型进行判断是reactive还是普通的web应用程序
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //从META-INF/spring.factories查找ApplicationContextInitializer类型的配置,
    //然后通过反射将其实例化保存到initializers变量中
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //推导出主要应用类并加载(也就是我们执行main方法的那个类)
    this.mainApplicationClass = deduceMainApplicationClass();
}

接着便开始执行SpringApplication实例的run方法

    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);
            //根据所在环境创建对应的应用上下文
            //【AnnotationConfigServletWebServerApplicationContext】
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            //重点:刷新上下文,通过调用context的refresh()方法,真正开始初始化Context容器,
            //同时在onRefresh阶段便开始创建和启动servlet容器了,
            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;
    }

对于Servlet Web应用程序,其实例化的上下文AnnotationConfigServletWebServerApplicationContext在初始化的onRefresh阶段会通过以下的createWebServer方法来创建Servlet容器


一:创建WebServer

AnnotationConfigServletWebServerApplicationContext
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {

    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            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) {
            //获取用于构建WebServer的工厂类
            ServletWebServerFactory factory = getWebServerFactory();

            //从BeanFactory中得到所有的ServletContextInitializer并作为参数来创建WebServer,
            //等调用WebServer的onStartup方法时对ServletContext进行装配。
            //通过这种方法就能完成拦截器、过滤器的装配工作。
            //在finishRefresh阶段将会启动WebServer(见下面的方法)
            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();
    }

    protected ServletWebServerFactory getWebServerFactory() {
        // Use bean names so that we don't consider the hierarchy
        //从BeanFactory中得到ServletWebServerFactory类型的Bean
        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的实现类,其onStartup方法的实现为先的selfInitialize方法
    private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
        return this::selfInitialize;
    }

    //Tomcat启动过程中的回调方法
    private void selfInitialize(ServletContext servletContext) throws ServletException {
        prepareWebApplicationContext(servletContext);
        registerApplicationScope(servletContext);
        WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
            beans.onStartup(servletContext);
        }
    }
    //启动WebServer
    @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;
    }
}
ServletContextInitializer

从上面来看,从BeanFactory中得到ServletWebServerFactory类型的Bean是关键,那么什么时候在哪里完成了ServletWebServerFactory类型实例的注册工作呢?我们从SpringBoot的自动化配置中来寻找。


二:创建ServletWebServerFactory类型Bean

首先看下TomcatServletWebServerFactory的继承关系


TomcatServletWebServerFactory

spring-boot-autoconfigure-2.2.1/META-INF/spring.factories中有以下配置:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration

ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
//收集通过“server.”开始配置的参数
@EnableConfigurationProperties(ServerProperties.class)
//导入了三种服务器Tomcat、Jetty和Undertow的配置
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {

    //用于给ConfigurableServletWebServerFactory进行配置
    @Bean
    public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
        return new ServletWebServerFactoryCustomizer(serverProperties);
    }

    //用于给TomcatServletWebServerFactory进行参数的定制
    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
            ServerProperties serverProperties) {
        return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    }
    ... ...
}

这里有个ServerProperties对象值得我们重点关注,里面有个ErrorProperties属性定义了错误处理。同时可进一步阅读
org.springframework.boot.web.servlet.support.ErrorPageFilterConfiguration、
org.springframework.boot.web.servlet.support.ErrorPageFilter、
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration类中对请求过程中的错误处理

ServletWebServerFactoryConfiguration

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {

    //Tomcat的配置
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatServletWebServerFactory tomcatServletWebServerFactory(
                ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
                ObjectProvider<TomcatContextCustomizer> contextCustomizers,
                ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers()
                    .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers()
                    .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers()
                    .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }
}

在这里便生成了ServletWebServerFactory类型的TomcatServletWebServerFactory 实例。

public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
        implements ConfigurableTomcatWebServerFactory, ResourceLoaderAware {
    
    @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());
        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);
        }
        //往下走,创建该Host下面的Context
        prepareContext(tomcat.getHost(), initializers);
        //创建并返回Server
        return getTomcatWebServer(tomcat);
    }

    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        return new TomcatWebServer(tomcat, getPort() >= 0);
    }

    protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = getValidDocumentRoot();
        TomcatEmbeddedContext context = new TomcatEmbeddedContext();
        if (documentRoot != null) {
            context.setResources(new LoaderHidingResourceRoot(context));
        }
        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);
        try {
            context.setCreateUploadTargets(true);
        }
        catch (NoSuchMethodError ex) {
            // Tomcat is < 8.5.39. Continue.
        }
        configureTldSkipPatterns(context);
        WebappLoader loader = new WebappLoader(context.getParentClassLoader());
        loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
        loader.setDelegate(true);
        context.setLoader(loader);
        if (isRegisterDefaultServlet()) {
            addDefaultServlet(context);
        }
        if (shouldRegisterJspServlet()) {
            addJspServlet(context);
            addJasperInitializer(context);
        }
        context.addLifecycleListener(new StaticResourceConfigurer(context));
        ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
        //在Host下面添加应用的Context
        host.addChild(context);
        //装配Tomcat的上下文
        configureContext(context, initializersToUse);
        postProcessContext(context);
    }

    protected void configureContext(Context context, ServletContextInitializer[] initializers) {
        //继续下面的TomcatStarter 
        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);
        }
        for (Valve valve : this.contextValves) {
            context.getPipeline().addValve(valve);
        }
        for (ErrorPage errorPage : getErrorPages()) {
            org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
            tomcatErrorPage.setLocation(errorPage.getPath());
            tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
            tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
            context.addErrorPage(tomcatErrorPage);
        }
        for (MimeMappings.Mapping mapping : getMimeMappings()) {
            context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
        }
        configureSession(context);
        new DisableReferenceClearingContextCustomizer().customize(context);
        for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
            customizer.customize(context);
        }
    }
}

应用ServletContextInitializer

class TomcatStarter implements ServletContainerInitializer {

    private static final Log logger = LogFactory.getLog(TomcatStarter.class);

    private final ServletContextInitializer[] initializers;

    private volatile Exception startUpException;

    TomcatStarter(ServletContextInitializer[] initializers) {
        this.initializers = initializers;
    }

    @Override
    public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
        try {
            for (ServletContextInitializer initializer : this.initializers) {
                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());
            }
        }
    }
}

SpringBoot应用程序启动过程小结

1,执行SpringApplication的静态run方法

创建SpringApplication的实例,执行无参构造方法,主要做了一下几个事:

2,执行SpringApplication实例的非静态run方法

1)根据应用程序类型创建应用上下文

2)执行SpringApplication的refresh()方法

该方法会执行context的refresh()方法,然后开始真正构建BeanFactory


启动Servlet容器
上一篇下一篇

猜你喜欢

热点阅读