SpringMVC架构及源码分析

来源:互联网 发布:盲打软件 编辑:程序博客网 时间:2024/06/17 09:14

SpringMVC

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

整体架构

在了解Spring整体架构时,这里盗用开涛博客的一张图。
SpringMVC架构
对该架构流程进行简单的分析如下:
任何一个请求都要经过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. * * <p>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. * * <p>This servlet leaves request handling to subclasses, inheriting the default * behavior of HttpServlet ({@code doGet}, {@code doPost}, etc). * * <p>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}). * * <p>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.     * <p>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.     * <p>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初始化上下文的关系
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 ↩
0 0
原创粉丝点击