文章目录
前文介绍了 Java架构直通车——理解Tomcat架构设计,我们知道Tomcat实际上就是Servlet容器,
引入
Spring MVC框架是围绕DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染和文件上传等功能。
Spring MVC框架是请求驱动的:所有设计都围绕着一个中央Servlet来展开,它负责把所有请求分发到控制器;同时提供其他Web应用开发所需要的功能。DispatcherServlet与Spring IoC容器做到了无缝集成,这意味着,Spring提供的任何特性在Spring MVC中你都可以使用。
DispatcherServlet处理流程
DispatcherServlet与WebApplicationContext
DispatcherServlet其实就是个Servlet(它继承自HttpServlet基类)。在Web MVC框架中,DispatcherServlet依赖于一个WebApplicationContext,这个context继承了WebApplicationContext的所有bean定义。继承的这些bean可以在每个servlet自己所属的域中被覆盖,覆盖的bean可以被设置成只有这个servlet实例自己才可以使用的属性。
WebApplicationContext是一个普通的ApplicationContext的扩展,它知道自己与哪个servlet相关联(通过ServletContext)。下面是WebApplicationContext所包含的bean:
处理流程
在请求到来的时候,会在请求中查找并绑定 WebApplicationContext,它可以作为参数被控制器中的方法使用。 默认绑定到 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE
对应的值。
处理流程不做赘述,参考:
再临SpringBoot——理解Spring Web Mvc架构处理流程
SpringMvc in Action——构建Sprig Web应用程序
DispatcherServlet源码分析
DispatcherServlet本质上还是一个Servlet。Servlet的生命周期大致分为三个阶段:
- 初始化阶段 init方法
- 处理请求阶段 service方法
- 结束阶段 destroy方法
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
public void destroy();
public ServletConfig getServletConfig();
public String getServletInfo();
}
这里就重点关注DispatcherServlet在这三个阶段具体做了那些工作。
init()
DispatcherServlet的init()的实现在其父类HttpServletBean中。
public final void init() throws ServletException {
...
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// Let subclasses do whatever initialization they like.
initServletBean();
...
}
以上部分源码描述的过程是通过读取的配置元素,读取到DispatcherServlet中,配置相关bean的配置。完成配置后调用initServletBean方法来创建Servlet WebApplicationContext。
initServletBean方法在FrameworkServlet类中重写了:
protected final void initServletBean() throws ServletException {
...
try {
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
}
...
}
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
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();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
onRefresh(wac);
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
service()
纵观SpringMVC的源码,大量运用模板方法的设计模式。Servlet的service方法也不例外。FrameworkServlet类重写service方法:
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (HttpMethod.PATCH == httpMethod || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
如果请求的方法是PATCH或者空,直接调用processRequest方法(后面会详细解释);否则,将调用父类的service的方法,即HttpServlet的service方法, 而这里会根据请求方法,去调用相应的doGet、doPost、doPut…
而doXXX系列方法的实现并不是HttpServlet类中,而是在FrameworkServlet类中。在FrameworkServlet中doXXX系列实现中,都调用了上面提到的processRequest方法:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
为了避免子类重写它,该方法用final修饰。
- 首先调用
initContextHolders
方法,将获取到的localeContext
、requestAttributes
、reques
t绑定到线程上。 - 然后调用
doService
方法,doService
具体是由DispatcherServlet
类实现的。 doService
执行完成后,调用resetContextHolders
,解除localeContext
等信息与线程的绑定。- 最终调用
publishRequestHandledEvent
发布一个处理完成的事件。
DispatcherServlet类中的doService
方法实现会调用doDispatch
方法,这里请求分发处理的主要执行逻辑。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch主要流程是:
- 先判断是否
Multipart
类型的请求。如果是则通过multipartResolver
解析request
- 通过
getHandler
方法找到从HandlerMapping
找到该请求对应的handler
,如果没有找到对应的handler
则抛出异常。 - 通过
getHandlerAdapter
方法找到handler对应的HandlerAdapter
- 如果有拦截器,执行拦截器
preHandler
方法 HandlerAdapter
执行handle
方法处理请求,返回ModelAndView
。- 如果有拦截器,执行拦截器
postHandle
方法 - 然后调用
processDispatchResult
方法处理请求结果,封装到response
中。
destroy()
destroy方法也是在FrameworkServlet里实现的,
@Override
public void destroy() {
getServletContext().log("Destroying Spring FrameworkServlet '" + getServletName() + "'");
// Only call close() on WebApplicationContext if locally managed...
if (this.webApplicationContext instanceof ConfigurableApplicationContext && !this.webApplicationContextInjected) {
((ConfigurableApplicationContext) this.webApplicationContext).close();
}
}
主要做了这么一个事:
protected void doClose() {
// Check whether an actual close attempt is necessary...
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isDebugEnabled()) {
logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}
// Stop all Lifecycle beans, to avoid delays during individual destruction.
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}
}
// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();
// Close the state of this context itself.
closeBeanFactory();
// Let subclasses do some final clean-up if they wish...
onClose();
// Reset local application listeners to pre-refresh state.
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Switch to inactive.
this.active.set(false);
}
}
可以看到publishEvent、销毁了bean和beanFactory。