springboot内嵌tomcat源码分析DispatchServlet装载过程

2022-07-27,,,,

内嵌tomcat原理

入口run方法

public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
// run 进入
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
// run 进入
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}
	// run 进入
public ConfigurableApplicationContext run(String... args) {
...
context = createApplicationContext();
refreshContext(context);
...
}

选择当前应用上下文

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				//表示当前应用是servlet应用
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
					//表示当前应用是REACTIVE应用响应式
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

回到run()方法的refreshContext(context);调用spring的onRefresh();

@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
		//创建web容器
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

private void createWebServer() {
		WebServer webServer = this.webServer;
		//获取server容器上下文 由于是内嵌容器上下文还没有所以当前一定为空
		//当以war包方式此处就不为null了
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
		//此处以jar运行一定进入此处
		//此处获取web容器工厂
			ServletWebServerFactory factory = getWebServerFactory();
			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();
	}

getWebServerFactory

此处能获取到beanName的原因
springboot的spi机制结合spring的@Import

spring.factories文件下

ServletWebServerFactory是AbstractServletWebServerFactory的子类
AbstractServletWebServerFactory实现了这3个web容器的factory

protected ServletWebServerFactory getWebServerFactory() {
		//此处和spi有关由于spring.factories如果不做替换spirngboot默认携带的是
		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));
		}
		//此处从ioc容器取出对应的servletFactory
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

getWebServer()



private void createWebServer() {
		...
		factory.getWebServer(getSelfInitializer());
		...
	}
@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		//创建tomcat
		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);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

执行完上述logic后执行getWebServer()这个方法是真正启动web容器的实现
我们则需要找到内嵌tomcat使用的重要步骤即可

//重要步骤
		Tomcat tomcat = new Tomcat();//创建
        tomcat.addWebapp("/","C://");//路径
        tomcat.start();//启动
        tomcat.getServer().await();//阻塞
        

第一步创建

//创建tomcat
		Tomcat tomcat = new Tomcat();

第二步路径

File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
prepareContext(tomcat.getHost(), initializers);

getTomcatWebServer(tomcat);里TomcatWebServer()的initialize()初始化方法

第三步启动this.tomcat.start();

第四步阻塞startDaemonAwaitThread();

private void initialize() throws WebServerException {
		...
				//此处启动tomcat
				this.tomcat.start();
		...
				startDaemonAwaitThread();
			...
	}
	private void startDaemonAwaitThread() {
		Thread awaitThread = new Thread("container-" + (containerCounter.get())) {

			@Override
			public void run() {
				TomcatWebServer.this.tomcat.getServer().await();
			}

		};
		awaitThread.setContextClassLoader(getClass().getClassLoader());
		awaitThread.setDaemon(false);
		awaitThread.start();
	}

DispatchServlet加载源码分析

getSelfInitializer()

在之前所说的getWebServer()里面有一个方法

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

重点此处是一个lambda表达式

private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}

	private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

getServletContextInitializerBeans()

找到所有实现ServletContextInitializer的bean
要执行这个lambda表达式的onstart方法则需要有地方调用selfInitialize()方法beans.onStartup(servletContext);

往下走

public WebServer getWebServer(ServletContextInitializer... initializers) {
	...
	//此处依然没有被执行
	prepareContext(tomcat.getHost(), initializers);
	...
	configureContext(context, initializersToUse);

}

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		。。。
		TomcatEmbeddedContext context = new TomcatEmbeddedContext();
		。。。

TomcatEmbeddedContext

class TomcatEmbeddedContext extends StandardContext
这个类在tomcat启动的时候会执行这个类的onstart方法这个属于tomcat的onstart流程了此处不扩展
*
大致流程
tomcat.start -> StandardContext.onstart()->tomcatStart.onstart->遍历initializers .onstart()

/**
     * The ordered set of ServletContainerInitializers for this web application.
     */
    private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
        new LinkedHashMap<>();

configureContext()方法

TomcatStarter

TomcatStarter starter = new TomcatStarter(initializers);

这个类非常重要 class TomcatStarter implements ServletContainerInitializer
这个 类的 initializers将赋值上我们刚才所说的lambda表达式也就是一堆的ServletContextInitializer

此处tomcat还不会执行到这个类的onstart方法因为这个类还不在tomcat容器如果将这个类放入tomcat容器将在启动自动调用onstart方法

configureContext方法接下来的步骤就是添加进去
这里将把这个tomcatStart添加进tomcat的StandardContext的initializers启动tomcat将执行这个tomcatStart的onstart了

context.addServletContainerInitializer(starter, NO_CLASSES);

//上边的方法将put进StandardContext的initializers
  @Override
    public void addServletContainerInitializer(
            ServletContainerInitializer sci, Set<Class<?>> classes) {
        initializers.put(sci, classes);
    }

接下来就是这个类是如何进刚才的initializers被扫描的呢?
同样道理在spring.factories里有DispatcherServletAutoConfiguration

@Configuration(
        proxyBeanMethods = false
    )
    @Conditional({DispatcherServletAutoConfiguration.DispatcherServletRegistrationCondition.class})
    @ConditionalOnClass({ServletRegistration.class})
    @EnableConfigurationProperties({WebMvcProperties.class})
    @Import({DispatcherServletAutoConfiguration.DispatcherServletConfiguration.class})
    protected static class DispatcherServletRegistrationConfiguration {
        protected DispatcherServletRegistrationConfiguration() {
        }

        @Bean(
            name = {"dispatcherServletRegistration"}
        )
        @ConditionalOnBean(
            value = {DispatcherServlet.class},
            name = {"dispatcherServlet"}
        )
        public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }
    }

DispatcherServletRegistrationBean

是ServletRegistrationBean->DynamicRegistrationBean->RegistrationBean的子类
而这个子类就是实现了刚才所谓的ServletContextInitializer在ioc容器里面需要getbean拿出来的类

本文地址:https://blog.csdn.net/weixin_44110695/article/details/107304651

《springboot内嵌tomcat源码分析DispatchServlet装载过程.doc》

下载本文的Word格式文档,以方便收藏与打印。