本文在基于SpringMVC 4.3.2.RELEASE版本基础上,对源码进行分析,并探讨SpringMVC的原理及用法。
springMVC
在了解Spring整体架构时,这里盗用开涛博客的一张图。
对该架构流程进行简单的分析如下:
任何一个请求都要经过DispatcherServlet进行分发,DispatcherServlet并不对请求进行处理,而是将请求交给HandlerMapping进行映射;HandlerMapping将请求映射为HandlerExecutionChain对象。该对象包含一个Handler对象和多个HandlerInterceptor对象。这里就体现了一种责任链模式的思想;Handler经过一层包装后变成HandlerAdapter,从而支持多种类型的处理器(Controller),HandlerAdapter将请求交给具体的处理器进行处理,完成后返回ModelAndView;通过ViewResolver对视图进行解析,并利用Model数据模型对视图渲染(Render).
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 |