spring boot 加载web容器tomcat流程源码分析

我本地的springboot版本是2.5.1,后面的分析都是基于这个版本

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

我们通过在pom文件中引入

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

来引入web容器,默认的web容器时tomcat。

本文章主要描述spring boot加载web容器 tomcat的部分,为了避免文章知识点过于分散,其他相关的如bean的加载,tomcat内部流程等不做深入讨论。

1、在springboot web工程中,全局上下文是AnnotationConfigServletWebApplicationContext

下面的部分,我们具体来看下

首先,我们的入口代码一般都是这样写的

 public static void main(String[] args) {
        SpringApplication.run(BootargsApplication.class,args);
    }

跳转到run方法里面,依次会调用下面两个方法

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

首先会创建SpringApplication实例对象,跳转到SpringApplication的构造方法去看看,依次会调用如下方法

public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
   ......
   //本次不相关的代码全部省略掉,只保留相关代码
   //这里的 this.webApplicationType=WebApplicationType.SERVLET, 我们来分析下这个代码的具体的执行赋值
   this.webApplicationType = WebApplicationType.deduceFromClasspath();
   ......
}

继续跳转到WebApplicationType.deduceFromClasspath()去看看

//这个方法主要是在当前类路径下查找指定的class类是否存在,返回对饮枚举类型
	static WebApplicationType deduceFromClasspath() {
	// WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";

	//我们通过pom文件引入spring-boot-starter-web,会简介引入spring-webmvc,上面这个类就在这个webmvc中,所以不会进入这个if分支
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
	//SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" }
	//javax.servlet.Servlet这个类存在于tomcat-embed-core中
    //org.springframework.web.context.ConfigurableWebApplicationContext这个类存在于spring-web中
    //这两个jar都是由spring-boot-starter-web间接引入的,所以也不会走这个分支
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		//所以会从这里返回
		return WebApplicationType.SERVLET;
	}

下面看下jar包的引入

回到new SpringApplication(primarySources).run(args)的调用来看run方法的代码

public ConfigurableApplicationContext run(String... args) {
		.......
		try {
			......
			//我们来看这个context的创建,context=new AnnotationConfigServletWebServerApplicationContext()下面来具体看这块的执行
			context = createApplicationContext();
		    ......
		    //后续几个部分会来说明这个方法
			refreshContext(context);
			......
		}
		catch (Throwable ex) {
			......
		}

		try {
		.......
		}
		catch (Throwable ex) {
		.......
		}
		return context;
	}

createApplicationContext()依次会调用如下方法

protected ConfigurableApplicationContext createApplicationContext() {
		//这里的this.webApplicationType就是上面的WebApplicationType.SERVLET
		return this.applicationContextFactory.create(this.webApplicationType);
	}
//最终会调用到这个lambda表达式,入参就是上面的WebApplicationType.SERVLET
	ApplicationContextFactory DEFAULT = (webApplicationType) -> {
		try {
			switch (webApplicationType) {
			case SERVLET:
				//会从这里返回
				return new AnnotationConfigServletWebServerApplicationContext();
			case REACTIVE:
				return new AnnotationConfigReactiveWebServerApplicationContext();
			default:
				return new AnnotationConfigApplicationContext();
			}
		}
		catch (Exception ex) {
			throw new IllegalStateException("Unable create a default ApplicationContext instance, "
					+ "you may need a custom ApplicationContextFactory", ex);
		}
	};

到这里,我们的上下文context就已经创建出来了,这块代码也比较简单。就不多说什么了

2、查找ServletWebServerFactory

再次回到new SpringApplication(primarySources).run(args)的调用来看run方法的代码

public ConfigurableApplicationContext run(String... args) {
		.......
		try {
			......
			//上面已经对context做过了讲解,context=new AnnotationConfigServletWebServerApplicationContext()
			context = createApplicationContext();
		    ......
		    //下面来看这个方法
			refreshContext(context);
			......
		}
		catch (Throwable ex) {
			......
		}

		try {
		.......
		}
		catch (Throwable ex) {
		.......
		}
		return context;
	}

