SpringMVC从配置初始化到HTTP请求全流程解析

本文详细解析了SpringMVC从配置初始化到HTTP请求的完整流程。首先,介绍了DispatcherServlet的初始化,涉及ServletWebServerFactoryAutoConfiguration和DispatcherServletAutoConfiguration,以及它们如何配置servlet容器。接着,探讨了MVC配置WebMvcAutoConfiguration,重点关注了EnableWebMvcConfiguration和WebMvcAutoConfigurationAdapter。最后,深入分析了DispatcherServlet的请求处理,包括HandlerMapping、HandlerAdapter的使用,以及请求处理的各个阶段。文章深入浅出地揭示了SpringMVC的工作原理。
摘要由CSDN通过智能技术生成

1. 初始化配置

SpringMVC的请求处理依赖DispatcherServlet,而bean的管理依赖自身的Spring容器,配置管理现在依赖于SpringBoot.

  • 对于在servlet 3.0之前,Spring要应用于Web项目,启动的过程要依赖容器,在容器启动完成后触发Spring的初始化,而Web容器启动状态的监听是通过Listener来完成的,具体的接口就是ServletContextListener,其属于Servlet的规范,Spring的ContextLoaderListener类实现了ServletContextListener接口,从而我们可以想到,如果要在Web容器启动后自动初始化Spring则需要把ServletContextListener配置到web.xml中去。
  • SpringBoot的诞生改变了Spring的初始化方式,SpringBoot首先启动Spring容器,然后再根据配置启动嵌入的容器。(其实早在SpringBoot诞生之前就可以这样干了, 我们可以利用ClassPathXmlApplicationContext或者XmlBeanDefinitionReader 来读取Spring bean的配置文件,从而进行Spring的初始化)。
  • 那么SpringMVC的DispatcherServlet是怎么初始化的呢。首先它是一个Servlet,在Servlet 3.0之前,直接在web.xml中配置servlet,因为servlet规范中Listener是先于servlet初始化的,所以在初始化servlet的时候Spring容器已经启动,从而当请求到达DispatcherServlet后可以利用Spring容器中的bean。
  • 在servlet3.0即之后,由于可以编程式的动态实例化servlet,其通过ServletRegistration.Dynamic类注册新的servlet,所以在SpringBoot中DispacherServlet的注入就可以直接交给autoConfigure模块的DispatcherServletAutoConfiguration类来完成了。其实servlet新规范中还有一套注解用来取代以前的web.xml配置,在SpringBoot的场景下也可以使用这套注解,Spring完全可以给DispatcherServlet加上@WebServlet(配合@ServletComponentScan)注解,在Spring初始化web容器之后,容器会自动装配上servlet,但是看了下SpringBoot的代码并没有这样做。

SpringMVC的配置包含两部分,一部分是MVC相关配置,一部分是DispatcherServlet与容器相关的配置。

1.1 servlet容器相关配置DispatcherServletAutoConfiguration

在SpringBoot中,是通过DispatcherServletAutoConfiguration来对DispatcherServlet进行配置的。
首先我们看一下类的配置:

@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) //以最高优先级启动
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET) //应用是基于servlet的才启动
@ConditionalOnClass(DispatcherServlet.class)//DispatcherServlet必须要存在
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class) //在ServletWebServerFactoryAutoConfiguration初始化之后
public class DispatcherServletAutoConfiguration {
   
}

这里首先我们要来关心一下ServletWebServerFactoryAutoConfiguration,因为DispatcherServlet的配置在它之后。

1.1.1 ServletWebServerFactoryAutoConfiguration

@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) //最高优先级启动
@ConditionalOnClass(ServletRequest.class) //ServletRequest类存在
@ConditionalOnWebApplication(type = Type.SERVLET) //servlet服务
@EnableConfigurationProperties(ServerProperties.class) //引入ServerProperties配置
@Import({
    //导入以下类 (@Import将对于的类实例化后注入IOC容器)
      ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class 
 })
