Srping MVC源码分析(一)

234 阅读10分钟

1.Spring MVC源码分析(一)

Spring MVC这个框架大家应该都挺熟悉的,是目前主流的WEB应用框架。Spring MVC中有一个核心类就是org.springframework.web.servlet.DispatcherServlet,也称为前端控制器,要看 Spring MVC源码,这个类是重中之重。

首先,我们看下DispatcherServlet的整体类图,对它有个整体的了解。

我们主要关注我上图中红色框内的部分,从这个类图中可以看出来,DispatcherServlet本质上就是一个Servlet,一想到Servlet我们就应该想到它的生命周期:initservicedestroy,如果对Servlet不熟悉,建议先去看一下相关内容,现在根据这个生命周期我们可以做一个大胆的猜测:1.DispatcherServlet的初始化就是在init中;2.DispatcherServlet的执行是在service中。下面我们就根据这两个方向来验证我们的猜想。

1.DispatcherServlet的初始化

刚刚我们已经猜想了DispatcherServlet的初始化是在init中,那么我们就应该找到init方法,由于顶层接口javax.servlet.Servlet是一个接口,我们就不看了,直接找到javax.servlet.GenericServletinit开始按照类图一步一步往下看。

javax.servlet.GenericServlet(通用Servlet,不带有任何协议):

//实现了Servlet的init方法
public void init(ServletConfig config) throws ServletException {
	this.config = config;
	this.init();
}

//这个重载的init方法,完全交由子类实现
public void init() throws ServletException {

}

javax.servlet.http.HttpServlet(Http协议的Servlet):

我们发现该类中并没有init方法,那我们就不用看了,直接看HttpServlet的下一个类。

org.springframework.web.servlet.HttpServletBean(配置Servlet的Bean):

我们在这个类中顺利的找到了javax.servlet.GenericServlet被重写的init方法。

@Override
public final void init() throws ServletException {
    if (logger.isDebugEnabled()) {
        logger.debug("Initializing servlet '" + getServletName() + "'");
    }

    // Set bean properties from init parameters.
    //1.获取web.xml DispatcherServlet的init-param标签,设置配置信息。
    //这个方法被final修饰了,意思就是这一部分是必须的,下面的initServletBean完全交由子类实现
    PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
    if (!pvs.isEmpty()) {
        try {
            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) {
            if (logger.isErrorEnabled()) {
                logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
            }
            throw ex;
        }
    }

    // Let subclasses do whatever initialization they like.
    //2.我们主要关注这里,初始化ServletBean,完全交由子类实现
    initServletBean();

    if (logger.isDebugEnabled()) {
        logger.debug("Servlet '" + getServletName() + "' configured successfully");
    }
}

org.springframework.web.servlet.FrameworkServlet(Spring web的基础Servlet,提供了与Spring应用上下文的集成):

@Override
protected final void initServletBean() throws ServletException {
    getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
    if (this.logger.isInfoEnabled()) {
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
    }
    long startTime = System.currentTimeMillis();

    try {
        //1.初始化Web容器,我们需要关心这个
        this.webApplicationContext = initWebApplicationContext();
        //2.这个不用关心,本类和子类都没有提供实现
        initFrameworkServlet();
    }
    catch (ServletException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }
    catch (RuntimeException ex) {
        this.logger.error("Context initialization failed", ex);
        throw ex;
    }

    if (this.logger.isInfoEnabled()) {
        long elapsedTime = System.currentTimeMillis() - startTime;
        this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
                         elapsedTime + " ms");
    }
}
protected WebApplicationContext initWebApplicationContext() {
    //1.查找root application context
    WebApplicationContext rootContext =
        WebApplicationContextUtils.getWebApplicationContext(getServletContext());
    WebApplicationContext wac = null;

    //2.webApplicationContext 是否已经存在 一般都是false
    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);
            }
        }
    }
    //3.如果为空
    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();
    }
    //4.创建
    if (wac == null) {
        // No context instance is defined for this servlet -> create a local one
        //这里会自动刷新
        wac = createWebApplicationContext(rootContext);
    }

    //5.如果走上面创建WebApplicationContext,那么将不会刷新
    //当WebApplicationContext已经存在了,会走手动刷新
    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.
        // 6.我们主要关注这里: DispatcherServlet 就是在这里实现的
        // 这个方法默认啥都不做,完全交由子类实现,这里就直接到了DispatcherServlet了
        onRefresh(wac);
    }

    if (this.publishContext) {
        // Publish the context as a servlet context attribute.
        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;
}

