springboot内嵌tomcat源码分析DispatchServlet装载过程
内嵌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
上一篇: 基于jquery实现的一个选择中国大学的弹框 (数据、步骤、代码)
下一篇: 后端c++知识点总结