欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-04 19:18:13
基于serverlect3l将需要扫描的接口放在META_INF里即可@handlestypes可以将Class拿到...

内嵌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();

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

@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

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

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

spring.factories文件下

springboot内嵌tomcat源码分析DispatchServlet装载过程
springboot内嵌tomcat源码分析DispatchServlet装载过程
ServletWebServerFactory是AbstractServletWebServerFactory的子类springboot内嵌tomcat源码分析DispatchServlet装载过程
AbstractServletWebServerFactory实现了这3个web容器的factory
springboot内嵌tomcat源码分析DispatchServlet装载过程

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

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

configureContext()方法

TomcatStarter

TomcatStarter starter = new TomcatStarter(initializers);

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

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

springboot内嵌tomcat源码分析DispatchServlet装载过程
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拿出来的类

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

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

相关标签: spring boot