之前看了网上很多文章,了解DispatcherServlet是SpringMVC的实现核心,那它是如何作为核心的servlet做做统一的请求分处理,这个servlet是如何加入到tomcat中,作为核心的请求处理的。
SpringBoot的加载DispatcherServlet的配置类
DispatcherServletAutoConfiguration是SpringBoot框架自动加载的DispatcherServlet到Spring Beanfactory的配置
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@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)
@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;
}
}
DispatcherServletRegistrationConfiguration的bean的注入到Spring的BeanFactory容器
@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;
}
}
由于DispatcherServletRegistrationBean是继承自ServletContextInitializer的接口
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;
}
register(description, servletContext);
}
下面是TomcatServletWebServerFactory初始化Tomcat的配置Context的函数,这里TomcatStart也是实现ServletContainerInitializer接口
- 创建TomcatStarter对象,并且判断设置到TomcatEmbeddedContext对象中。
- 增加ServletContainerInitializer到TomcatEmbeddedContext对象中,这里其实就是将所有的ServletContainerInitializer初始化实现类都代理到TomcatStarter中,再由TomcatEmbeddedContext启动时候去执行其onStartup方法。
*/
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
TomcatStarter starter = new TomcatStarter(initializers);
if (context instanceof TomcatEmbeddedContext) {
TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
embeddedContext.setStarter(starter);
embeddedContext.setFailCtxIfServletStartFails(true);
}
context.addServletContainerInitializer(starter, NO_CLASSES);
for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
context.addLifecycleListener(lifecycleListener);
}
}
由于ServletContextInitializer接口是Tomcat的TomcatEmbeddedContext的父类StandardContext的startIntenal启动函数中执行其持有的initializers集合(即实现ServletContainerInitializer接口初始化类),并执行其OnStartup方法,
// Call ServletContainerInitializers
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
现在回过来看DispatcherServletRegistrationBean的父类DynamicRegistrationBean的register函数。
@Override
protected final void register(String description, ServletContext servletContext) {
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
return;
}
configure(registration);
}
ServletRegistrationBean类实现DynamicRegistrationBean的addRegistration方法,
- 获取Servlet的名称,就是dispatcherServlet。
- 注册DespatcherServlet的bean到ServletContext中。
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
从DispatcherServletRegistrationBean的构造函数,将DispatcherServlet传到父类中,父类就是ServletRegistrationBean
public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
//调用父类的ServletRegistrationBean的构造方法,传入DispatcherServlet的bean
super(servlet);
Assert.notNull(path, "Path must not be null");
this.path = path;
super.addUrlMappings(getServletUrlMapping());
}
DispatcherServletRegistrationBean的父类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));
}
至此,tomcat启动完后,就完成了ServletContext的添加DispatcherServlet这个Servlet. 而Tomcat则是通过StandardWrapperValve的invoke方法中去构造ApplicationFilterChain,然后最后调用Servlet(即DispatcherServlet)的service方法完成请求处理,接下来就是到SpringMVC去调用HandleMapping,再调用HandlerMappingAdapter去调用对应的Controller了,
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
// 就是DispatcherServlet的service方法
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
总结
本文主要对SprintBoot对SpringMvc中DispatcherServlet是如何加入Tomcat成为Servlet的过程,并成为Servlet请求处理入口的整合的分析,欢迎一起交流