public class ServletWebServerFactoryAutoConfiguration {
   
}

很明显可以看出,该类是为servlet容器相关配置来服务的,它要求必须存在ServletRequest类,应用类型是SERVLET。这里我们注意到首先引入了BeanPostProcessorsRegistrar,应该是要左一些bean初始化的后置处理,然后引入了具体的servlet容器的实现配置。

具体看下这个自动配置类做了些什么:

//实例化ServletWebServerFactoryCustomizer 
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
   
   return new ServletWebServerFactoryCustomizer(serverProperties);
}
//在有Tomcat类存在(即引入了tomcat的jar)的情况下实例化TomcatServletWebServerFactoryCustomizer 
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
      ServerProperties serverProperties) {
   
   return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}


/**
 * 通过实现ImportBeanDefinitionRegistrar接口来实现早期的WebServerFactoryCustomizerBeanPostProcessor注入
 * 其实就是注入一个bean后置处理器
 * */

public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
   

   private ConfigurableListableBeanFactory beanFactory;

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

   //注册web服务自定义配置后置处理器和错误页面后置处理器
   @Override
   public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
         BeanDefinitionRegistry registry) {
   
      if (this.beanFactory == null) {
   
         return;
      }
      registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
            WebServerFactoryCustomizerBeanPostProcessor.class);
      registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
            ErrorPageRegistrarBeanPostProcessor.class);
   }

   //注入类具体实现
   private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
   
   //在BeanFactory中找不到对应的bean时,则将对应Class的bean注入
      if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
   
         RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
         beanDefinition.setSynthetic(true);
         registry.registerBeanDefinition(name, beanDefinition);
      }
   }

}

1.1.2 ServletWebServerFactoryConfiguration

再来看引入的ServletWebServerFactoryConfiguration具体做了哪些配置:

/**servlet web服务器的配置类
这些类应该被Import到一个常规的自动配置类中以此来保证他们的执行顺序。
这里就是被引入到了ServletWebServerFactoryAutoConfiguration 类里面。
**/
@Configuration
class ServletWebServerFactoryConfiguration {
   