org.springframework.web.servlet.DispatcherServlet(前端控制器):

@Override
protected void onRefresh(ApplicationContext context) {
    //核心初始化方法
    initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
    //文件上传
    initMultipartResolver(context);
    //国际化
    initLocaleResolver(context);
    //主题
    initThemeResolver(context);
    //请求-处理器映射
    initHandlerMappings(context);
    //处理器适配器
    initHandlerAdapters(context);
    //异常处理
    initHandlerExceptionResolvers(context);
    //将请求的url转为视图的名称
    initRequestToViewNameTranslator(context);
    //视图解析器
    initViewResolvers(context);
    //保存请求资源,例如session中的,供forward、redirect使用,也能及时的清除过期资源
    initFlashMapManager(context);
}

上面的initStrategies方法包含了各个组件的初始化,这里我就挑两个来看一下:

org.springframework.web.servlet.DispatcherServlet#initMultipartResolver(初始化文件上传组件):

private void initMultipartResolver(ApplicationContext context) {
    try {
        //1.加载我们申明的MultipartResolver,这里我们申明的CommonsMultipartResolver bean的名称是固定的,否则会读取不到,DispatcherServlet已经申明好了
        this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using MultipartResolver [" + this.multipartResolver + "]");
        }
    }
    catch (NoSuchBeanDefinitionException ex) {
        // Default is no multipart resolver.
        //2.默认为null
        this.multipartResolver = null;
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME +
                         "': no multipart request handling provided");
        }
    }
}

org.springframework.web.servlet.DispatcherServlet#initHandlerExceptionResolvers(初始化异常处理组件):

private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    //1.默认加载所有我们自己实现的异常处理器
    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
            .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            //2.加载申明的相同bean name的异常处理器
            HandlerExceptionResolver her =
                context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    //3.默认异常处理器
    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
        //我们需要看下这个方法,可以知道DispatcherServlet默认值是从哪里来的?
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isDebugEnabled()) {
            logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
        }
    }
}

org.springframework.web.servlet.DispatcherServlet#getDefaultStrategies

​ 可以看到getDefaultStrategies方法是从一个Properties中获取的值,那么问题来了,这个Properties是从哪里来的?

可以看到是静态块出事了Properties,那么我们就去看下这个Properties文件。

private static final Properties defaultStrategies;

//在静态块初始化加载了DispatcherServlet.properties文件
static {
    // Load default strategy implementations from properties file.
    // This is currently strictly internal and not meant to be customized
    // by application developers.
    try {
        ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
        defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
    }
    catch (IOException ex) {
        throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
    }
}

DispatcherServlet.properties(申明了DispatcherServlet的默认组件):

剩下的大家可以自己去看一下,我这里只挑了两个具有代表性的看了一下,只要理解了这里,其他的都差不多。

至此,这个DispatcherServelt的初始化工作就完成了。总结一下:

1.DispatcherServlet是一个Servlet,初始化工作就是在init方法中完成的。

2.Servlet的实现GenericServlet是一个通用Servlet,与协议无关,init方法只是获取了ServletConfig,并调用子类的init方法。

3.HttpServletBean实现了GenericServletinit方法,完成了web.xml中DispatcherServlet的init-param的配置,并调用子类的initServletBean方法。

4.FrameworkServlet是Spring Web模块的基础Servlet,集成了Spring Application Context。通过调用initWebApplicationContext方法初始化容器,如果容器不存在,则创建容器,并自动刷新;如果容器已经存在,则调用子类的onRefresh方法手动刷新容器,这里就到DispatcherServlet了。

