DispatcherServlet源码分析(一)

以下源码分析基于 Spring的5.1.3 版本

DispatcherServlet的继承层级

先来看看DispatcherServlet的继承层级,如下图,由上至下我们的主要关注点应放在ServletGenericServletHttpServletHttpServletBeanFrameworkServletDispatcherServlet这几个东西上。
在这里插入图片描述

首先最顶层的接口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,我们看该抽象类实现了接口Servletservice()方法

@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,并重写了HttpSerlvetservice()方法(方法2)来处理PATCH请求,以及重写了HttpSerlvetdoGet()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()方法,否则就调用父类HttpServletservice()方法(方法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()方法是一个模板方法,供子类去实现,等下要讲的继承了FrameworkServletDispatcherServlet就实现了这个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()

  1. initWebApplicationContext():初始化并加载WebApplicationContext,在属性加载后被调用(因为在HttpServletBean中,init()是先初始化属性,再调用initServletBean()的)
  2. 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写。

小结

  1. Servlet: 有init()service()两个方法,供子类实现
  2. GenericServlet:实现了init()方法设置了ServletConfig对象,并再定义了一个init()(该方法为模板方法,供给子类实现)
  3. HttpServlet:实现了service()方法,主要是将请求变为Http请求,并且定义了doGet()doPost()doXXX()方法
  4. HttpServletBean:实现了GenericServlet的模板方法init(),而这个模板方法又调用了另一个模板方法initServletBean()供子类实现
  5. FrameworkServlet:该类做的工作有两点:
    a). 一是初始化 WebApplicationContext 上下文,它实现了HttpServletBeaninitServletBean()方法,在里面调用了initWebApplicationContext()(就是该方法执行了WebApplicationContext的初始化工作)是初始化 WebApplicationContext 上下文,它实现了HttpServletBeaninitServletBean()方法,在里面调用了initWebApplicationContext()(就是该方法执行了WebApplicationContext的初始化工作)
    b).二是重写HttpServletservice()来处理新增的PATCH请求,以及重写doGet()doPost()等方法去调用processRequest()方法,定义模板方法doService()供子类实现
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值