SpringBoot如何启动tomcat

@SpringBootApplication
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
	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;
	}

 在refreshContext(context);方法中会一直调到ServletWebServerApplicationContext类的onRefresh()方法中,其中createWebServer();就会开始创建tomcat容器,在该方法中会先找到容器中有哪一种的ServletWebServerFactory,再通过factory.getWebServer(getSelfInitializer())来创建容器

	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) {
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
			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();
	}

	protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
		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);
	}
ServletWebServerFactory factory = getWebServerFactory();会拿到ServletWebServerFactory类型的bean,由上图可知,只能拿到一个,有多个的话会报错,并且先拿bean的名称再去创建bean也是为了避免不必要的创建bean.

 可以看到ServletWebServerFactory的实现类有Jetty、Tomcat和Undertow

 对于tomcat而言,会在TomcatServletWebServerFactory类的getWebServer()中去创建tomcat容器 

 因此,只需要确定为什么容器中会有TomcatServletWebServerFactory类型的bean,而没有JettyServletWebServerFactory类型的

 在ServletWebServerFactoryAutoConfiguration配置类中会去导入

ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class
这三种类型的配置类

 

因为springboot的依赖中会自动引入tomcat的依赖 ,因此条件注解

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })成立,而

对于Jetty,项目中是没有该依赖的,也就是说如果要使用Jetty需要在SpringBoot的依赖中排除tomcat的依赖引入Jetty相应的依赖才行

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

		@Bean
		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;
		}

	}
tomcatServletWebServerFactory(),方法的入参,spring会负责传入,用ObjectProvider包装一层是说容器中可以没有TomcatConnectorCustomizer类型的bean,否则的话就必须存在该类型的bean,若没有报错
@SpringBootApplication
public class MyApplication {

    @Bean
    public TomcatConnectorCustomizer tomcatConnectorCustomizer() {
        return new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                connector.setPort(8888);
            }
        };
    }

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

默认是没有TomcatConnectorCustomizer的bean的,可以自定义一个,设置tomcat的启动端口

 

tomcat启动过程中会拿到 我们自定义的tomcatConnectorCustomizer这个bean从而去修改tomcat默认配置

 但是通常我们也不会去这样修改tomcat的端口号,而是通过application.properties配置文件来修改,那么是怎么通过配置文件来修改端口号的呢?

 

 

 在ServletWebServerFactoryAutoConfiguration的配置类中会去导入一个BeanPostProcessorsRegistrar,而该Registrar会往容器中注入一个WebServerFactoryCustomizerBeanPostProcessor,该beanPostProcessor会在TomcatServletWebServerFactory初始化的时候修改其一些属性

 

 对tomcat属性进行修改的是ServletWebServerFactoryCustomizer中进行的,而该bean是在ServletWebServerFactoryAutoConfiguration自动配置类中导入的,而ServerProperties就是取的application.properties文件中以server开头的配置

@EnableConfigurationProperties(ServerProperties.class)注解的作用是让ServerProperties生效,也就是拿到配置文件中以server开头的配置,生成一个ServerProperties的对象


版权声明:本文为weixin_44778850原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。