5.DispatcherServlet通过调用initStrategies方法,initStrategies方法调用了各个组件的initXXX方法初始化各个组件。

6.各个组件的initXXX方法都是差不多的步骤:1.加载用户定义的组件;2.根据DispatcherServlet.properties加载默认组件。

2.DispatcherServlet的执行

我们前面已经猜想了DispatcherServlet的执行是在service方法中,那么我们现在也和上面看初始化一样,按照类图,一步一步的往下看。

和上面一样,我们直接从javax.servlet.GenericServlet开始看。

javax.servlet.GenericServlet

//抽象方法,完全交由子类实现
public abstract void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;

javax.servlet.http.HttpServlet

@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");
    }
	//1.转换
    request = (HttpServletRequest) req;
    response = (HttpServletResponse) res;
	//2.根据请求类型进行分发
    service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
{
    //1.获取请求类型,根据不同的类型,做不同的处理
    String method = req.getMethod();

    //2.这里的doXXX方法该类有实现,但是一般调用的是子类实现,这里会跳转到FrameworkServlet
    //3.请求方式比较多,我们直接以get请求为例,往下看
    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);
    }
}

org.springframework.web.servlet.HttpServletBean

该类重写了init方法,在上面DispatcherServlet初始化的时候已经看过了,这里没有重写service方法,不用关心。

org.springframework.web.servlet.FrameworkServlet

@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    //1.所有的doXXX方法都会调用这个方法
    processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    //1.这些国际化、属性、异步先不管
    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 {
       	//2.我们要关注的:这是一个抽象方法,完全交给DispatcherServlet去实现
        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);
    }
}

org.springframework.web.servlet.DispatcherServlet

@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.
    //1.保留原始属性快照
    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));
            }
        }
    }

    //2.请求属性设置
    // 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());

    //3.移除与请求匹配的上一个请求保存的FlashMap,并删除过期数据
    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 {
        //4.这是我们比较熟悉的一个方法了,做分发
        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);
            }
        }
    }
}
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.
            //1.获取Handler,其实HandlerExecutionChain,这个类包含了Handler和HandlerInterceptor
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response);
                return;
            }

            // Determine handler adapter for the current request.
            //2.找到Handler的适配器,因为Handler是Object,所以通过SPI机制,找到对应Handler的适配器
            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;
                }
            }

            //3.org.springframework.web.servlet.HandlerInterceptor#preHandle
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler.
            //4.真正的去执行
            //对于@RequestBody、@ResponseBody、@RestController等注解使用的是AbstractHandlerMethodAdapter
            //这个方法里面主要三步:
                    //1.参数解析:涉及转换器
                    //2.反射执行
                    //3.结果封装:涉及转换器
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

            if (asyncManager.isConcurrentHandlingStarted()) {
                return;
            }

            //5.将请求转为视图名称
            applyDefaultViewName(processedRequest, mv);

            //6.org.springframework.web.servlet.HandlerInterceptor#postHandle
            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);
        }
        //7.视图渲染
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        //8.org.springframework.web.servlet.HandlerInterceptor#afterCompletion
        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方法的核心流程:

1.根据请求的url从HandlerMapping中获取HandlerExecutionChain,可以理解为Handler。
2.HandlerAdapter 典型的适配器模式,找到处理Handler的HandlerAdapter,因为Handler是Object,便于第三方框架扩展。
3.HandlerInterceptor#preHandle。
4.HandlerAdapter#handler。
	1.org.springframework.web.servlet.mvc.method.annotation.
RequestResponseBodyMethodProcessor#resolveArgument 涉及转换器。
	2.invoke。
	3.org.springframework.web.servlet.mvc.method.annotation.
	RequestResponseBodyMethodProcessor#handleReturnValue 涉及转换器。
5.HandlerInterceptor#postHandle。
6.DispatcherServlet#processDispatcherResult。
	1.异常处理。
	2.视图渲染 render。
7.如果发生异常:HandlerInterceptor#afterCompletion。

也就是我们经常看到的Spring MVC的核心流程图: