了解Spring Boot启动原理的童鞋都知道Spring Boot程序在启动时,会加载classpath中的spring.factories文件,进行组件自动装配,那么Spring MVC是怎么被Spring Boot程序启动的,本文就对此进行简要分析。
在spring.factories文件中可以找到DispatcherServerlet类的自动配置类DispatcherServletAutoConfiguration
···
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//指定该配置类的加载顺序
@Configuration(proxyBeanMethods = false)//声明该类为配置类
@ConditionalOnWebApplication(type = Type.SERVLET)//指定配置类加载条件为Servlet
@ConditionalOnClass(DispatcherServlet.class)//指定classpath中存在DispatcherServlet时才会加载
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//指定该配置类在ServletWebServer装配完成后进行
public class DispatcherServletAutoConfiguration {
//DispatcherServlet默认的bean名称
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
//DispatcherServletRegistration默认的bean名称
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
protected static class DispatcherServletConfiguration {
//配置DispatcherServlet
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
//设置配置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)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
//配置dispatcherServletRegistrationBean
@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 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;
}
}
其他代码略...
}
DispatcherServletRegistrationBean的作用是自动配置DispatcherServlet,该类是ServletContextInitializer的实现类。
Servlet容器在创建时,会调用IOC容器中的所有ServletContextInitializer对象的onStartup方法。
public abstract class RegistrationBean implements ServletContextInitializer, Ordered {
private static final Log logger = LogFactory.getLog(RegistrationBean.class);
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;
}
//向ServletContext中注册Servlet
register(description, servletContext);
}
代码略...
}
最终调用ServletRegistrationBean的addRegistration添加dispatcherServlet到servletContext中
public class ServletRegistrationBeanextends DynamicRegistrationBean { .... @Override protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) { String name = getServletName(); //将dispatcherServlet添加到servletContext中 return servletContext.addServlet(name, this.servlet); } .... }
启动Servlet容器时,容器会创建自动启动的Servlet。
public void start() throws WebServerException {
synchronized (this.monitor) {
if (this.started) {
return;
}
try {
addPreviouslyRemovedConnectors();
Connector connector = this.tomcat.getConnector();
if (connector != null && this.autoStart) {
//启动需要自启动的Servlet
performDeferredLoadOnStartup();
}
checkThatConnectorsHaveStarted();
this.started = true;
logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '"
+ getContextPath() + "'");
}
catch (ConnectorStartFailedException ex) {
stopSilently();
throw ex;
}
catch (Exception ex) {
PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
Context context = findContext();
ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
}
}
}



