共计 14287 个字符,预计需要花费 36 分钟才能阅读完成。
咱们晓得,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
再回到后面,看 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.
* <p>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,
它是什么时候退出 bean 工厂的呢?通过搜寻得悉它是在 ServletWebServerFactoryConfiguration 中被注册的:
咱们再看 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<? extends ServletContextInitializer>... initializerTypes) {this.initializers = new LinkedMultiValueMap<>();
this.initializerTypes = (initializerTypes.length != 0)
? Arrays.asList(initializerTypes)
: Collections.singletonList(ServletContextInitializer.class);
// 这里实例化了 dispatchServlet
addServletContextInitializerBeans(beanFactory);
addAdaptableBeans(beanFactory);
List<ServletContextInitializer> 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<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)) {addServletContextInitializerBean(initializerBean.getKey(),
initializerBean.getValue(), beanFactory);
}
}
ServletContextInitializer 的实现类中有个 dispatcherServletRegistration,就是讲 dispatchServlet 注册到 tomcat 的;
再回到 initializer.onStartup 办法:
@Override
public void onStartup(Set<Class<?>> 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 实现:
能够看到,这里将 dispatchServlet 注册进了 tomcat 的 servlet。