点到refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			shutdownHook.registerApplicationContext(context);
		}
		refresh(context);
	}

继续点到refresh(context)

protected void refresh(ConfigurableApplicationContext applicationContext) {
        //这里的applicationContext就是AnnotationConfigServletWebServerApplicationContext的对象,由于这个类没有refresh方法,会跳转到它的父类ServletWebServerApplicationContext的方法中去,我们继续点进去
		applicationContext.refresh();
	}

点到ServletWebServerApplicationContext的refresh方法

public final void refresh() throws BeansException, IllegalStateException {
		try {
            //继续跳转到父类AbstractApplicationContext方法
			super.refresh();
		}
		catch (RuntimeException ex) {
			WebServer webServer = this.webServer;
			if (webServer != null) {
				webServer.stop();
			}
			throw ex;
		}
	}

打开AbstractApplicationContext的refresh方法

//springboot 大部分的初始化工作是在这里完成的,不过这不是我们本地的重点,不相关的我们统统略过
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			......
			try {
			......
				//继续点到这个方法,这里又会跳转到ServletWebServerApplicationContext这个类的方法
				onRefresh();
			.....
			}

			catch (BeansException ex) {
			.....
			}

			finally {
			.....
			}
		}
	}

打开ServletWebServerApplicationContext的onRefresh方法

protected void onRefresh() {
		super.onRefresh();
		try {
			//这里就是我们本次的重点,会在这里创建具体的web容器,我们点进去看看,还是ServletWebServerApplicationContext这个类的方法
			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();
		//第一次进来webServer servletContext都是null,会进到if分支里面
		if (webServer  == null && servletContext == null) {
		   //这里只是做个标记,不用关注,跳过
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			//这里就会来查找ServletWebServerFactory,也就是web容器的工厂,具体看下getWebServerFactory()方法,还是ServletWebServerApplicationContext这个类的方法
			ServletWebServerFactory factory = getWebServerFactory();
			createWebServer.tag("factory", factory.getClass().toString());
			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();
	}

打开ServletWebServerApplicationContext的getWebServerFactory方法

protected ServletWebServerFactory getWebServerFactory() {
		// Use bean names so that we don't consider the hierarchy
		//从beanFactory中查找ServletWebServerFactory类型的bean的定义,返回对应bean的名字
		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));
		}
        //这里会从beanFactory中返回bean的名字为beanNames[0],类型为ServletWebServerFactory.class的bean对象,如果当前bean还未创建,则此时就会创建bean对象并返回
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

从上面的代码并看不出来实际的ServletWebServerFactory对象具体是什么?下面带着大家一起简单过下这部分的加载,这里具体就是springboot 加载bean的流程了,这部分的逻辑比较多,本次就不具体展开。关于springboot 加载bean的流程计划后续专门再写一篇。

springboot在启动过程中会在当前类路径下META-INF/spring.factories这个文件中,key=org.springframework.boot.autoconfigure.EnableAutoConfiguration的属性的作为bean的定义进行加载,在这过程中还会使用key=org.springframework.boot.autoconfigure.AutoConfigurationImportFilterfilter的属性作为过滤,配合META-INF/spring-autoconfigure-metadata.properties对这些类做一个过滤,剔除掉不符合的类(后续还会根据类上的注解判断是否要继续剔除)。

当前这些主要在spring-boot-autoconfigure-2.5.1.jar这个文件中

下面截取上面说的两部分,可以看到这里的过滤器就3个,具体不展开讨论了,自动导入的类就是下面的再加过滤去掉的

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
#下面这个会在创建servelt中使用,下部分我们再关注它
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
#下面这个就是我们需要用到的
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
......
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
......

我们看下上面的org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration这个类,这个类在web 场景下,不会被剔除。会被加载。我们看看这个类,我们只看头部就可以了

这里我们看到类上有Import注解,会继续导入这几个类,

ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class

这三个都是本次相关的,它们都是ServletWebServerFactoryConfiguration的内部类,我们进去看看,类的结构都是一样的,我们就看下ServletWebServerFactoryConfiguration.EmbeddedTomcat类吧

