Spring Servlet 源码实现原理(1)
Servlet:
DispatcherServlet
它们的 view 拆分策略通过实现 ViewResolver 进行指定,分解象征性的 view 的名字到 View 对象中。
一个 web 应用可以定义不同的 DispatcherServlets,每一个 servlet 会被不同的属性命名空间操作,用 mappings、handlers等加载各自的 application context。一旦有任意一个 root application context 被 ContextLoaderListener 加载,都会被分享。
在 Spring 3.1 中,DispatcherServlet 可能会被一个 web application context 注入,而不是在各自内部创建。servlet 实例支持程序加载,在 Servlet 3.0 的环境中将会非常有用。
public class DispatcherServlet extends FrameworkServlet {
/**
* 处理实际的发送给 handler。handler 将从申请 servlet 的 HandlerMappings 中获得。HandlerAdapter将
* 通过查询 servlet 安装的 HandlerAdapters 去查找第一个支持这个 handler 的类,并从中获得。
* 所有的 HTTP 方法都会被这个方法处理。它将会提交给 HandlerAdapters 或者 handlers 去决定哪些方法
* 被接受。
*/
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;
}
// 调用实际的 handler
// 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);
}
}
}
}
/**
* 暴露了 DispatcherServlet 指定的请求属性,代理 doDispatch 给实际的分发处理。FrameworkServlet 未
* 实现的方法
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(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<String, Object>();
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());
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);
}
}
}
}
/**
* 渲染给定的 ModelAndView,这个是请求中处理的最后一个策略。它可能会需要通过名字拆分.
**/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" +
getServletName() + "'", ex);
}
throw ex;
}
}
}
HttpServlet
给子类提供了一个抽象的类,为一个 Web 网站去生成 HTTP servlet 套件。一个抽象的 HttpServlet 必需重写最少一个方法,一般是以下这些。不用重写 service 方法。
Servlets 通常运行在多线程的servers。可以知道的是,一个 servlet 一定要处理 并且请求和注意访问共享资源的同步。共享资源包括内存中的数据,比如实例或者类变量和扩展对象,比如文件、数据库连接和网络连接。
* <ul>
* <li> <code>doGet</code>, if the servlet supports HTTP GET requests
* <li> <code>doPost</code>, for HTTP POST requests
* <li> <code>doPut</code>, for HTTP PUT requests
* <li> <code>doDelete</code>, for HTTP DELETE requests
* <li> <code>init</code> and <code>destroy</code>,
public abstract class HttpServlet extends GenericServlet {
// 请求方法都只有这两个参数 HttpServletRequest, HttpServletResponse
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
/**
* 通过公共的 service 方法接收标准的 HTTP 请求,分发给在这个类中定义的 doXX 的方法。这个方法是一个 service方法的 HTTP指定版本,不需要重写该方法。
**/
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 = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// 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);
}
}
/*
* Sets the Last-Modified entity header field, if it has not
* already been set and if the value is meaningful. Called before
* doGet, to ensure that headers are set before response data is
* written. A subclass might have set this header already, so we
* check.
*/
private void maybeSetLastModified(HttpServletResponse resp,
long lastModified) {
if (resp.containsHeader(HEADER_LASTMOD))
return;
if (lastModified >= 0)
resp.setDateHeader(HEADER_LASTMOD, lastModified);
}
/**
* Dispatches client requests to the protected
* <code>service</code> method. There's no need to
* override this method.
*
* @param req the {@link HttpServletRequest} object that
* contains the request the client made of
* the servlet
*
* @param res the {@link HttpServletResponse} object that
* contains the response the servlet returns
* to the client
*
* @exception IOException if an input or output error occurs
* while the servlet is handling the
* HTTP request
*
* @exception ServletException if the HTTP request cannot
* be handled
*
* @see javax.servlet.Servlet#service
*/
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException
{
HttpServletRequest request;
HttpServletResponse response;
if (!(req instanceof HttpServletRequest &&
res instanceof HttpServletResponse)) {
throw new ServletException("non-HTTP request or response");
}
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
service(request, response);
}
}
HttpServletBean
简单的扩展了 HttpServlet,在web.xml 中,它的配置参数和 servlet 标签被当做bean 属性的init-param 元素。
这个通用的 servlet 的基本类并没有依赖 Spring ApplicationContext 的概念。
FrameworkServlet 类是一个更特定的基础 servlet 类,它加载了自己的 application context。FrameworkServlet 被当作 Spring 的完全合格的 DispatcherServlet 直接类。
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
}
FrameworkServlet
Spring web 框架的基础 servlet。在 JavaBean 基础的总体解决方案中,提供了一个 Spring application context 的集成。
这个类提供了相应的功能:
1.管理每一个 servlet 的 WebApplicationContext 实例,由 beans 所在的 servlet 命名空间管理这个 servlet 的配置。
2.咋就请求过程中发布事件,无论该请求是否被成功的处理。
为了处理请求,子类必需实现 doService。因为这个继承 HttpServletBean 而不是直接继承 HttpServlet, beans 的属性会被自动的映射进来。子类可以重写 initFrameworkServlet() 实现自定义初始化。
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
/**
* 为了保证去拦截批量的请求,重写了父类的实现。
*/
@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);
}
}
/**
* 处理请求,不管出去就发布事件。实际执行的事件处理是通过抽象的 doService 模版方法。HTTP 的请求方法都是调
* 用这个方法进行处理
**/
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); // 调用 doService
}
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);
}
}
/**
* 等待子类实现.子类一定得实现这个方法去执行请求处理的工作,接收 GET, POST, PUT AND DELETE 的集中回调
*
**/
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
servlet 调用堆栈: