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

SpringBoot加载web项目流程源码解析

程序员文章站 2022-07-10 20:55:35
​上一篇文章中我们写了一个极简版的SpringBoot,基本完成了Controller的映射和访问,真实的SpringBoot是如何来做的?我们以2.3.0.RELEASE版本的SpringBoot为例来看下它是如何一步一步创建内嵌的tomcat并且注册DispatcherServlet的,它是否跟上篇文章中讲的一样,也是利用了Servlet3.0的ServletContainerInitializer和WebApplicationInitializer呢? 友情提示:公众号的排版对源码的文章非常不友好,建...

​上一篇文章中我们写了一个极简版的SpringBoot,基本完成了Controller的映射和访问,真实的SpringBoot是如何来做的?我们以2.3.0.RELEASE版本的SpringBoot为例来看下它是如何一步一步创建内嵌的tomcat并且注册DispatcherServlet的,它是否跟上篇文章中讲的一样,也是利用了Servlet3.0的ServletContainerInitializer和WebApplicationInitializer呢? 友情提示:公众号的排版对源码的文章非常不友好,建议直接上IDEA。
首先从SpringApplication#run方法开始:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

继续往下走,一直到:

public ConfigurableApplicationContext run(String... args) {try {//这里会创建AnnotationConfigServletWebServerApplicationContext
		context = createApplicationContext();
		//重点看下这里,其实里面是调用的context.refresh();
		refreshContext(context);}return context;
}

SpringApplication#refresh:

protected void refresh(ConfigurableApplicationContext applicationContext) {
	//这个就是前面创建出来的AnnotationConfigServletWebServerApplicationContext
	applicationContext.refresh();
}

这个refresh就是spring核心的那个refresh方法,因为AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,因此,会调用到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;
	}
}

通过上面的代码也能很容易的看出来,里面肯定创建了webServer,也就是tomcat,它的refresh实际上啥也没干,直接是调用的父类的refresh,也就是AbstractApplicationContext#refresh,熟悉spring的小伙伴对这个方法应该非常熟悉,这个方法就是用来初始化整个spring的环境的,做的事情很多,我们就不再做太多介绍,里面会执行到一行onRefresh(),因为ServletWebServerApplicationContext重写了onRefresh():

@Override
protected void onRefresh() {
	super.onRefresh();
	try {
		//就是在这里创建的webserver
		createWebServer();
	}
	catch (Throwable ex) {
		throw new ApplicationContextException("Unable to start web server", ex);
	}
}

ServletWebServerApplicationContext#createWebServer():

private void createWebServer() {
	WebServer webServer = this.webServer;
	ServletContext servletContext = getServletContext();
	if (webServer == null && servletContext == null) {
		ServletWebServerFactory factory = getWebServerFactory();
		//这里真正去创建tomcat
		this.webServer = factory.getWebServer(getSelfInitializer());
		。。。
	}
	。。。
}

这里首先是调用了getSelfInitializer(),然后把拿到的ServletContextInitializer传递给getWebServer()方法,注意下ServletContextInitializer,SpringBoot就是用这个接口来完成类似Servlet3.0的ServletContainerInitializer的功能的,进去到getWebServer()里面,TomcatServletWebServerFactory#getWebServer():

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);
	//添加了connector
	tomcat.getService().addConnector(connector);
	//在这里设置了connector的端口号
	customizeConnector(connector);
	tomcat.setConnector(connector);
	tomcat.getHost().setAutoDeploy(false);
	configureEngine(tomcat.getEngine());
	for (Connector additionalConnector : this.additionalTomcatConnectors) {
		tomcat.getService().addConnector(additionalConnector);
	}
	//这里会把ServletContextInitializer传进去
	prepareContext(tomcat.getHost(), initializers);
	return getTomcatWebServer(tomcat);
}

看下TomcatServletWebServerFactory#prepareContext():

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {//这里又添加了2个ServletContextInitializer
	ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
	host.addChild(context);
	//重点看下这里
	configureContext(context, initializersToUse);
	postProcessContext(context);
}

TomcatServletWebServerFactory#configureContext():

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
	//这里创建了一个TomcatStarter,传递进去ServletContextInitializer
	TomcatStarter starter = new TomcatStarter(initializers);
	//TomcatEmbeddedContext继承了tomcat里面的StandardContext
	if (context instanceof TomcatEmbeddedContext) {
		TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
		//把TomcatStarter添加到StandardContext的一个成员变量starter中
		embeddedContext.setStarter(starter);
		embeddedContext.setFailCtxIfServletStartFails(true);
	}
	//把TomcatStarter添加到StandardContext的initializers中,
	//initializers是一个Map
	context.addServletContainerInitializer(starter, NO_CLASSES);
	。。。
}

TomcatStarter是一个非常关键的类,它实现了ServletContainerInitializer接口,这个接口是servlet提供的,因此TomcatStarter是连接SpringBoot和tomcat的一个关键纽带。
继续往下执行就到了TomcatServletWebServerFactory#getTomcatWebServer():

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
	return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
}
public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {.
	//这里会去启动tomcat
	initialize();
}

TomcatWebServer#initialize():