   //嵌入tomcat
   @Configuration
   @ConditionalOnClass({
    Servlet.class, Tomcat.class, UpgradeProtocol.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedTomcat {
   
      @Bean
      public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
   
         return new TomcatServletWebServerFactory();
      }
   }

   /**
    * jetty的配置.
    */
   @Configuration
   @ConditionalOnClass({
    Servlet.class, Server.class, Loader.class, WebAppContext.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedJetty {
   
      @Bean
      public JettyServletWebServerFactory JettyServletWebServerFactory() {
   
         return new JettyServletWebServerFactory();
      }
   }

   /**
    * Undertow配置.
    */
   @Configuration
   @ConditionalOnClass({
    Servlet.class, Undertow.class, SslClientAuthMode.class })
   @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
   public static class EmbeddedUndertow {
   
      @Bean
      public UndertowServletWebServerFactory undertowServletWebServerFactory() {
   
         return new UndertowServletWebServerFactory();
      }
   }
}

从以上代码可以看出,ServletWebServerFactoryConfiguration其实就是根据classpath引入的jar包判断并实例化对应的servlet容器,分别是Tomcat, Jetty,Undertow。主要是分别初始化他们对应的xxServlectWebServerFactory。

1.1.3 ServletWebServerFactoryAutoConfiguration 总结

该类做了以下的事情:
1.初始化TomcatServletWebServerFactoryCustomizer
2.xxServletWebServerFactoryCustomizer通过ServerProperties 进行服务器客户化配置,配置文件前缀是server,例如server.port=8080
3.注入webServerFactoryCustomizerBeanPostProcessor和errorPageRegistrarBeanPostProcessor 后置处理器
4.初始化TomcatServletWebServerFactory(或者其它容器)

1.2 真正的DispatcherServletAutoConfiguration配置

看完了servlet容器相关的配置,然后我们再来看一下DispatcherServletAutoConfiguration做的事情:

1.2.1 实例化DispatcherServlet并传入配置

	@Configuration
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties({
    HttpProperties.class, WebMvcProperties.class })
	protected static class DispatcherServletConfiguration {
   
	
		private final HttpProperties httpProperties;
		private final WebMvcProperties webMvcProperties;
        //注入配置
		public DispatcherServletConfiguration(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
   
			this.httpProperties = httpProperties;
			this.webMvcProperties = webMvcProperties;
		}
		//实例化DispatcherServlet
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet() {
   
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(this.webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(this.webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet
					.setThrowExceptionIfNoHandlerFound(this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
			dispatcherServlet.setEnableLoggingRequestDetails(this.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;
		}
	}

1.2.2 第二步 :将DispatcherServlet实例注入Servlet容器

@Configuration
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
   
	private final WebMvcProperties webMvcProperties;
    //文件上传相关配置
	private final MultipartConfigElement multipartConfig;
    //传入MVC配置和文件上传配置
	public DispatcherServletRegistrationConfiguration(WebMvcProperties webMvcProperties,
			ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
   
		this.webMvcProperties = webMvcProperties;
		this.multipartConfig = multipartConfigProvider.getIfAvailable();
	}
    //将servlet动态配置到ServletContext中, 这是servlet 3.0+的规范
	@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
	@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
	public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet) {
   
		DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
				this.webMvcProperties.getServlet().getPath());
		registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
		registration.setLoadOnStartup(this.webMvcProperties.getServlet().getLoadOnStartup());
		if (this.multipartConfig != null) {
   
			registration.setMultipartConfig(this.multipartConfig);
		}
		return registration;
	}
}

这里我们可以详细看一下DispatcherServletRegistrationBean是如何实现Servlet的注入的,首先看一下它的依赖图:
依赖图
我们先看一下顶层接口的定义:

/**
 * 用于编程式配置Servlet 3.0+的ServletContext,它不像WebApplicationInitializer,
 * 实现了本接口(但是没有实现WebApplicationInitializer)将不会被SpringServletContainerInitializer(它实现了servlet 3.0规范的ServletContainerInitializer接口)主动发现,
 * 因此后续也不会被Servlet容器自动的启动
 * <p>
 * 这个接口的设计初衷就是要让Spring来管理它的实现而部署Servlet容器
 **/
@FunctionalInterface
public interface ServletContextInitializer {
   
    /**
	 给ServletContext配置任何的servlet、filter、listener、context-params 以及 attribute等必要的初始化条件。 
	 **/
	void onStartup(ServletContext servletContext) throws ServletException;
}

总结一下就是ServletContextInitializer是Spring提供的用来实现动态扩展ServletContext的接口,它的实现由Spring管理,并非Servlet容器。
它的实现RegistrationBean抽象类如下:

public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
   
	private int order = Ordered.LOWEST_PRECEDENCE;
	private boolean enabled = true;

	@Override
	public final void onStartup(ServletContext servletContext) throws ServletException {
   
		String description = getDescription();
		if (!isEnabled()) {
   
			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
			return;
		}
		register(description, servletContext);
	}
	//返回一个本次注册的描述
	protected abstract String getDescription();
    //注册
	protected abstract void register(String description, ServletContext servletContext);
    //--------setter and getter-----------------
}

这个类RegistrationBean其实也很简单,在这一层它增加了两个东西,一个是注册的优先级,默认最低,一个是注册是否开起的标志,实际上的注册动作通过模板模式让子类来提供具体的实现。
其子类 DynamicRegistrationBean的实现如下:

/**
基于Servlet 3.0+ javax.servlet.Registration.Dynamic 类实现动态注册的基础类。
这里 Registration就是servlet的规范了,而它的实现由具体的servlet容器来提供,例如tomcat
 */
public abstract class DynamicRegistrationBean<D extends Registration.Dynamic> extends RegistrationBean {
   

    //servlet name,如果不指定,后面会通过getOrDeduceName进行推导,其实就是disparcherServlet
	private String name;
    //servlet是否开启异步  3.0+ 规范
    //这个很重要,说明DispatcherServlet默认是开启异步请求功能的
	private boolean asyncSupported = true;
    //servlet初始化参数
	private Map<String, String> initParameters = new LinkedHashMap<>();

	/**以上参数的set get方法,此处省略………… **/

   //对父类register的实现
	@Override
	protected final void register(String description, ServletContext servletContext) {
   
	    //注册,委托servletContext完成注册
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
   
			logger.info(
					StringUtils.capitalize(description) + " was not registered " + "(possibly already registered?)");
			return;
		}
		configure(registration);
	}

   //同样,具体的注册方法还是交由子类来实现,并且会返回用于注册的Registration.Dynamic
	protected abstract D addRegistration(String description, ServletContext servletContext);

   //配置异步和初始化参数
	protected void configure(D registration) {
   
		registration.setAsyncSupported(this.asyncSupported);
		if (!this.initParameters.isEmpty()) {
   
			registration.setInitParameters(this.initParameters);
		}
	}
    //…………省略不重要代码…………
}

在这里统一管理了servlet是否异步模式,以及提供了初始化参数的配置。
再看看addRegistration是如何实现的 。到目前为止,以上的抽象类是供所有的Filter/Listener/Servlet等注册使用的,再往下就是具体的某种类型注册的实现了,这里即ServletRegistrationBean。

/**
 * 一个ServletContextInitializer的实例,用于注册Servlet到Servlet 3.0+容器。
 * 类似于ServletContext提供的ServletContext.addServlet(String, Servlet)特性,但是对Spring Bean更友好。
 * <p>
 * setServlet(Servlet servlet)方法在onStartup方法被调用前必须先指定它。
 * URL mapping可以用setUrlMappings来配置,或者当配置/*时直接省略(除非alwaysMapUrl被设置为false), servlet 名称如果没有指定则会被推断得出。
 */
public class ServletRegistrationBean<T extends Servlet> extends DynamicRegistrationBean<ServletRegistration.Dynamic> {
   
    //当没有指定urlMappings的时候默认使用改urlMapping
	private static final String[] DEFAULT_MAPPINGS = {
    "/*" };
    //本次要注入的servlet
	private T servlet;
    //可以配置urlMapping
	private Set<String> urlMappings = new LinkedHashSet<>();
    //是否必须指定urlMapping(也不知道干嘛的,后面再看)
	private boolean alwaysMapUrl = true;
    //是否启动即加载
	private int loadOnStartup = -1;
    //文件上传配置
	private MultipartConfigElement multipartConfig;

	//构造方法
	public ServletRegistrationBean() {
   
	}
	//构造方法
	public ServletRegistrationBean(T servlet, String... urlMappings) {
   
		this(servlet, true, urlMappings);
	}
	//构造方法
	public ServletRegistrationBean(T servlet, boolean alwaysMapUrl, String... urlMappings) {
   
		Assert.notNull(servlet, "Servlet must not be null");
		Assert.notNull(urlMappings, "UrlMappings must not be null");
		this.servlet = servlet;
		this.alwaysMapUrl = alwaysMapUrl;
		this.urlMappings.addAll(Arrays.asList(urlMappings));
	}

	//设置要注册的servlet
	public void setServlet(T servlet) {
   
		Assert.notNull(servlet, "Servlet must not be null");
		this.servlet = servlet;
	}
	public T getServlet() {
   
		return this.servlet;
	}

	/**  省略一些set get方法 **/

    //此处实现了父类的addRegistration方法,完成了servlet的最终注册,即通过ServletContext.addServlet方法
	@Override
	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
   
		String name = getServletName();
		return servletContext.addServlet(name, this.servlet);
	}

	//该方法是基于addRegistration返回的ServletRegistration.Dynamic来做进一步的配置
	@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;
		<
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值