Hello,大家好,我是此林。

今天来深入底层讲一讲SpringBoot是如何启动的,也就是我们单击运行SpringBoot启动类,它底层发生了什么?

SpringBoot启动类很简单,只有一行代码。

b62308fb026043c6ac519c25d2049691.png

我们点进run() 方法。

7be9287ad004483f882ed0c0b82fc921.png

我们发现,它底层其实进行了两步操作。

第一步是new出一个SpringApplication对象,第二个是执行run() 方法。

我们先看看是如何new SpringApplication的。

继续点进SpringApplication的构造方法。

33c0817180f343028be4b982864752c1.png

可以看到,这里初始化设置了非常多的属性。

我们主要最后几行关键代码。

this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();

通过DEBUG打断点我们发现,this.primarySources 就是当前启动类com.itheima.test.App。

e84b4d5f2147420aadb67a096ebbcfcd.png

也就是我们之前之前启动类里传入的参数App.class本身。

b62308fb026043c6ac519c25d2049691.png

继续执行程序,这里判断了应用的类型。这里是servlet类型(传统Web应用),此外还有None类型(非Web应用)、Reactive类型。我们后期创建WebServer容器要根据这个属性来创建不同的Context,决定是否要启动Tomcat。

d5ed806850b74065afcb43bad21a082d.png

再看这两行代码。

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));

1. 这里this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 底层是从 META-INF/spring.factories 文件里找到实现了 ApplicationContextInitializer 接口的所有实现类。它们用于在 ApplicationContext 初始化之前进行一些自定义的初始化操作。

以下是spring.factories 文件:

00f97d7211184c4e8d9870340c94ca57.png

2. this.getSpringFactoriesInstances(ApplicationListener.class) 也一样,底层是从 META-INF/spring.factories 文件里找到键名为 

org.springframework.context.ApplicationListener

的值,也就是找到所有实现了 ApplicationListener 接口的类。它们用于监听 Spring 应用上下文中的事件。

以下是相关的spring.factories 文件:

530b3acf0be54e58b530dafaaf0ea0ea.png

为了更易于理解,我们举个例子,再深入看看 this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
        // 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 对每一个实现类进行实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对实例数组进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

这里有三步:

1. 获取注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)

2. 对每一个实现类进行实例化(返回一个实例数组)

3. 对实例数组进行排序,表示执行顺序。排序规则如下:如果实现了 PriorityOrdered 接口,优先级最高。如果实现了Ordered接口或者基于注解@Order,数值越小优先级越高。

点进AnnotationAwareOrderComparator.sort() 方法。发现它调用了java.util.List的 sort(Comparator<? super E> c) 方法。这里传进去的INSTANCE是 AnnotationAwareOrderComparator 本身,说明 AnnotationAwareOrderComparator 实现了Comparator 接口,重写了 compare() 方法。

48c561783a4b4b2f8845ee9cd742d918.png

但是我们又发现 AnnotationAwareOrderComparator 类里似乎没有实现Comparator 接口实现自定义排序。

3757b31806724cb0aa861f3ec29c32b3.png

我们查看AnnotationAwareOrderComparator继承关系链发现,它继承了OrderComparator类;OrderComparator又实现了Comparator接口,所以AnnotationAwareOrderComparator 间接实现了Comparator 接口来自定义排序。

public int compare(@Nullable Object o1, @Nullable Object o2) {
        return this.doCompare(o1, o2, (OrderSourceProvider)null);
    }

    private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
        boolean p1 = o1 instanceof PriorityOrdered;
        boolean p2 = o2 instanceof PriorityOrdered;
        if (p1 && !p2) {
            return -1;
        } else if (p2 && !p1) {
            return 1;
        } else {
            int i1 = this.getOrder(o1, sourceProvider);
            int i2 = this.getOrder(o2, sourceProvider);
            return Integer.compare(i1, i2);
        }
    }

可以发现,主要功能是比较两个对象 o1 和 o2 的优先级顺序。
先检查 o1 和 o2 是否实现了 PriorityOrdered 接口。
如果 o1 实现了而 o2 没有实现,则 o1 的优先级更高,返回 -1。
如果 o2 实现了而 o1 没有实现,则 o2 的优先级更高,返回 1。
如果两者都实现了或都没有实现 PriorityOrdered 接口,则通过 getOrder 方法获取它们的顺序值,并比较这两个值。

我们再回到this.getSpringFactoriesInstances(ApplicationContextInitializer.class) 源码。

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
        // 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        // 对每一个实现类进行实例化
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        // 对实例数组进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

我们再来详细地看看这行代码的底层,它是如何获取ApplicationContextInitializer接口的所有实现类的?

// 返回了注册在META-INF/spring.factories文件中的ApplicationContextInitializer.class接口的所有实现类(一个全类名集合)
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