private void initialize() throws WebServerException {
	logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
	。。。
	// 启动tomcat
	this.tomcat.start();
	//启动一个阻塞线程,防止tomcat退出
	startDaemonAwaitThread();
。。。
}

Tomcat启动以后,会执行到tomcat里面的StandardContext#startInternal():

protected synchronized void startInternal() throws LifecycleException {
	...
	// initializers是前面存进去的,里面只有一个TomcatStarter
	for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
		initializers.entrySet()) {
		try {
			//这里会调用TomcatStarter的onStartup()
			//TomcatStarter里面有所有的ServletContextInitializer,因此这里就会回调到SpringBoot里面的ServletContextInitializer里面去
		    entry.getKey().onStartup(entry.getValue(),
		            getServletContext());
		} catch (ServletException e) {
		    log.error(sm.getString("standardContext.sciFail"), e);
		    ok = false;
		    break;
		}
	}
	...
}

TomcatStarter#onStartup():

@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
	try {
		// initializers也是前面存进去的,里面一共3个,其中一个是ServletWebServerApplicationContext里面的selfInitialize
		for (ServletContextInitializer initializer : this.initializers) {
			initializer.onStartup(servletContext);
		}
	}
	catch (Exception ex) {
		...
	}
}

ServletWebServerApplicationContext#selfInitialize():

private void selfInitialize(ServletContext servletContext) throws ServletException {
	prepareWebApplicationContext(servletContext);
	registerApplicationScope(servletContext);
	WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
	//这里面就拿到了DispatcherServletRegistrationBean
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		beans.onStartup(servletContext);
	}
}

首先看下ServletWebServerApplicationContext#getServletContextInitializerBeans():

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
	return new ServletContextInitializerBeans(getBeanFactory());
}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
		Class<? extends ServletContextInitializer>... initializerTypes) {
	this.initializers = new LinkedMultiValueMap<>();
	this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
			: Collections.singletonList(ServletContextInitializer.class);
	//这里去加载DispatcherServletRegistrationBean
	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);
	logMappings(this.initializers);
}
private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
	for (Class<? extends ServletContextInitializer> initializerType : this.initializerTypes) {
		for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,initializerType)) {
			//这里就有DispatcherServletRegistrationBean
			addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
		}
	}
}

拿到了DispatcherServletRegistrationBean,上面继续执行它的onStartup(),RegistrationBean#onStartup():

@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
	//注册DispatcherServlet
	register(description, servletContext);
}

DynamicRegistrationBean# register():

@Override
protected final void register(String description, ServletContext servletContext) {
	//注册DispatcherServlet
	D registration = addRegistration(description, servletContext);
	if (registration == null) {
		logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
		return;
	}
	//设置映射路径和启动级别
	configure(registration);
}

ServletRegistrationBean#configure():

@Override
protected void configure(ServletRegistration.Dynamic registration) {
	super.configure(registration);
	//设置映射路径
	String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
	if (urlMapping.length == 0 && this.alwaysMapUrl) {
		urlMapping = DEFAULT_MAPPINGS;
	}
	if (!ObjectUtils.isEmpty(urlMapping)) {
		registration.addMapping(urlMapping);
	}
	//设置启动级别
	registration.setLoadOnStartup(this.loadOnStartup);
	if (this.multipartConfig != null) {
		registration.setMultipartConfig(this.multipartConfig);
	}
}

至此,整个Spring环境和DispatcherServlet就都加载起来了。
现在还剩下一个问题,DispatcherServletRegistrationBean是如何加载到Spring的容器的呢?
在spring-boot-autoconfigure-**.jar的META-INF下面的spring.factories里面配置了一个DispatcherServletAutoConfiguration,在这里面去创建了DispatcherServletRegistrationBean。
(1)SpringBoot首先扫描classpath,根据里面是否有web相关的类去创建了AnnotationConfigServletWebServerApplicationContext。
(2)AnnotationConfigServletWebServerApplicationContext在它的refresh()里面回调了ServletWebServerApplicationContext的onRefresh(),在这里面去创建了tomcat。
(3)SpringBoot把实现了ServletContextInitializer接口的实现类传递到了TomcatStarter里面,TomcatStarter是由SpringBoot提供的,它同时实现了ServletContainerInitializer接口,而ServletContainerInitializer是Servlet提供的,因此,TomcatStarter是一个非常关键的纽带。
(4)当tomcat启动以后,内部会调用所有的ServletContainerInitializer的onStartup(),因此就回调到了TomcatStarter的onStartup(),进而回调到了ServletContextInitializer里面。
(5)在ServletWebServerApplicationContext里面的一个ServletContextInitializer里面去加载了DispatcherServletRegistrationBean,在它的onStartup()回调里面去创建了DispatcherServlet,并且设置了映射路径和启动级别。
(6)整个启动流程可以看出来,SpringBoot并不是向SpringMVC那样通过回调ServletContainerInitializer来完成加载。SpringBoot是直接生成了它的一个叫TomcatStarter的子类,然后在其中把对ServletContainerInitializer的回调转移到对ServletContextInitializer的回调,然后在ServletContextInitializer中去加载的DispatcherServlet。

欢迎扫码查看更多文章:
SpringBoot加载web项目流程源码解析

本文地址:https://blog.csdn.net/goldenfish1919/article/details/107456809

相关标签: spring java