SpringBoot(一)内嵌tomcat原理和DispatcherServlet配置原理

Springboot使用起来很简单,在pom中引入如下依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.3</version>
  </parent>
<dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
</dependencies>

其实就可以起来一个web服务,可以写controller了。

 启动入口也很简单。

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

 

对于启动流程来说,主要看SpringApplication.run方法了。

先挑着看,具体每步做什么,在启动流程中分析。

1:看创建的容器   public ConfigurableApplicationContext run(String... args) {

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
        ConfigurableApplicationContext context = null;
        this.configureHeadlessProperty();
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting(bootstrapContext, this.mainApplicationClass);

        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
// 创建的容器,分为三种 AnnotationConfigServletWebServerApplicationContext AnnotationConfigReactiveWebServerApplicationContext
// AnnotationConfigApplicationContext
context
= this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 在这里启动容器
this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }

1.1创建容器:

 protected ConfigurableApplicationContext createApplicationContext() {
        return this.applicationContextFactory.create(this.webApplicationType);
    }

 

使用默认的容器工厂:是一个函数式接口。

 

 

 会根据是servlet编程还是响应式编程来创建容器。

我们这里是servlet编程,使用 AnnotationConfigServletWebServerApplicationContext容器

 

 

 

2:启动容器 this.refreshContext(context);

 private void refreshContext(ConfigurableApplicationContext context) {
        if (this.registerShutdownHook) {
            shutdownHook.registerApplicationContext(context);
        }

        this.refresh(context);
    }
protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();
    }

 

是调用AnnotationConfigServletWebServerApplicationContext 中的refresh()方法,这是spring启动过程中的主要方法。

这个refresh()方法是在父类 AbstractApplicationContext 中的

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
// 这里是spring提供的一个扩展点,在父类中是个空方法,要由子类重写,这里就是springboot中的容器重写了
this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }

 

AnnotationConfigServletWebServerApplicationContext 的父类中ServletWebServerApplicationContext重写了onResresh()方法

ServletWebServerApplicationContext#onResresh()

 protected void onRefresh() {
        super.onRefresh();

        try {
//内嵌的服务就是这里面创建的
this.createWebServer(); } catch (Throwable var2) { throw new ApplicationContextException("Unable to start web server", var2); } }

 

 private void createWebServer() {
        WebServer webServer = this.webServer;
// 得到servlet上下文 ServletContext servletContext
= this.getServletContext();
// 这里判断了下servlet上下文是否为空???见下面补充
if (webServer == null && servletContext == null) { StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
// 如果没有上下文就要创建服务了 ServletWebServerFactory factory
= this.getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString());
// 执行TomcatServletServerWebServerFactory
this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()}); createWebServer.end(); this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { this.getSelfInitializer().onStartup(servletContext); } catch (ServletException var5) { throw new ApplicationContextException("Cannot initialize servlet context", var5); } } this.initPropertySources(); }

 

    补充:上面为什么会有判断servlet上下文是否为空的逻辑呢?

    因为对于springboot项目可以部署到tomcat,jetty,undertow等web服务器中,默认是使用tomcat的。如果像使用其他web服务器,就要使用配置文件了来修改默认的属性。这是约定大于配置。因为没有配置文件springboot也可以起一个web服务,因为由一些默认的配置。

    对于jar部署的springboot项目,入口就是main方法,它要先读取配置才能启动servlet容器。这个时候走到上面的逻辑是没有servlet上下文的。

   如果是war包部署的web服务,是tomcat先启动的,那走到上面逻辑的时候已经有了servlet上下文。

需要看下这里创建的到底是什么:ServletWebServerFactory factory = this.getWebServerFactory();

就是找一个ServletWebServerFactory的一个实现类类。

protected ServletWebServerFactory getWebServerFactory() {
        String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        if (beanNames.length == 0) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.");
        } else if (beanNames.length > 1) {
            throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
        } else {
            return (ServletWebServerFactory)this.getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
        }
    }

 

这个接口呢有下面几个实现类:我们这里肯定要看tomcat的实现类,那这个TomcatServletWebServerFactory 是在哪里加载进来的呢

 

 

 怎么加载的就要看springboot的自动配置原理了,在springboot中自动配置原理也是spi机制的一种原理,只不过加载的spring.factories文件这个后面再具体分析。

 

 

 里面有一个ServletWebServerFactoryAutoConfiguration类,通过@Import导入了一个EmbeddedTomcat类。

 

 

 

 

这里就是 TomcatServletWebServerFactory  放入spring容器中的地方。

回到上面的逻辑,得到TomcatServletWebServerFactory调用其getWebServer 方法。