点进loadFactoryNames() 方法。

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

这里 loadSpringFactories(classLoaderToUse) 返回的是一个 Map<String, List<String>> ,getOrDefault() 则是取到键为 factoryTypeName 的 List,取不到则返回空集合。那么factoryTypeName到底是什么?我们发现它就是 ApplicationContextInitializer 接口。

由于刚才我们看过spring.factories 文件了,它就是一个键值对文件,它的值也就是一个类名列表,所以我们推测 Map<String, List<String>> 就是整个spring.factories。

继续点进 loadSpringFactories() 方法。果然如此,详细请看注释。

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            Map<String, List<String>> result = new HashMap();

            try {
                // 加载出了所有META_INF/spring.factories文件
                Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
                // 遍历全部的META_INF/spring.factories文件
                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 每一个META_INF/spring.factories文件相当于是一个properties文件
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();
                    // 转成Map格式,key是接口名,value是实现类的列表
                    while(var6.hasNext()) {
                        Map.Entry<?, ?> entry = (Map.Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

我们要扫描所有的所有META_INF/spring.factories文件,因为它可能不止一个。

我们再来总结一下,以上流程就是创建初始化SpringApplication对象,并设置相关属性。关键的还是这几行代码。

​
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();

​

至此,我们SpringApplication对象的创建就结束了。

现在我们再来详细讲一讲SpringApplication对象的run() 方法。

4785a8c931904ce488a2a384e8134ae8.png

run() 方法点进来:

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);
			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;
	}

我们发现run() 方法最后返回了 ConfigurableApplicationContext ,通过继承关系链我们知道,这个接口继承了ApplicationContext接口,返回了一个Spring 容器。

流程大概如下:

1. 创建秒表记录容器创建启动的时间。

2. 加载spring.factories文件中的所有实现了 SpringApplicationRunListener接口的实现类,这些实现类是事件监听器。

SpringApplicationRunListeners listeners = getRunListeners(args);

SpringBoot 启动的不同阶段(如 starting, environmentPrepared, contextPrepared, contextLoaded, started, running, failed 等)会发布不同的事件,会调用这些监听器的相应方法。SpringApplicationRunListener接口默认只有一个实现类EventPublishingRunListener

3. run方法刚开始执行的事件广播。就是触发事件ApplicationStartingEvent,所有监听了事件ApplicationStartingEvent的ApplicationListener都会在此时执行。

listeners.starting(bootstrapContext, this.mainApplicationClass);

4. 创建并配置当前应用将要使用的环境。

这里加载了命令行参数。

ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

这里是准备环境,读取所有配置源的配置属性值。

在创建环境完成够需要发布事件environmentPrepared,所以需要传入参数listeners。

因为我们启动SpringBoot项目通常会输入命令:java -jar application.jar --spring.profiles.active=dev
命令行中可能存在指定环境变量的参数--spring.active.profile=dev,所以需要传入参数applicationArguments。

ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);

5. 打印banner信息,也可以自己设置banner.txt

Banner printedBanner = printBanner(environment);

6. 创建applicationContext,即创建Spring IOC 容器。

context = createApplicationContext();

根据应用类型(servlet)去new了一个ApplicationContext对象,但是并没有设置applicationContext的属性。到了这一步中context中有一个environment属性,但是它不是springApplication对象中的environment属性。

// environment.getProperty("test.string") = test

// context.getEnvironment.getProperty("test.string") = null

7. 设置应用启动策略

context.setApplicationStartup(this.applicationStartup);

8. 准备应用上下文,之前准备environment信息,现在准备IOC容器的信息。

prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

参数:
bootstrapContext:初始化前的上下文,包含一些初始化配置和资源。
context:就是刚刚new 出来没有设置属性的 ConfigurableApplicationContext。
environment:应用环境,包含配置属性等。
listeners:应用监听器,用于监听应用生命周期事件。
applicationArguments:应用启动时传递的命令行参数。
printedBanner:启动时打印的欢迎信息(banner)。

1) 在prepareContext() 方法里会遍历所有的ApplicationContextInitializer,调用他们的 initialize() 方法,来对 IOC 容器进行初始化扩展工作。ApplicationContextInitializer 也就是在之前new Application() 里设置的。

2.)然后调用 listeners.contextPrepared(context) ,遍历所有的 listeners(默认只有一个实现类EventPublishingRunListener),调用每个listener 的 contextPrepared() 方法,即上下文准备就绪。

3)然后拿到BeanFactory,注册SpringApplicationArguments(命令行参数)和 Banner 组件。

4)最后调用 listeners.contextLoaded(),通知所有监听器,IOC容器加载完了。

9. 刷新 IOC 容器(这是核心,创建容器里的所有组件)

在这里的 invokeBeanFactoryPostProcessors() 方法

会先扫描@Controller、@Configuration等注解,再进行SpringBoot自动配置,最后再用 onRefresh() 创建启动Tomcat服务器。所以我们可以知道SpringBoot自动配置的时机是在加载用户自定义配置类之后。

refreshContext(context);

 关于@SpringApplication注解自动配置,这篇博客里有,这里就不赘述了。

  SpringBoot自动配置原理:底层源码分析-CSDN博客

10. 刷新容器后的操作。

afterRefresh(context, applicationArguments);

11. SpringBoot启动结束,关闭秒表,并且日志打印出启动时长。可以通过配置参数 spring.main.log-startup-info=false 设置是否输出

Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}

12. 通知所有的监听器,应用启动完成。调用 started 方法,传递应用上下文 context 和启动时间 timeTakenToStartup。现在已经是started阶段了,前文已经说过SpringBoot 启动的不同阶段(如 starting, environmentPrepared, contextPrepared, contextLoaded, started, running, failed 等)。

listeners.started(context, timeTakenToStartup);

13. 调用所有的Runners,调用run() 方法

callRunners(context, applicationArguments);

说了这么久,我们貌似还没有说到tomcat 启动原理,既然我们想知道tomcat在SpringBoot中是怎么启动的,那么run方法中,重点关注创建应用上下文(createApplicationContext)和刷新上下文(refreshContext)。

这里做了高度封装,我们需要深入底层源码来看看。

点进 createApplicationContext()

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

这里的 this.applicationContextFactory 的是 DefaultApplicationContextFactory 类,所以执行的create() 方法是 DefaultApplicationContextFactory 中的。

我们点进DefaultApplicationContextFactory的create() 方法

    @Override
	public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
		try {
			return getFromSpringFactories(webApplicationType, ApplicationContextFactory::create,
					AnnotationConfigApplicationContext::new);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	}

create() 方法接收一个 WebApplicationType 参数,并尝试通过 getFromSpringFactories() 根据 WebApplicationType 创建不同的 ConfigurableApplicationContext 实例。

继续看 getFromSpringFactories() 方法。

private <T> T getFromSpringFactories(WebApplicationType webApplicationType,
	BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
	for (ApplicationContextFactory candidate : SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class,
			getClass().getClassLoader())) {
		T result = action.apply(candidate, webApplicationType);
		if (result != null) {
			return result;
		}
	}
	return (defaultResult != null) ? defaultResult.get() : null;
}

1. SpringFactoriesLoader 先查找类路径下的 META-INF/spring.factories 文件,加载所有实现了 ApplicationContextFactory 接口的类。

2. 用for循环对所有实现类遍历,用 candidate 变量表示。由于 action 是一个 BiFunction<ApplicationContextFactory, WebApplicationType, T> 类型的函数式接口,action之前传进来的是 ApplicationContextFactory::create。所以 action.apply(candidate, webApplicationType) 相当于调用了 candidate 的 create 方法,并传入 webApplicationType,返回一个 T 类型的结果。

6558b51e91d64b43aba9249fdfab4d87.png打个断点,我们可以清楚地看到,webApplicationType 为 "SERVLET",candidate 为 "AnnotationConfigServletWebServerApplicationContext"。

我们再去 AnnotationConfigServletWebServerApplicationContext 里面看下 create() 方法。

d506af8e98e74f54a188b3ff9cb8e7c6.png

可以看到,它先判断WebApplicationType 是否为 SERVLET,如果是则返回 AnnotationConfigServletWebServerApplicationContext,这个为后续创建tomcat容器做了准备。

前文是createContext()方法,接下来我们再来看 refreshContext()

层层点进去,最后来到了这个方法。

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

继续打个断点,我们发现调用了 AnnotationConfigServletWebServerApplicationContext 的refresh() 方法,这就和createContext()创建返回的AnnotationConfigServletWebServerApplicationContext 关联起来了。

1d09f2c4e89f43b49b0b3135bf34c650.png

再去看下 AnnotationConfigServletWebServerApplicationContext 的 refresh() 方法。

点进 AnnotationConfigServletWebServerApplicationContext,发现它并没有refresh() 方法,于是推测在它的父类 ServletWebServerApplicationContext 中,点进 ServletWebServerApplicationContext,发现ServletWebServerApplicationContext 调用了它父类 AbstractApplicationContext 的refresh() 方法。

AbstractApplicationContext 的refresh() 方法,主要调用onRefresh方法执行自定义的刷新操作。

963d84e29381425598ec64c6758fcce2.png

再看this.onRefresh(),发现是个空方法。

09b01b57e5f64a4cb490412def042cad.png

迂回了半天,又回到了 ServletWebServerApplicationContext

35bf4602ea78481087db9a5cd5458a4c.png

这是 ServletWebServerApplicationContext 的 onRefresh() 方法。

b3ce32c13c8643eab0a287841b93ad76.png

所以说到底,直接说 refreshContext() 最终执行的是 ServletWebServerApplicationContext 的onRefresh() 方法不就好了?

不是此林要故意迂回卖关子,这毕竟是源码阅读的一个寻找的过程。

接下来再看createWebServer() 方法,打断点,看相关变量。是不是终于有了Tomcat的迹象了?

ea7587f3a7dd42759151f4ca7b3dd9ee.png

创建Tomcat容器一定在这行代码里。

this.webServer = factory.getWebServer(getSelfInitializer());

由于factory 是 TomcatServletWebServerFactory,我们直接去这个里看它的getWebServer方法。

/**
 * 创建并配置Tomcat Web服务器
 * 
 * @param initializers 用于初始化Servlet上下文的初始化器数组
 * @return 返回一个已配置的WebServer实例
 */
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 如果禁用了MBean注册,则调用相应的方法禁用
    if (this.disableMBeanRegistry) {
        Registry.disableRegistry();
    }
    
    // 创建Tomcat实例
    Tomcat tomcat = new Tomcat();
    
    // 确定Tomcat的基础目录,如果未指定,则创建一个临时目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    // 为Tomcat服务器添加生命周期监听器
    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的连接器
    tomcat.setConnector(connector);
    
    // 禁用自动部署,以避免在开发过程中不必要的自动重新加载
    tomcat.getHost().setAutoDeploy(false);
    
    // 配置Tomcat的引擎
    configureEngine(tomcat.getEngine());
    
    // 添加额外的Tomcat连接器
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    
    // 为tomcat准备上下文,可以理解为用代码写web.xml,tomcat启动的时候会去加载这里设置的信息
    prepareContext(tomcat.getHost(), initializers);
    
    // 返回配置好的Tomcat Web服务器实例
    return getTomcatWebServer(tomcat);
}

这段代码的主要功能就是创建并配置一个Tomcat服务器实例,然后返回一个WebServer对象。相关细节注释里都写了,如果知道Tomcat源码,应该会比较熟悉。

我们看最后一行的getTomcatWebServer(tomcat)源码,这里直达底层的 TomcatWebServer类的 initialize()方法。

/**
 * 初始化嵌入式 Tomcat 服务器。
 * 
 * 该方法负责根据配置设置初始化 Tomcat 服务器,包括初始化监听器、启动服务器以及处理启动过程中的异常。
 * 
 * @throws WebServerException 如果在初始化嵌入式 Tomcat 服务器时发生异常。
 */
private void initialize() throws WebServerException {
    // 记录已初始化的 Tomcat 端口信息
    logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
    synchronized (this.monitor) {
        try {
            // 将实例 ID 添加到引擎名称中以确保唯一性
            addInstanceIdToEngineName();

            // 查找上下文并为其添加生命周期监听器
            Context context = findContext();
            context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                    // 移除服务连接器,以防止在服务启动时进行协议绑定
                    removeServiceConnectors();
                }
            });

            // 启动服务器以触发初始化监听器
            this.tomcat.start();

            // 可以在主线程中重新抛出失败异常
            rethrowDeferredStartupExceptions();

            try {
                // 绑定类加载器,如果发生命名异常则跳过
                ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
            }
            catch (NamingException ex) {
                // 命名未启用,继续执行
            }

            // 与 Jetty 不同,所有 Tomcat 线程都是守护线程。创建一个阻塞的非守护线程以防止立即关闭
            startNonDaemonAwaitThread();
        }
        catch (Exception ex) {
            // 处理启动过程中发生的异常,静默停止并销毁服务器,然后抛出自定义异常
            stopSilently();
            destroySilently();
            throw new WebServerException("无法启动嵌入式 Tomcat", ex);
        }
    }
}

this.tomcat.start(); 就是启动Tomcat服务器。

内嵌tomcat的创建和启动已经看到了,那么它是怎么初始化servlet容器的呢?

就在之前我们的onRefresh() 方法里。getWebServer() 是启动Tomcat容器,getSelfInitializer()就是初始化servlet容器了。源码就不详细看了,感兴趣的小伙伴可以自己去研究研究。

this.webServer = factory.getWebServer(getSelfInitializer());

本文持续更新中....... 

关注我吧!老朋友此林,带你看不一样的世界!有疑惑的小伙伴可以私信我或者评论区留言,我看到后会及时回复!

往期文章:

SpringBoot自动配置原理:底层源码分析-CSDN博客

进阶版:深入浅出 Spring AOP底层原理分析(附自定义注解案例)(二)更新已完结-CSDN博客

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