首页 > 文章列表 > SpringBoot中WEB的启动流程是什么

SpringBoot中WEB的启动流程是什么

web springboot
122 2023-05-17

SpringBoot中WEB的启动流程是什么

想必大家都体验过springboot的便捷,以前想要运行web项目,我们首先需要将项目打成war包,然后再运行Tomcat启动项目,不过自从有了springboot,我们可以像启动jar包一样简单的启动一个web项目,今天我们就来分析下springboot启动web项目整个流程。

老规矩,我们从spring.factories文件开始。

spring-boot-starter-web下没有spring.factories文件

所以我们从spring-boot-autoconfigure下的spring.factories开始

一、DispatcherServlet的注册

1.1 把DispatcherServlet注入IOC容器

DispatcherServlet是通过DispatcherServletAutoConfiguration注册的

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)

@Configuration(proxyBeanMethods = false)

@ConditionalOnWebApplication(type = Type.SERVLET)

@ConditionalOnClass(DispatcherServlet.class)

@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)

public class DispatcherServletAutoConfiguration {



	public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";



	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";



	@Configuration(proxyBeanMethods = false)

	@Conditional(DefaultDispatcherServletCondition.class)

	@ConditionalOnClass(ServletRegistration.class)

	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })

	protected static class DispatcherServletConfiguration {



		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)

		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {

			DispatcherServlet dispatcherServlet = new DispatcherServlet();

			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());

			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());

			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());

			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());

			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.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)

	@Import(DispatcherServletConfiguration.class)

	protected static class DispatcherServletRegistrationConfiguration {



		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)

		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)

		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;

		}



	}



	@Order(Ordered.LOWEST_PRECEDENCE - 10)

	private static class DefaultDispatcherServletCondition extends SpringBootCondition {



		@Override

		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {

			ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");

			ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

			List<String> dispatchServletBeans = Arrays

					.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));

			if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {

				return ConditionOutcome

						.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

			}

			if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {

				return ConditionOutcome.noMatch(

						message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

			}

			if (dispatchServletBeans.isEmpty()) {

				return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());

			}

			return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")

					.items(Style.QUOTE, dispatchServletBeans)

					.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

		}



	}



	@Order(Ordered.LOWEST_PRECEDENCE - 10)

	private static class DispatcherServletRegistrationCondition extends SpringBootCondition {



		@Override

		public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {

			ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

			ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);

			if (!outcome.isMatch()) {

				return outcome;

			}

			return checkServletRegistration(beanFactory);

		}



		private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {

			List<String> servlets = Arrays

					.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));

			boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);

			if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {

				return ConditionOutcome.noMatch(

						startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));

			}

			return ConditionOutcome.match();

		}



		private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {

			ConditionMessage.Builder message = startMessage();

			List<String> registrations = Arrays

					.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));

			boolean containsDispatcherRegistrationBean = beanFactory

					.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);

			if (registrations.isEmpty()) {

				if (containsDispatcherRegistrationBean) {

					return ConditionOutcome.noMatch(message.found("non servlet registration bean")

							.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));

				}

				return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());

			}

			if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {

				return ConditionOutcome.noMatch(message.found("servlet registration bean")

						.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));

			}

			if (containsDispatcherRegistrationBean) {

				return ConditionOutcome.noMatch(message.found("non servlet registration bean")

						.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));

			}

			return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)

					.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));

		}



		private ConditionMessage.Builder startMessage() {

			return ConditionMessage.forCondition("DispatcherServlet Registration");

		}



	}



}

这也是SpringBoot中IOC容器和WEB容器是同一个的原因

Spring把DispatcherServlet放到容器中后,在DispatcherServlet的初始化中会执行ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,而其postProcessBeforeInitialization底层如下