这里就是创建内嵌tomcat的地方,下面的写法是内嵌tomcat中创建有关tomcat组件的写法。

 public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
        // new 一个tomcat实例
        Tomcat tomcat = new Tomcat();
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
// tomcat中的Connector组件 Connector connector
= new Connector(this.protocol); connector.setThrowOnFailure(true); tomcat.getService().addConnector(connector); this.customizeConnector(connector); tomcat.setConnector(connector); tomcat.getHost().setAutoDeploy(false); this.configureEngine(tomcat.getEngine()); Iterator var5 = this.additionalTomcatConnectors.iterator(); while(var5.hasNext()) { Connector additionalConnector = (Connector)var5.next(); tomcat.getService().addConnector(additionalConnector); } // DispatcherServlet就是在这里初始化的 this.prepareContext(tomcat.getHost(), initializers);
// tomcat的start,和socket启动阻塞都在这个方法里
return this.getTomcatWebServer(tomcat); }
   protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        return new TomcatWebServer(tomcat, this.getPort() >= 0, this.getShutdown());
    }

 

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        this.monitor = new Object();
        this.serviceConnectors = new HashMap();
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        this.gracefulShutdown = shutdown == Shutdown.GRACEFUL ? new GracefulShutdown(tomcat) : null;
        this.initialize();
    }
 private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
        synchronized(this.monitor) {
            try {
                this.addInstanceIdToEngineName();
                Context context = this.findContext();
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) && "start".equals(event.getType())) {
                        this.removeServiceConnectors();
                    }

                });
// tomcat就是在这里启动的
this.tomcat.start(); this.rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), this.getClass().getClassLoader()); } catch (NamingException var5) { } // 这里就是设置socket监听 this.startDaemonAwaitThread(); } catch (Exception var6) { this.stopSilently(); this.destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", var6); } } }

 

 private void startDaemonAwaitThread() {
        Thread awaitThread = new Thread("container-" + containerCounter.get()) {
            public void run() {
                TomcatWebServer.this.tomcat.getServer().await();
            }
        };
        awaitThread.setContextClassLoader(this.getClass().getClassLoader());
        awaitThread.setDaemon(false);
        awaitThread.start();
    }

 


 

上面就是内嵌tomcat的启动过程,下面分析下DispatcherServlet加载的源码分析。

在上面createServer方法中有一行调用: this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});

参数是一个ServletContextInitializer 的数组,是从getSelfInitializer() 方法中得到,DispatcherServlet就是这里加载的。

  // 但是这里返回是个lambda 表达式,ServletContextInitializer是个一个函数式接口,里面只有一个onStartup方法,这里相当于返回了一个接口的实例,只是这个实例不再容器中
// 只是这个实例的onStartup的逻辑就是下面的selfInitialize方法, 调用getSerlfInitializer方法的时候,下面那个方法不会马上执行。
private ServletContextInitializer getSelfInitializer() { return this::selfInitialize; } private void selfInitialize(ServletContext servletContext) throws ServletException { this.prepareWebApplicationContext(servletContext); this.registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(this.getBeanFactory(), servletContext);
// 这里就是找ServletContextInitializer的实现类 Iterator var2
= this.getServletContextInitializerBeans().iterator(); while(var2.hasNext()) {
// 这里遍历调用onStarup方法 ServletContextInitializer beans
= (ServletContextInitializer)var2.next(); beans.onStartup(servletContext); } }

 

@FunctionalInterface
public interface ServletContextInitializer {
    void onStartup(ServletContext servletContext) throws ServletException;
}

 

回到上面的getWebServer方法,它的参数就是getSelfInitializer()得到的lamda表达式。在调用 this.prepareContext(tomcat.getHost(), initializers);

时初始化DispatcherServlet。这是在TomcatServletWebServerFactory 中的。

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
        File documentRoot = this.getValidDocumentRoot();
// 这个new出来的类,继承自tomcat中的StandardContext
// tomcat在初始化过程中会触发StandardContext中的一个全局变量 Map<ServletContainerInitializer,Set<Class<?>>> initializers
// 遍历它并执行ServletContainerInitializer.onStartup方法 这个过程在springmvc初始化servlet中有提到过 TomcatEmbeddedContext context
= new TomcatEmbeddedContext(); if (documentRoot != null) { context.setResources(new TomcatServletWebServerFactory.LoaderHidingResourceRoot(context)); } context.setName(this.getContextPath()); context.setDisplayName(this.getDisplayName()); context.setPath(this.getContextPath()); File docBase = documentRoot != null ? documentRoot : this.createTempDir("tomcat-docbase"); context.setDocBase(docBase.getAbsolutePath()); context.addLifecycleListener(new FixContextListener()); context.setParentClassLoader(this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader()); this.resetDefaultLocaleMapping(context); this.addLocaleMappings(context); try { context.setCreateUploadTargets(true); } catch (NoSuchMethodError var8) { } this.configureTldPatterns(context); WebappLoader loader = new WebappLoader(); loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName()); loader.setDelegate(true); context.setLoader(loader); if (this.isRegisterDefaultServlet()) { this.addDefaultServlet(context); } if (this.shouldRegisterJspServlet()) { this.addJspServlet(context); this.addJasperInitializer(context); } context.addLifecycleListener(new TomcatServletWebServerFactory.StaticResourceConfigurer(context)); ServletContextInitializer[] initializersToUse = this.mergeInitializers(initializers); host.addChild(context);
// 到这里 initializersToUse还是上面的lambda表达式,只是做了一些合并操作 ,执行也是在这里面的
this.configureContext(context, initializersToUse); this.postProcessContext(context); }
this.configureContext(context, initializersToUse);
 protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 它就实现了ServletContainerInitializer的接口 并通过构造函数把上面的那个lambda表达式传递进去
// 它里面的onStartup方法,就会在执行的时候遍历这个数组中的实例执行其onStartup方法,也就时执行了上面的lambda表达式 TomcatStarter starter
= new TomcatStarter(initializers); if (context instanceof TomcatEmbeddedContext) { TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext)context; embeddedContext.setStarter(starter); embeddedContext.setFailCtxIfServletStartFails(true); } // 这个context就是StandardContext的子类,把starter添加进去,上面TomcatStarter能执行也是这一步把它添加到了StandardContext的全局变量中了。 context.addServletContainerInitializer(starter, NO_CLASSES); Iterator var7 = this.contextLifecycleListeners.iterator(); while(var7.hasNext()) { LifecycleListener lifecycleListener = (LifecycleListener)var7.next(); context.addLifecycleListener(lifecycleListener); } var7 = this.contextValves.iterator(); while(var7.hasNext()) { Valve valve = (Valve)var7.next(); context.getPipeline().addValve(valve); } var7 = this.getErrorPages().iterator(); while(var7.hasNext()) { ErrorPage errorPage = (ErrorPage)var7.next(); 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); } var7 = this.getMimeMappings().iterator(); while(var7.hasNext()) { Mapping mapping = (Mapping)var7.next(); context.addMimeMapping(mapping.getExtension(), mapping.getMimeType()); } this.configureSession(context); (new DisableReferenceClearingContextCustomizer()).customize(context); var7 = this.getWebListenerClassNames().iterator(); while(var7.hasNext()) { String webListenerClassName = (String)var7.next(); context.addApplicationListener(webListenerClassName); } var7 = this.tomcatContextCustomizers.iterator(); while(var7.hasNext()) { TomcatContextCustomizer customizer = (TomcatContextCustomizer)var7.next(); customizer.customize(context); } }

 

知道了怎么走到ServletContextInitializer接口的onStartup方法,看下它有那些实现类。

 

 这里会走到这个RegistrationBean,为什么会是它,而且看下也没有加上什么注解,它是怎么加载到spring容器中的呢,下面会说。在这个类里有onStartup方法。

 

 

 它里面有一个注册方法this.register(description, servletContext); ,这个方法实在子类DynamicRegistrationBean中的

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

 

这个register方法中又有一个this.addRegistration(description, servletContext); 方法的调用,这个方法还是子类中调用的。子类是ServletRegistrationBean

到这里才真正看到Servlet的影子。

   protected Dynamic addRegistration(String description, ServletContext servletContext) {
        String name = this.getServletName();
        return servletContext.addServlet(name, this.servlet);
    }

 

 

上面三个的继承关系如下:

 

 

 

 那上面那个servlet哪里来的呢?  这又要说到springboot的自动装配了,在spring.factories中有一个DispatcherServletAutoConfiguration 的配置类。

 

 

 在这个配置类中有两个重要的地方和DispatcherServlet有关。

首先定义了一个DispatcherServlet

 

 

 把它和上面的ServletRegistrationBean 关联起来的是下面的一个Bean

 

 这个dispatcherServletRegistration 的bean中会自动注入上面创建的DispatcherServlet,然后把它传递给了DispatcherServletRegistrationBean构造函数。

 

 

 从这个bean的继承和构造函数的调用就应该能明白,这里把DispatcherServlet传递给了ServletRegistrationBean 

 

 这就把上面整个串起来了。

这里贴上上面两个流程的图解,有助于理解。可能图的版本和贴出来的代码版本不一致,但是不影响理解主线的流程。

tomcat内嵌:

 

 

dispatcherServlet的装配

 

 

 






 

posted @ 2021-08-14 23:56  蒙恬括  阅读(1518)  评论(1编辑  收藏  举报