可以看到EmbeddedTomcat上面有ConditionalOnClass,ConditionalOnMissingBean这两个注解,

简单说下,ConditionalOnClass是表示当前类路径下存在对应类是加载

ConditionalOnMissingBean是表示当前beanFactory中没有对应类型bean定义的话加载

多个条件都是and的关系,有一个条件不成立,就不会去进行后续处理。

在这里EmbeddedTomcat类这两个条件是成立的,这时就会继续遍历当前类的所有方法,找到@Bean注解的方法,加载到beanFactory中去

而EmbeddedJetty,EmbeddedUndertow两个类条件是不成立的,就不会进行后续执行,剔除掉了

这里就会把EmbeddedTomcat.tomcatServletWebServerFactory这个方法进行加载,返回值是TomcatServletWebServerFactory类型,我们看下TomcatServletWebServerFactory类的继承图,可以看到它正好是继承了ServletWebServerFactory接口。

再次打开ServletWebServerApplicationContext的getWebServerFactory方法

protected ServletWebServerFactory getWebServerFactory() {
		.......
        //所以这里的逻辑实际上会执行ServletWebServerFactoryConfiguration.EmbeddedTomcat类的tomcatServletWebServerFactory方法,返回TomcatServletWebServerFactory对象,相关的属性注入等等这里就不讲述了
		return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
	}

到这里,整个ServletWebServerFactory的查找就完成了

3、创建DispatcherServletRegistrationBean,DispatcherServlet

再看看上面的META-INF/spring.factories文件

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
#现在我们重点来看这个类
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
......
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
......

