Spring MVC原理之Spring MVC的设计与实现
接上文 SpringMVC原理之Spring应用上下文(Ioc容器)在Web容器中的启动分析。在前文的分析过程中,了解了 Spring的上下文体系通过ContextLoader和DispatcherServiet建立并初始化的过程。在完成对ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet,这个初始化的启动与在web.xml中对...
接上文 SpringMVC原理之Spring应用上下文(Ioc容器)在Web容器中的启动分析。
在前文的分析过程中,了解了 Spring的上下文体系通过
ContextLoader
和DispatcherServiet
建立并初始化的过程。在完成对ContextLoaderListener的初始化以后,Web容器开始初始化DispatcherServlet,这个初始化的启动与在web.xml中对载入次序的定义有关。
DispatcherServiet会建立自己的上下文来持有Spring MVC的Bean对象,在建立这个自己持有的Ioc容器时,会从ServletContext中得到根上下文作为DispatcherServlet持有上下文的双亲上下文。有了这个根上下文,再对自己持有的上下文进行初始化,最后把自己持有的这个上下文保存到ServletContext(Web容器的上下文)中,供以后检索和使用。
Spring MVC设计概览
DispatcherServlet的启动过程,实际上也是Spring MVC的启动过程。Spring MVC的核心类DispatcherServlet的类图设计如下:
DispatcherServiet通过继承FrameworkServlet和HttpServletBean而继承了 HttpServlet ,通过使用Servlet API来对HTTP请求进行响应,成为Spring MVC的前端处理器,同时成为MVC模块与Web容器集成的处理前端。
DispatcherServiet
的工作大致可以分为两个部分:一个是初始化部分,由initServletBean()
启动,通过initWebAppIicationContext()方法最终调用DispatcherServlet的initStrategies方法。在这个方法里,DispatcherServlet对MVC模块的其他部分进行了初始化,比如handlerMapping, ViewResolver等。另一个是对HTTP请求进行响应,作为一个Serviet,Web容器会调用Servlet的doGet()和doPost()方法,在经过FrameworkServlet的processRequest()简单处理后,会调用DispatcherServlet的doService()
方法,在这个方法调用中封装了doDispatch(),这个doDispatch()是Dispatcher实现MVC模式的主要部分。
先看看DispatcherServlet
类的部分源码如下,是不是发现的什么,这里的这么多_BEAN_NAME
是不是好眼熟。。。
public class DispatcherServlet extends FrameworkServlet {
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
public static final String LOCALE_RESOLVER_BEAN_NAME = "localeResolver";
public static final String THEME_RESOLVER_BEAN_NAME = "themeResolver";
public static final String HANDLER_MAPPING_BEAN_NAME = "handlerMapping";
public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
public static final String HANDLER_EXCEPTION_RESOLVER_BEAN_NAME = "handlerExceptionResolver";
public static final String REQUEST_TO_VIEW_NAME_TRANSLATOR_BEAN_NAME = "viewNameTranslator";
public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
public static final String FLASH_MAP_MANAGER_BEAN_NAME = "flashMapManager";
public static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
public static final String LOCALE_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".LOCALE_RESOLVER";
public static final String THEME_RESOLVER_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_RESOLVER";
public static final String THEME_SOURCE_ATTRIBUTE = DispatcherServlet.class.getName() + ".THEME_SOURCE";
public static final String INPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".INPUT_FLASH_MAP";
public static final String OUTPUT_FLASH_MAP_ATTRIBUTE = DispatcherServlet.class.getName() + ".OUTPUT_FLASH_MAP";
public static final String FLASH_MAP_MANAGER_ATTRIBUTE = DispatcherServlet.class.getName() + ".FLASH_MAP_MANAGER";
public static final String EXCEPTION_ATTRIBUTE = DispatcherServlet.class.getName() + ".EXCEPTION";
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";
private static final String DEFAULT_STRATEGIES_PREFIX = "org.springframework.web.servlet";
protected static final Log pageNotFoundLogger = LogFactory.getLog("org.springframework.web.servlet.PageNotFound");
private static final Properties defaultStrategies;
private boolean detectAllHandlerMappings = true;
private boolean detectAllHandlerAdapters = true;
private boolean detectAllHandlerExceptionResolvers = true;
private boolean detectAllViewResolvers = true;
private boolean throwExceptionIfNoHandlerFound = false;
private boolean cleanupAfterInclude = true;
@Nullable
private MultipartResolver multipartResolver;
@Nullable
private LocaleResolver localeResolver;
@Nullable
private ThemeResolver themeResolver;
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
private List<HandlerAdapter> handlerAdapters;
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
@Nullable
private FlashMapManager flashMapManager;
@Nullable
private List<ViewResolver> viewResolvers;
public DispatcherServlet() {
this.setDispatchOptionsRequest(true);
}
}
DispatcherServlet的处理过程:
DispatcherServlet的启动和初始化
作为Servlet,DispatcherServlet的启动与Servlet的启动过程是相联系的。在Servlet的初始化过程中,Servlet的init方法会被调用。
在HttpServletBean
中进行初始化:
public final void init() throws ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Initializing servlet '" + this.getServletName() + "'");
}
//获取Servlet的初始化参数.对Bean属性进行配置
PropertyValues pvs = new HttpServletBean.ServletConfigPropertyValues(this.getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(this.getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, this.getEnvironment()));
this.initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
} catch (BeansException var4) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Failed to set bean properties on servlet '" + this.getServletName() + "'", var4);
}
throw var4;
}
}
//调用子类的initServletaean进行具体的初始化
this.initServletBean();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet '" + this.getServletName() + "' configured successfully");
}
}
FrameworkServlet
中的初始化方法:
protected final void initServletBean() throws ServletException {
this.getServletContext().log("Initializing Spring FrameworkServlet '" + this.getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
//初始MVC化上下文
this.webApplicationContext = this.initWebApplicationContext();
this.initFrameworkServlet();
} catch (RuntimeException | ServletException var5) {
this.logger.error("Context initialization failed", var5);
throw var5;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + this.getServletName() + "': initialization completed in " + elapsedTime + " ms");
}
}
//初始化MVC上下文
protected WebApplicationContext initWebApplicationContext() {
//取得跟上下文
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext)wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
//设置MVC上下文的双亲上下文为跟上下文webApplicationContext
cwac.setParent(rootContext);
}
this.configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
//先查找
wac = this.findWebApplicationContext();
}
if (wac == null) {
//再创建
wac = this.createWebApplicationContext(rootContext);
}
if (!this.refreshEventReceived) {
//IoC容器必经步骤 跟上下文和MVC上下文都是IoC容器
this.onRefresh(wac);
}
if (this.publishContext) {
String attrName = this.getServletContextAttributeName();
//设置MVC上下文到Web上下文servletContext中
this.getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + this.getServletName() + "' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
//被调用创建
protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
return this.createWebApplicationContext((ApplicationContext)parent);
}
//被上面调用
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
Class<?> contextClass = this.getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + this.getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "', using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException("Fatal initialization error in servlet with name '" + this.getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext");
} else {
//初始化MVC上下文,
ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
wac.setEnvironment(this.getEnvironment());
//设置双亲
wac.setParent(parent);
String configLocation = this.getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
this.configureAndRefreshWebApplicationContext(wac);
return wac;
}
}
由干这个根上下文是DispatcherServlet建立的上下文的双亲上下文,所以根上下文中粉理的Bean也是可以被DispatcherServlet的上下文使用的。通过getBean向IoC容器获取Bean时.容器会先到它的双亲IoC容器中获取getBean。
除了上面的SpringMVC上下文的的创建之外,还需要启动SpringMVC中的其他一些配置初始化,通过上面的onRefresh
调用来完成,这个方法在子类DispatcherServlet
中被覆盖了,实际调用了initStrategies
进行配置,这里就不细说这些配置了。
源码如下:
protected void onRefresh(ApplicationContext context) {
this.initStrategies(context);
}
//各种初始化配置
protected void initStrategies(ApplicationContext context) {
this.initMultipartResolver(context);
this.initLocaleResolver(context);
this.initThemeResolver(context);
this.initHandlerMappings(context);
this.initHandlerAdapters(context);
this.initHandlerExceptionResolvers(context);
this.initRequestToViewNameTranslator(context);
this.initViewResolvers(context);
this.initFlashMapManager(context);
}
MVC处理HTTP分发请求
在MVC框架初始化完成以后,对HTTP请求的处理是在doService()方法中完成的。DispatcherServiet是HttpServlet的子类,
与其他HttpServlet一样,可以通过doService()
来响应HTTP的请求。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (this.logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
this.logger.debug("DispatcherServlet with name '" + this.getServletName() + "'" + resumed + " processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap();
Enumeration attrNames = request.getAttributeNames();
label112:
while(true) {
String attrName;
do {
if (!attrNames.hasMoreElements()) {
break label112;
}
attrName = (String)attrNames.nextElement();
} while(!this.cleanupAfterInclude && !attrName.startsWith("org.springframework.web.servlet"));
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
//对HTTP请求参数进行快照处理
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, this.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 {
//这里分发请求
this.doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted() && attributesSnapshot != null) {
this.restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
对请求的处理实际上是由doDispatch()来完成的,过程如下图:
doDispatch()
源码入下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
//这里为视图准备好ModelAndViev,这个ModelAndView持有handler处理请求的结果
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
//根据请求得到对应的HandlerExecutionChain
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
//利用HandlerAdapter处理一把,检查Handler的合法性
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//handler处理的结果封装到ModelAndView对象中.为视图提供展现数据
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
//根据ModelAndView 渲染View视图
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
通过render方法来处理请求;
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
Locale locale = this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale();
response.setLocale(locale);
String viewName = mv.getViewName();
View view;
if (viewName != null) {
//处理视图
view = this.resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() + "' in servlet with name '" + this.getServletName() + "'");
}
} else {
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a View object in servlet with name '" + this.getServletName() + "'");
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'");
}
try {
//返回码
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
view.render(mv.getModelInternal(), request, response);
} catch (Exception var8) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Error rendering view [" + view + "] in DispatcherServlet with name '" + this.getServletName() + "'", var8);
}
throw var8;
}
}
通过resolveViewName
来处理视图:
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model, Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
Iterator var5 = this.viewResolvers.iterator();
while(var5.hasNext()) {
ViewResolver viewResolver = (ViewResolver)var5.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
更多推荐
所有评论(0)