private void invokeAwareInterfaces(Object bean) {

		if (bean instanceof EnvironmentAware) {

			((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());

		}

		if (bean instanceof EmbeddedValueResolverAware) {

			((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);

		}

		if (bean instanceof ResourceLoaderAware) {

			((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);

		}

		if (bean instanceof ApplicationEventPublisherAware) {

			((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);

		}

		if (bean instanceof MessageSourceAware) {

			((MessageSourceAware) bean).setMessageSource(this.applicationContext);

		}

		if (bean instanceof ApplicationContextAware) {

			((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);

		}

	}

而DispatcherServlet是一个ApplicationContextAware,所以会执行其setApplicationContext方法,设置其属性webApplicationContext

@Override

	public void setApplicationContext(ApplicationContext applicationContext) {

	   //传入ioc容器

		if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {

			this.webApplicationContext = (WebApplicationContext) applicationContext;

			this.webApplicationContextInjected = true;

		}

	}

所以在web容器启动过程会把web容器设置成和ioc容器一样,springMVC容器创建代码如下,参考文章springMVC全注解启动和容器的初始化

protected WebApplicationContext initWebApplicationContext() {

		WebApplicationContext rootContext =

				WebApplicationContextUtils.getWebApplicationContext(getServletContext());

		WebApplicationContext wac = null;

        //因为webApplicationContext这里有值了,所以会进入这里

		if (this.webApplicationContext != null) {

	        //把web容器设置成和ioc容器一样

			wac = this.webApplicationContext;

			if (wac instanceof ConfigurableWebApplicationContext) {

				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;

				if (!cwac.isActive()) {

					if (cwac.getParent() == null) 

						cwac.setParent(rootContext);

					}

					configureAndRefreshWebApplicationContext(cwac);

				}

			}

		}

		if (wac == null) {

			wac = findWebApplicationContext();

			wac = createWebApplicationContext(rootContext);

		if (!this.refreshEventReceived) {

			synchronized (this.onRefreshMonitor) {

				onRefresh(wac);

		if (this.publishContext) {

			String attrName = getServletContextAttributeName();

			getServletContext().setAttribute(attrName, wac);

		return wac;

	}

这里可能要有人问了,为什么在springMVC环境中,this.webApplicationContext为null,因为在springMVC中DispatcherServlet没有通过spring容器管理

protected void registerDispatcherServlet(ServletContext servletContext) {

		String servletName = getServletName();

		Assert.hasLength(servletName, "getServletName() must not return null or empty");

        //创建web容器

		WebApplicationContext servletAppContext = createServletApplicationContext();

		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

        //创建DispatcherServlet对象

		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);

		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");

		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

        //把dispatcherServlet作为Servlet注册到上下文中

		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);

		if (registration == null) {

			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +

					"Check if there is another servlet registered under the same name.");

		}

        //容器在启动的时候加载这个servlet,其优先级为1(正数的值越小,该servlet的优先级越高,应用启动时就越先加载)

		registration.setLoadOnStartup(1);

		//设置Servlet映射mapping路径

		//getServletMappings()是模版方法,需要我们自己配置

		registration.addMapping(getServletMappings());

		//设置是否支持异步请求

		//isAsyncSupported默认是true

		registration.setAsyncSupported(isAsyncSupported());

        //处理自定义的Filter进来,一般我们Filter不这么加进来,而是自己@WebFilter,或者借助Spring,  

        //备注:这里添加进来的Filter都仅仅只拦截过滤上面注册的dispatchServlet

		Filter[] filters = getServletFilters();

		if (!ObjectUtils.isEmpty(filters)) {

			for (Filter filter : filters) {

				registerServletFilter(servletContext, filter);

			}

		}

		//这个很清楚:调用者若相对dispatcherServlet有自己更个性化的参数设置,复写此方法即可

		customizeRegistration(registration);

	}

1.2 把DispatcherServlet注入Servlet容器

SpringBoot中容器是AnnotationConfigServletWebServerApplicationContext,其onRefresh()方法如下

@Override

protected void onRefresh() {

	super.onRefresh();

	try {

		createWebServer(); //创建Servlet容器

	}

	catch (Throwable ex) {

		throw new ApplicationContextException("Unable to start web server", ex);

	}

}



private void createWebServer() {

		WebServer webServer = this.webServer;

		ServletContext servletContext = getServletContext();

		if (webServer == null && servletContext == null) {

			ServletWebServerFactory factory = getWebServerFactory(); 

			this.webServer = factory.getWebServer(getSelfInitializer());//创建容器,并执行所有ServletContextInitializer的onStartup

		}

		else if (servletContext != null) {

			try {

				getSelfInitializer().onStartup(servletContext);

			}

			catch (ServletException ex) {

				throw new ApplicationContextException("Cannot initialize servlet context", ex);

			}

		}

		initPropertySources();

	}

注意,这里他不会执行SpringServletContainerInitializer。

流程如下

1、通过getSelfInitializer()方法执行容器中所有的ServletContextInitializer

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

	}

}

而ServletContextInitializer有个子类ServletRegistrationBean,通过其addRegistration方法注入Servlet容器中

@Override

protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {

	String name = getServletName();

	return servletContext.addServlet(name, this.servlet);

}