Spring Boot是如何启动Web Container和Spring MVC的

Spring Boot一般使用starter来整合第三方框架与扩展功能。其套路是:

  1. 定义一个XXXXAutoConfiguration的类。其中XXXX一般可以写成所需集成的功能或框架。其上可定义@Conditional相关注解,指定何时可初始化该功能。
  2. 在该类上@Import一个ImportBeanDefinitionRegistrar接口的实现类来注入指定的Bean。
  3. 添加自定义的BeanPostProcessor来实现ImportBeanDefinitionRegistrar接口,以便在Bean初始化之前或之后完成配置功能或者初始化某些依赖功能。
  4. 在META-INF/spring.factories中定义org.springframework.boot.autoconfigure.EnableAutoConfiguration=<XXXXAutoConfiguration>的完整类名,包括所在包名。Spring Boot在初始化时,会自动扫描spring.factories文件中的相关类,然后初始化并注入到IoC容器中。

SpringBoot整合并启动Spring MVC,基本上也符合这个套路。整合Spring MVC需要增加以下依赖:

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

该依赖中默认包含了Tomcat的依赖。

下面对整合过程进行分析,启动Spring MVC,首先需要一个Servlet Container,以便与Client端转换Request和Response。

1. Servlet Container是在哪里启动的?

我们在spring-boot-autoconfigure包的META-INF/spring.factories文件中找到了Servlet Container的配置类:EmbeddedServletContainerAutoConfiguration.class。其实现如下:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {
    ......
}

从该类的注解可以看出,使用@Import导入了BeanPostProcessorRegistrar.class类。从该类中可以看出,该类首先检查是否存在自定义的EmbeddedServletContainerCustomizerBeanPostProcessor,如果没有,则注册默认的EmbeddedServletContainerCustomizerBeanPostProcessor类。其实现如下:

	public static class BeanPostProcessorsRegistrar
			implements ImportBeanDefinitionRegistrar, BeanFactoryAware {

		private ConfigurableListableBeanFactory beanFactory;

		@Override
		public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
			if (beanFactory instanceof ConfigurableListableBeanFactory) {
				this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
			}
		}

		@Override
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
			if (this.beanFactory == null) {
				return;
			}
			registerSyntheticBeanIfMissing(registry,
					"embeddedServletContainerCustomizerBeanPostProcessor",
					EmbeddedServletContainerCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry,
					"errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry,
				String name, Class<?> beanClass) {
			if (ObjectUtils.isEmpty(
					this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
				beanDefinition.setSynthetic(true);
				registry.registerBeanDefinition(name, beanDefinition);
			}
		}

	}

EmbeddedServletContainerCustomizerBeanPostProcessor在EmbeddedServletContainer对象初始化之前,进行必要的参数配置。其代码实现如下:

	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		if (bean instanceof ConfigurableEmbeddedServletContainer) {
			postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
		}
		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}

	private void postProcessBeforeInitialization(
			ConfigurableEmbeddedServletContainer bean) {
		for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
			customizer.customize(bean);
		}
	}

	private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
		if (this.customizers == null) {
			// Look up does not include the parent context
			this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
					this.beanFactory
							.getBeansOfType(EmbeddedServletContainerCustomizer.class,
									false, false)
							.values());
			Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}

在Spring的容器配置加载完之后,就会执行EmbeddedWebApplicationContext.refresh()方法。在其onRefresh方法中调用createEmbeddedServletContainer()方法。在此方法中初始化EmbeddedServletContainer:

首先,需要获取EmbeddedServletContainerFactory:

	protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
		// Use bean names so that we don't consider the hierarchy
		String[] beanNames = getBeanFactory()
				.getBeanNamesForType(EmbeddedServletContainerFactory.class);
		if (beanNames.length == 0) {
			throw new ApplicationContextException(
					"Unable to start EmbeddedWebApplicationContext due to missing "
							+ "EmbeddedServletContainerFactory bean.");
		}
		if (beanNames.length > 1) {
			throw new ApplicationContextException(
					"Unable to start EmbeddedWebApplicationContext due to multiple "
							+ "EmbeddedServletContainerFactory beans : "
							+ StringUtils.arrayToCommaDelimitedString(beanNames));
		}
		return getBeanFactory().getBean(beanNames[0],
				EmbeddedServletContainerFactory.class);
	}

那EmbeddedServletContainerFactory的默认实现有哪些呢?还是在一开始的EmbeddedServletContainerAutoConfiguration中:

	/**
	 * Nested configuration if Tomcat is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {

		@Bean
		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
			return new TomcatEmbeddedServletContainerFactory();
		}

	}

	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
			return new JettyEmbeddedServletContainerFactory();
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
			return new UndertowEmbeddedServletContainerFactory();
		}

	}

如果实现第三方Factory,与上类似。根据Factory获取相应的ServletContainer。该container初始化之后回调onStartup()。EmbeddedWebApplicationContext在onStartup回调中完成SpringMvc功能注入。

在ServletContextInitializer类的selfInitialize方法中获取到所有ServletContextInitializer对象,并调用其onStartup方法。

2. 初始化Servlet,Filter等

以Servlet为例,ServletRegistrationBean的onStartup实现如下:

	@Override
	public void onStartup(ServletContext servletContext) throws ServletException {
		Assert.notNull(this.servlet, "Servlet must not be null");
		String name = getServletName();
		if (!isEnabled()) {
			logger.info("Servlet " + name + " was not registered (disabled)");
			return;
		}
		logger.info("Mapping servlet: '" + name + "' to " + this.urlMappings);
		Dynamic added = servletContext.addServlet(name, this.servlet);
		if (added == null) {
			logger.info("Servlet " + name + " was not registered "
					+ "(possibly already registered?)");
			return;
		}
		configure(added);
	}

	/**
	 * Configure registration settings. Subclasses can override this method to perform
	 * additional configuration if required.
	 * @param registration the registration
	 */
	protected void configure(ServletRegistration.Dynamic registration) {
		super.configure(registration);
		String[] urlMapping = this.urlMappings
				.toArray(new String[this.urlMappings.size()]);
		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);
		}
	}

其他组件如Filter等,初始化方式类似。

3. 初始化Spring MVC的DispatcherServlet:

DispatcherServlet是由DispatcherServletAutoConfiguration来启动,其实现如下:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration
@ConditionalOnWebApplication
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(EmbeddedServletContainerAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
    ......
}

从注解@AutoCondifureAfter可以看出,该类是在EmbeddedServletContainerAutoConfiguration之后初始化。

其DispatcherServlet定义如下:

		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public ServletRegistrationBean dispatcherServletRegistration(
				DispatcherServlet dispatcherServlet) {
			ServletRegistrationBean registration = new ServletRegistrationBean(
					dispatcherServlet, this.serverProperties.getServletMapping());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(
					this.webMvcProperties.getServlet().getLoadOnStartup());
			if (this.multipartConfig != null) {
				registration.setMultipartConfig(this.multipartConfig);
			}
			return registration;
		}

初始化了DispatcherServlet之后,Spring MVC也就启动起来了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值