SpringMVC架构及源码分析

SpringMVC

本文在基于SpringMVC 4.3.2.RELEASE版本基础上,对源码进行分析,并探讨SpringMVC的原理及用法。
springMVC

整体架构

在了解Spring整体架构时,这里盗用开涛博客的一张图。

对该架构流程进行简单的分析如下:
任何一个请求都要经过DispatcherServlet进行分发,DispatcherServlet并不对请求进行处理,而是将请求交给HandlerMapping进行映射;HandlerMapping将请求映射为HandlerExecutionChain对象。该对象包含一个Handler对象和多个HandlerInterceptor对象。这里就体现了一种责任链模式的思想;Handler经过一层包装后变成HandlerAdapter,从而支持多种类型的处理器(Controller),HandlerAdapter将请求交给具体的处理器进行处理,完成后返回ModelAndView;通过ViewResolver对视图进行解析,并利用Model数据模型对视图渲染(Render).

DispatcherServlet1

DispatcherServlet继承关系如下:
DispatcherServlet—>FrameworkServlet—>HttpServletBean—>HttpServlet—>GenericServlet—>Servlet.
HttpServletBean继承自javax.servlet.http包下的HttpServlet,那么就先从该类分析:

/**
 * Simple extension of {@link javax.servlet.http.HttpServlet} which treats
 * its config parameters ({@code init-param} entries within the
 * {@code servlet} tag in {@code web.xml}) as bean properties.
 *
 * 

A handy superclass for any type of servlet. Type conversion of config * parameters is automatic, with the corresponding setter method getting * invoked with the converted value. It is also possible for subclasses to * specify required properties. Parameters without matching bean property * setter will simply be ignored. * *

This servlet leaves request handling to subclasses, inheriting the default * behavior of HttpServlet ({@code doGet}, {@code doPost}, etc). * *

This generic servlet base class has no dependency on the Spring * {@link org.springframework.context.ApplicationContext} concept. Simple * servlets usually don't load their own context but rather access service * beans from the Spring root application context, accessible via the * filter's {@link #getServletContext() ServletContext} (see * {@link org.springframework.web.context.support.WebApplicationContextUtils}). * *

The {@link FrameworkServlet} class is a more specific servlet base * class which loads its own application context. FrameworkServlet serves * as direct base class of Spring's full-fledged {@link DispatcherServlet}. * * @author Rod Johnson * @author Juergen Hoeller * @see #addRequiredProperty * @see #initServletBean * @see #doGet * @see #doPost */ @SuppressWarnings("serial") public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { }

该类的初始化方法init()

    /**
     * Map config parameters onto bean properties of this servlet, and
     * invoke subclass initialization.
     * @throws ServletException if bean properties are invalid (or required
     * properties are missing), or if subclass initialization fails.
     */
    @Override
    public final void init() throws ServletException {
        if (logger.isDebugEnabled()) {
            logger.debug("Initializing servlet '" + getServletName() + "'");
        }

        // 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();

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

try…catch中的内容暂时略过。
之后,该方法调用了initServletBean(),该方法在类中进行声明,但并没有实现,而是留给子类FrameworkServlet实现。子类FrameworkServlet实现过程如下:

    /**
     * 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 {
        getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
        if (this.logger.isInfoEnabled()) {
            this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
        }
        long startTime = System.currentTimeMillis();

        try {
            this.webApplicationContext = initWebApplicationContext();
            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");
        }
    }

该方法中依然调用了initFrameworkServlet(),该方法在类中定义,并没有实现,而是留给子类进行实现,不过这次DispatcherServlet并没有重写该方法。

    /**
     * This method will be invoked after any bean properties have been set and
     * the WebApplicationContext has been loaded. The default implementation is empty;
     * subclasses may override this method to perform any initialization they require.
     * @throws ServletException in case of an initialization exception
     */
    protected void initFrameworkServlet() throws ServletException {
    }

如上对DispatcherServlet对请求处理的过程分析,可以看出,DispatcherServlet将请求分发给HandlerMapping.
HandlerMapping是位于org.springframework.web.servlet包下的接口.他就定义了一个函数getHandler(HttpServletRequest request).

    /**
     * Return a handler and any interceptors for this request. The choice may be made
     * on request URL, session state, or any factor the implementing class chooses.
     * 

The returned HandlerExecutionChain contains a handler Object, rather than * even a tag interface, so that handlers are not constrained in any way. * For example, a HandlerAdapter could be written to allow another framework's * handler objects to be used. *

Returns {@code null} if no match was found. This is not an error. * The DispatcherServlet will query all registered HandlerMapping beans to find * a match, and only decide there is an error if none can find a handler. * @param request current HTTP request * @return a HandlerExecutionChain instance containing handler object and * any interceptors, or {@code null} if no mapping found * @throws Exception if there is an internal error */ HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

ContextLoaderListener初始化上下文和DispatcherServlet初始化上下文的关系

DispatcherServlet中有几个特殊的Bean,这些Bean就是上文中提到的Controller, HandlerMapping, HandlerAdapter, ViewResolver, LocalResolver, ThemeResolver, MultipartResolver.

DispatcherServlet需要在web.xml中进行配置。需要声明其在容器启动时初始化,即load-on-startup。同时设置其url-pattern为“/”,表示它拦截所有的请求。
它默认使用WebApplicationContext作为上下文,如果不进行配置,默认会去/WEB-INF/[servlet名字]-servlet.xml中寻找对应的配置文件。
也可以手动对其进行配置,以覆盖默认的配置。

参数 描述
contextClass 实现WebApplicationContext接口的类,当前servlet用它来创建上下文,如果没有指定,默认使用XmlApplicationContext
contextConfigLocation 在此处设置其配置文件的路径,通常的做法是classpath: [servlet名字]-servlet.xml表示去根路径寻找配置文件
namespace 如果不指定,默认使用[servlet名字]-servlet.xml

  1. https://fangjian0423.gitbooks.io/springmvc-source-minibook/content/SpringMVC-dispatcherServlet.html
    [^2]:https://my.oschina.net/lichhao/blog/99039 ↩

你可能感兴趣的:(SpringMVC架构及源码分析)