我们打开org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration这个类看看

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
//我们当前只关注这ConditionalOnWebApplication、ConditionalOnClass注解
//ConditionalOnWebApplication是根据type来判断指定类是否存在
//当前的type是 Type.SERVLET,是来查找org.springframework.web.context.support.GenericWebApplicationContext类是否存在,这个类存在于spring-web中,所以这个条件是true
@ConditionalOnWebApplication(type = Type.SERVLET)
//这个注解上面说过了 ,就是查找指定的类是否存在,这个是查找DispatcherServlet.class是否存在,这里也会返回true
@ConditionalOnClass(DispatcherServlet.class)
//上面两个条件都成立,就会执行后续的操作,去遍历内部类和方法
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {

	/**
	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/**
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/".
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration(proxyBeanMethods = false)
    //这里还是个条件,通过实现Condition接口,通过matches方法来判断
    //DefaultDispatcherServletCondition这个类就在当前这个文件里,matches判断的结果也是true
	@Conditional(DefaultDispatcherServletCondition.class)
    //ServletRegistration.class这个类存在于tomcat-embed-core里面,这个结果也是true
	@ConditionalOnClass(ServletRegistration.class)
    //上面两个条件成立,就会执行后续的操作,去遍历内部类和方法
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {

        //beanFactory会创建这个DispatcherServletbean的定义,bean的名字就是dispatcherServlet
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
			dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());
			return dispatcherServlet;
		}

		@Bean
		@ConditionalOnBean(MultipartResolver.class)
		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}

	}

	@Configuration(proxyBeanMethods = false)
    //和上面的一样,不说了
	@Conditional(DispatcherServletRegistrationCondition.class)
    //和上面的一样,不说了
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
    //这里会要在查找DispatcherServletConfiguration.class,并执行加载bean定义的流程,这就是上面的类了
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
        //ConditionalOnBean查找是否存在指定bean的定义,这个方法要注入参数,需要这个类,当前这里就是上面的dispatcherServlet方法定义的,这里也是存在的
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
        //DispatcherServlet dispatcherServlet这个就是dispatcherServlet这个方法定义的bean,在创建DispatcherServletRegistrationBean这个bean的时候,就会去查找dispatcherServlet是否存在,如果不存在,先创建dispatcherServlet这个bean,再创建DispatcherServletRegistrationBean
		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
					webMvcProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
			multipartConfig.ifAvailable(registration::setMultipartConfig);
			return registration;
		}

	}
  ......
}

上面就是创建DispatcherServlet,DispatcherServletRegistrationBean的过程了

4、创建tomcat,加载Servlet.class,filter.class,监听器

再次回到ServletWebServerApplicationContext的createWebServer方法

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
			//上面我们已经看到了这里,factory是TomcatServletWebServerFactory类的一个实例对象
			ServletWebServerFactory factory = getWebServerFactory();
            //这里还是做个标记,不用关注
            createWebServer.tag("factory", factory.getClass().toString());
            //这里就是具体创建tomcat了,这里的入参getSelfInitializer()是个lambda表达式,这个后续很重要
			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();
	}
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}
	//是创建webServer的参数
	private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

factory.getWebServer(getSelfInitializer())会调用到TomcatServletWebServerFactory的getWebServer的方法

public WebServer getWebServer(ServletContextInitializer... initializers) {
        .......
		//上面的入参会在这里传下去
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}

点进prepareContext(tomcat.getHost(), initializers)去看看

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		......
		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
		host.addChild(context);
		//继续传下去
		configureContext(context, initializersToUse);
		postProcessContext(context);
	}

再点到configureContext(context, initializersToUse)这个调用去看看

protected void configureContext(Context context, ServletContextInitializer[] initializers) {
		//会传递给TomcatStarter,作为构造参数,下面我们去这里看看
		TomcatStarter starter = new TomcatStarter(initializers);
        ......
    }

下面我们去看看TomcatStarter这个类是怎么使用这个initializers这个构造参数的。

这个类不长
class TomcatStarter implements ServletContainerInitializer {
    ......
	TomcatStarter(ServletContextInitializer[] initializers) {
        //入参会作为它的成员属性
		this.initializers = initializers;
	}

	@Override
	public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
		try {
			for (ServletContextInitializer initializer : this.initializers) {
                //会在这里调用onStartup方法,这里的入参就是ApplicationContextFacade的对象,里面包装了ApplicationContext,里面再包装了TomcatEmbeddedContext,这要就和tomcat联系起来了,下面的截图就是servletContext的对象结构
				initializer.onStartup(servletContext);
			}
		}
		catch (Exception ex) {
			......
	}

上面的initializer.onStartup(servletContext)会调用到ServletWebServerApplicationContext的selfInitialize方法

private void selfInitialize(ServletContext servletContext) throws ServletException {
        //这里是将ApplicationContextFacade设置到当前的servletContext上
		prepareWebApplicationContext(servletContext);
        //这里是在beanFactory中注册application的scope
		registerApplicationScope(servletContext);
        //这里还是注册上下文相关的bean
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
        //我们重点来看这里getServletContextInitializerBeans()是定义个一个ServletContextInitializerBeans对象,我们点进去看看
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
		//这里的getBeanFactory()就是全局的beanFactory
		return new ServletContextInitializerBeans(getBeanFactory());
	}
public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		this.initializers = new LinkedMultiValueMap<>();
        //由于我们没有传initializerTypes这个参数,所以this.initializerTypes里面就只有ServletContextInitializer.class这个类
		this.initializerTypes = (initializerTypes.length != 0) ? Arrays.asList(initializerTypes)
				: Collections.singletonList(ServletContextInitializer.class);
        //这里主要是从beanFactory中查找this.initializerTypes类型,我们进去看看,就是下面这个方法了
		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) {
            //默认情况下这里找到就只有上面第3部分的DispatcherServletRegistrationBean对应的bean
			for (Entry<String, ? extends ServletContextInitializer> initializerBean : getOrderedBeansOfType(beanFactory,
					initializerType)) {
                //这里的key是bean的名字,value就是bean对象,在进去看看,就是下面这个方法
				addServletContextInitializerBean(initializerBean.getKey(), initializerBean.getValue(), beanFactory);
			}
		}
	}
	private void addServletContextInitializerBean(String beanName, ServletContextInitializer initializer,
			ListableBeanFactory beanFactory) {
        //会走到这个分支
		if (initializer instanceof ServletRegistrationBean) {
            //这里返回的servlet也还是第3部分DispatcherServlet对应的bean
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
            //再点进去
			addServletContextInitializerBean(Servlet.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer).getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer, beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer).getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer, beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName, initializer, beanFactory,
					initializer);
		}
	}

	private void addServletContextInitializerBean(Class<?> type, String beanName, ServletContextInitializer initializer,
			ListableBeanFactory beanFactory, Object source) {
        //这里的initializers是个map,按照类型,bean对象进行加载,这里的type是javax.servlet.Servlet.class,value是上面的DispatcherServletRegistrationBean
		this.initializers.add(type, initializer);
		if (source != null) {
			// Mark the underlying source as seen in case it wraps an existing bean
            //将DispatcherServlet对应的bean加到这里
			this.seen.add(source);
		}
		if (logger.isTraceEnabled()) {
			String resourceDescription = getResourceDescription(beanName, beanFactory);
			int order = getOrder(initializer);
			logger.trace("Added existing " + type.getSimpleName() + " initializer bean '" + beanName + "'; order="
					+ order + ", resource=" + resourceDescription);
		}
	}

再回到ServletContextInitializerBeans的构造方法,接着看后面的

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		...... //这里的内容上面已经看过了,我们现在看下面这句,点进去
		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);
	}
protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
		//这句不用关注
		MultipartConfigElement multipartConfig = getMultipartConfig(beanFactory);
		//这句不用关注
		addAsRegistrationBean(beanFactory, Servlet.class, new ServletRegistrationBeanAdapter(multipartConfig));
		//点到这里去看看
		addAsRegistrationBean(beanFactory, Filter.class, new FilterRegistrationBeanAdapter());

		for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
			addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
					new ServletListenerRegistrationBeanAdapter());
		}
	}
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory, Class<T> type,
			Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
		//这里的beanType是 Filter.class,下面这句就是从beanFactory中获取所有类型为Filter.class的bean
		List<Map.Entry<String, B>> entries = getOrderedBeansOfType(beanFactory, beanType, this.seen);
		for (Entry<String, B> entry : entries) {
			String beanName = entry.getKey();
			B bean = entry.getValue();
            //将bean放置到this.seen里面
			if (this.seen.add(bean)) {
				// One that we haven't already seen
                //包装成RegistrationBean对象
				RegistrationBean registration = adapter.createRegistrationBean(beanName, bean, entries.size());
				int order = getOrder(bean);
				registration.setOrder(order);
                //同样放置到this.initializers里面
				this.initializers.add(type, registration);
				if (logger.isTraceEnabled()) {
					logger.trace("Created " + type.getSimpleName() + " initializer for bean '" + beanName + "'; order="
							+ order + ", resource=" + getResourceDescription(beanName, beanFactory));
				}
			}
		}
	}

再回到上面的addAdaptableBeans方法,看后面的

protected void addAdaptableBeans(ListableBeanFactory beanFactory) {
		......//这里刚才说过了
         //下面这部分不说了,这里基本和上面一样,不过处理的类型变成了ServletContextAttributeListener.class、ServletRequestListener.class、ServletRequestAttributeListener.class、HttpSessionAttributeListener.class、HttpSessionIdListener.class、HttpSessionListener.class、ServletContextListener.class这些类型
		for (Class<?> listenerType : ServletListenerRegistrationBean.getSupportedTypes()) {
			addAsRegistrationBean(beanFactory, EventListener.class, (Class<EventListener>) listenerType,
					new ServletListenerRegistrationBeanAdapter());
		}
	}

再回到ServletContextInitializerBeans的构造方法,接着看后面的

public ServletContextInitializerBeans(ListableBeanFactory beanFactory,
			Class<? extends ServletContextInitializer>... initializerTypes) {
		......//这里刚才都说过了,看下面
        //这里就是把上面所有获取到的相关的bean放置到this.sortedList中,下面我是我本地this.sortedList的截图
		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);
	}

这里ServletContextInitializerBeans的构造方法就完成了,再回过头去看看这个类的定义

public class ServletContextInitializerBeans extends AbstractCollection<ServletContextInitializer>

这个类继承了AbstractCollection类,那它就需要实现下面这个抽象方法

public abstract Iterator<E> iterator();

我们看看ServletContextInitializerBeans的iterator的方法

@Override
public Iterator<ServletContextInitializer> iterator() {
   return this.sortedList.iterator();
}

看到了吧,这就是返回上面的this.sortedList.iterator()

我们再次回到ServletWebServerApplicationContext的selfInitialize方法

private void selfInitialize(ServletContext servletContext) throws ServletException {
	......//这里上面都说过了
	//getServletContextInitializerBeans()这个方法就是构造了ServletContextInitializerBeans
	//这里的for循环也是调用了ServletContextInitializerBeans的iterator的方法,实际上遍历的也就是上面的this.sortedList
	for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
		//这里就是把在beanFactory中查找到的Servlet.class,filter.class,监听器等等添加到tomcat容器中,我们就只进到servlet里面去看看
		//进到DispatcherServletRegistrationBean里面去看看
		beans.onStartup(servletContext);
	}
}
//这个方法在DispatcherServletRegistrationBean的父类RegistrationBean中
	//所有的Servlet.class,filter.class,监听器都会走到这里
	public final void onStartup(ServletContext servletContext) throws ServletException {
		//这里是返回的表述,不关注
		String description = getDescription();
		if (!isEnabled()) {
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		//这里由不同的子类去实现,DispatcherServletRegistrationBean会调用到ServletRegistrationBean中
		register(description, servletContext);
	}
//这个方法是在ServletRegistrationBean这个类中
	@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
		String name = getServletName();
		//这里的servletContext上面的ApplicationContextFacade的对象,这里就会将DispatcherServlet的bean对象加载到TomcatEmbeddedContext中,后续所有的http请求最后都会流转到DispatcherServlet去进行具体的分发
		return servletContext.addServlet(name, this.servlet);
	}

到这里Servlet.class,filter.class,监听器就全部加载到tomcat中去

5、创建RequestMappingHandlerMapping

再看看上面的META-INF/spring.factories文件

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
......
#现在我们重点来看这个类
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
......

具体的加载和上面部分都类似,就不展开了,直接看我们需要的

//这里会创建RequestMappingHandlerMapping的bean
		@Bean
		@Primary
		@Override
		public RequestMappingHandlerMapping requestMappingHandlerMapping(
				@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
				@Qualifier("mvcConversionService") FormattingConversionService conversionService,
				@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
			// Must be @Primary for MvcUriComponentsBuilder to work
			return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService,
					resourceUrlProvider);

看下这个类的继承图

RequestMappingHandlerMapping实现了InitializingBean接口,会在bean对象创建后的invokeInitMethods方法中调用afterPropertiesSet方法,最终会调用的AbstractHandlerMethodMapping的afterPropertiesSet方法中

@Override
	public void afterPropertiesSet() {
		initHandlerMethods();
	}

	/**
	 * Scan beans in the ApplicationContext, detect and register handler methods.
	 * @see #getCandidateBeanNames()
	 * @see #processCandidateBean
	 * @see #handlerMethodsInitialized
	 */
	protected void initHandlerMethods() {
        //这里查找beanFactory中的所有bean进行遍历
		for (String beanName : getCandidateBeanNames()) {
			if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                //点到这里去看看
				processCandidateBean(beanName);
			}
		}
		handlerMethodsInitialized(getHandlerMethods());
	}
