以下源码分析基于
Spring的5.1.3
版本
DispatcherServlet的继承层级
先来看看DispatcherServlet
的继承层级,如下图,由上至下我们的主要关注点应放在Servlet
、GenericServlet
,HttpServlet
,HttpServletBean
,FrameworkServlet
,DispatcherServlet
这几个东西上。
首先最顶层的接口Servlet
有一个service()
方法,Servlet
又被抽象类GenericServlet
实现,但GenericServlet
没有实现Servlet
接口的service()
方法,仅仅实现了init()
。
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
方法体内的init()
是个模板方法,供子类实现,HttpServletBean
就实现了该模板方法。
HttpServlet抽象类
去到抽象类HttpServlet
,我们看该抽象类实现了接口Servlet
的service()
方法
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}
看代码可得,该方法主要是将普通请求转化为Http请求,然后再调用另一个 service()
方法(我们姑且称这个为方法2,前文提到的service()为方法1)。
方法2service()
源码如下:
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
分析源码得知,方法2里主要是根据请求类型来调用对应的响应方法,如果为Get
请求,就调用doGet()
,如果为Post
请求,则调用doPost()
。通常我们都会继承HttpServlet
类,然后重写doGet()
和doPost()
方法来处理请求。
FrameworkServlet抽象类
接下来再看FrameworkServlet
这个抽象类,这个抽象类继承了HttpServletServlet
,并重写了HttpSerlvet
的service()
方法(方法2)来处理PATCH请求,以及重写了HttpSerlvet
的doGet()
、doPost()
等等的doXXX()
方法。
/**
* Override the parent class implementation in order to intercept PATCH requests.
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
可以看到,源码中先判断请求类型是否为PATCH类型的请求,如果是或者请求为空,则直接调用processRequest()
方法,否则就调用父类HttpServlet
的service()
方法(方法2)。
在FrameworkServlet
中我们可以看到重写了doGet()
、doPost()
、doPut()
、doDelete()
,这4个方法都如出一辙地直接调用了processRequest()
方法,看源码:
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
那么这个processRequest()
方法有什么神奇之处?上代码:
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
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 | 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();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
可以看到在这个方法里大概做了这样几件事:国际化的设置,创建ServletRequestAttributes
对象,初始化上下文holders(即将Request对象放入到线程上下文中),然后调用了doService()
方法。doService()
方法是一个模板方法,供子类去实现,等下要讲的继承了FrameworkServlet
的DispatcherServlet
就实现了这个doService()
方法。程序的入口就是该doService()
方法。
另外,FrameworkServlet
还实现了HttpServletBean
提供的模板方法initServletBean()
/**
* Overridden method of {@link HttpServletBean}, invoked after any bean properties
* have been set. Creates this servlet's WebApplicationContext.
*/
@Override
protected final void initServletBean() throws ServletException {
//删除了打印日志的代码
//该方法最重要的就是这两行代码,对WebApplicationContext进行初始化
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
//删除了打印日志的代码
}
可以看到里面调用了initWebApplicationContext()
以及initFrameworkServlet()
initWebApplicationContext()
:初始化并加载WebApplicationContext
,在属性加载后被调用(因为在HttpServletBean
中,init()
是先初始化属性,再调用initServletBean()
的)initFrameworkServlet()
:又是一个模板方法,供子类实现任何初始化工作,在初始化WebApplicationContext
之后被调用
initWebApplicationContext()
源码如下:
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
// A context instance was injected at construction time -> use it
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent -> set
// the root application context (if any; may be null) as the parent
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
// No context instance was injected at construction time -> see if one
// has been registered in the servlet context. If one exists, it is assumed
// that the parent context (if any) has already been set and that the
// user has performed any initialization such as setting the context id
wac = findWebApplicationContext();
}
if (wac == null) {
// No context instance is defined for this servlet -> create a local one
wac = createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
// Either the context is not a ConfigurableApplicationContext with refresh
// support or the context injected at construction time had already been
// refreshed -> trigger initial onRefresh manually here.
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
}
}
if (this.publishContext) {
// Publish the context as a servlet context attribute.
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
}
return wac;
}
DispatcherServlet类
接下来要分析的就是真个SpringMVC最基础的DispatcherServlet
了。
首先DispatcherServlet
继承自FrameworkServlet
,实现了doService()
这个模板方法。
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
可以看到,源码中除了别的有的没的,doService()
调用了一个 doDispatch()
方法,该方法才是DispatcherServlet
的核心方法。
关于DispathcerServlet
的分析会在另一篇blog写。
小结
Servlet
: 有init()
、service()
两个方法,供子类实现GenericServlet
:实现了init()
方法设置了ServletConfig
对象,并再定义了一个init()
(该方法为模板方法,供给子类实现)HttpServlet
:实现了service()
方法,主要是将请求变为Http请求,并且定义了doGet()
、doPost()
等doXXX()
方法HttpServletBean
:实现了GenericServlet
的模板方法init()
,而这个模板方法又调用了另一个模板方法initServletBean()
供子类实现FrameworkServlet
:该类做的工作有两点:
a). 一是初始化WebApplicationContext
上下文,它实现了HttpServletBean
的initServletBean()
方法,在里面调用了initWebApplicationContext()
(就是该方法执行了WebApplicationContext
的初始化工作)是初始化WebApplicationContext
上下文,它实现了HttpServletBean
的initServletBean()
方法,在里面调用了initWebApplicationContext()
(就是该方法执行了WebApplicationContext
的初始化工作)
b).二是重写HttpServlet
的service()
来处理新增的PATCH请求,以及重写doGet()
、doPost()
等方法去调用processRequest()
方法,定义模板方法doService()
供子类实现