protected void processCandidateBean(String beanName) {
		Class<?> beanType = null;
		try {
			//根据beanName获取对应bean的Class对象
			beanType = obtainApplicationContext().getType(beanName);
		}
		catch (Throwable ex) {
			// An unresolvable bean type, probably from a lazy bean - let's ignore it.
			if (logger.isTraceEnabled()) {
				logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
			}
		}
		//判断类上是否有Controller.class,RequestMapping.class注解
		if (beanType != null && isHandler(beanType)) {
			//这里就会解析beanName上的所有方法进行遍历,查找有 RequestMapping.class注解的方法,创建RequestMappingInfo对象,放置到registry属性中(在AbstractHandlerMethodMapping)中,这样我们所有定义的controller中的http请求就会全部被扫描到
			detectHandlerMethods(beanName);
		}
	}

6、加载RequestMappingHandlerMapping到DispatcherServlet中

在我们第一次请求的时候,会执行到DispatcherServlet的initStrategies方法,这个方法只会执行一次

protected void initStrategies(ApplicationContext context) {
		......
		这就会加载之前查找到的RequestMappingHandlerMapping中的我们定义的controller
		initHandlerMappings(context);
		......
	}

会调用到这里

private void initHandlerMappings(ApplicationContext context) {
		this.handlerMappings = null;

		if (this.detectAllHandlerMappings) {
			// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
			//这里会在beanFactory中查找所有HandlerMapping.class的bean,其中就包含我们第5部分的RequestMappingHandlerMapping
			Map<String, HandlerMapping> matchingBeans =
					BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
			if (!matchingBeans.isEmpty()) {
			//将所有查找到的放置到handlerMappings中
				this.handlerMappings = new ArrayList<>(matchingBeans.values());
				// We keep HandlerMappings in sorted order.
				AnnotationAwareOrderComparator.sort(this.handlerMappings);
			}
		}
		......
	}

当我们浏览器请求的时候,最终会走到DispatcherServlet的doDispatch的方法,处理我们的请求并返回,简单看看

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		......

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
                //在这里,就会请求request的请求路径去查找实际要执行的controller的方法,点进去看看
				mappedHandler = getHandler(processedRequest);
				......
	}
@Nullable
	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
            //这其实就是根据遍历,查找对应的路径,这个this.handlerMappings就是就是在initHandlerMappings方法中赋值的
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

到这里,springboot 加载web容器的整个流程基本就算完成了,这块涉及的东西比较多,所以说的可能比较粗,大家见谅。

(0)

相关推荐

  • Spring Boot如何移除内嵌Tomcat,使用非web方式启动

    前言:当我们使用Spring Boot编写了一个批处理应用程序,该程序只是用于后台跑批数据,此时不需要内嵌的tomcat,简化启动方式使用非web方式启动项目,步骤如下: 1.修改pom.xml文件 在pom.xml文件中去除内嵌tomcat,添加servlet依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</

  • 解决SpringBoot webSocket 资源无法加载、tomcat启动报错的问题

    问题描述: 1. 项目集成WebSocket,且打包发布tomcat时出现websocket is already in CLOSING or CLOSE state这样的问题,建议参考"解决方法二",但是"解决方法一"请要了解查看 ,因为解决方法二是在一的基础上进行更正 2. 如果出现javax.websocket.server.ServerContainer not available这样的错误,请参考"解决方法一"中步骤3 解决方法一:(常

  • spring-boot-starter-web更换默认Tomcat容器的方法

    Spring Boot支持容器的自动配置,默认是Tomcat,当然我们也是可以进行修改的. 我们知道Spring Boot支持容器的自动配置,默认是Tomcat,当然我们也是可以进行修改的: 1.首先我们排除spring-boot-starter-web依赖中的Tomcat:在pom文件中排除tomcat的starter <dependency> <groupId>org.springframework.boot</groupId> <artifactId>

  • spring boot 加载web容器tomcat流程源码分析

    我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.1</version> <relativePath/> <!-- lookup parent fr

  • SpringBoot 创建web项目并部署到外部Tomcat

    前言 使用SpringBoot来开发项目相对于传统模式,要快速优雅许多,相信目前国内绝大部分web项目的开发还没有使用SpringBoot来做,如果你正需要开发一个web项目,不妨尝试使用SpringBoot来做. 本身SpringBoot是内嵌了web服务器,不需要单独的Tomcat,但是实际生产环境中,如果是web项目,Tomcat肯定是运维部门部署好了的,这个Tomcat,做了一些个性化的设置,开发出来的项目需要部署到这个Tomcat,如果是使用SpringBoot开发web服务,我认为可

  • spring boot实战之内嵌容器tomcat配置

    本文介绍了spring boot实战之内嵌容器tomcat配置,分享给大家,具体如下: 默认容器 spring boot默认web程序启用tomcat内嵌容器tomcat,监听8080端口,servletPath默认为 / 通过需要用到的就是端口.上下文路径的修改,在spring boot中其修改方法及其简单: 在资源文件中配置: server.port=9090 server.contextPath=/lkl 启动spring boot 2015-10-04 00:06:55.768 INFO

  • spring boot加载freemarker模板路径的方法

    1,之前用的eclipse开发工具来加载spring boot加载freemarker模板路径,现在换用idea却不能使用了,所以来记录一下 加载freemarker模板三种方式,如下 public void setClassForTemplateLoading(Class clazz, String pathPrefix); public void setDirectoryForTemplateLoading(File dir) throws IOException; public void

  • RocketMQ之NameServer架构设计及启动关闭流程源码分析

    目录 NameServer 1.架构设计 2.核心类与配置 NamesrvController NamesrvConfig NettyServerConfig RouteInfoManager 3.启动与关闭流程 3.1.步骤一 3.2.步骤二 3.3.步骤三 NameServer 1.架构设计 消息中间件的设计思路一般都是基于主题订阅与发布的机制,RocketMQ也不例外.RocketMQ中,消息生产者(Producer)发送某主题的消息到消息服务器,消息服务器对消息进行持久化存储,而消息消费

  • spring boot加载资源路径配置和classpath问题解决

    1.spring boot默认加载文件的路径: /META-INF/resources/ /resources/ /static/ /public/ 我们也可以从spring boot源码也可以看到: private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classp

  • Spring Boot加载配置文件的完整步骤

    前言 本文针对版本2.2.0.RELEASE来分析SpringBoot的配置处理源码,通过查看SpringBoot的源码来弄清楚一些常见的问题比如: SpringBoot从哪里开始加载配置文件? SpringBoot从哪些地方加载配置文件? SpringBoot是如何支持yaml和properties类型的配置文件? 如果要支持json配置应该如何做? SpringBoot的配置优先级是怎么样的? placeholder是如何被解析的? 带着我们的问题一起去看一下SpringBoot配置相关的源

  • spring boot加载第三方jar包的配置文件的方法

    前言 今天收到一封邮件,大概内容如下:spring boot鼓励去配置化,那么怎么将第三方jar包中的xml去配置化了? 其实,这个问题,在前面的文章中也有提到,http://www.jb51.net/article/125700.htm 下面,我们就以Quartz定时任务为例,单独对这个问题来进行说明,如何实现去配置化. 如果不使用spring boot,我们配置一个简单的定时任务时,需要引入以下配置文件: <!-- 配置需要定时执行的任务类以及方法 --> <bean id=&quo

  • 详解Spring Boot加载properties和yml配置文件

    一.系统启动后注入配置 package com.example.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframewo

  • Spring Boot如何使用Undertow代替Tomcat

    1. Undertow 简介 Undertow 是一个采用 Java 开发的灵活的高性能 Web 服务器,提供包括阻塞和基于 NIO 的非堵塞机制.Undertow 是红帽公司的开源产品,是 Wildfly 默认的 Web 服务器.Undertow 提供一个基础的架构用来构建 Web 服务器,这是一个完全为嵌入式设计的项目,提供易用的构建器 API,完全向下兼容 Java EE Servlet 3.1 和低级非堵塞的处理器. 2. Undertow特点 高性能 在多款同类产品的压测中,在高并发情

  • 详解Android布局加载流程源码

    一.首先看布局层次 看这么几张图 我们会发现DecorView里面包裹的内容可能会随着不同的情况而变化,但是在Decor之前的层次关系都是固定的.即Activity包裹PhoneWindow,PhoneWindow包裹DecorView.接下来我们首先看一下三者分别是如何创建的. 二.Activity是如何创建的 首先看到入口类ActivityThread的performLaunchActivity方法: private Activity performLaunchActivity(Activi

随机推荐