您的位置:首页 > 编程语言 > Java开发

浅谈对Spring mvc的理解和DispatherServlet源码深入分析

2017-09-19 20:54 579 查看

简介

Spring mvc是Spring 为展示层提供的一个优秀的Web框架,它基于MVC的设计理念,并采用松耦合、可插拔的组件结构,让它比其他的MVC框架更具有灵活性和扩展性。

体系结构

学习Spring MVC的时候,最重要还是要懂得它里面内部的各个组件是如何运转工作的,我刚开始接触的时候,只想着一门心思去写代码,而忽略了其最精髓的原理理解和逻辑流程,这也导致我在进行Spring mvc配置和编写代码的时候经常出现错误,反而大大地降低了我的开发效率,因此了解Sping mvc的工作流程、体系结构还是对我们非常有帮助的。

下面是spring mvc的一个框架模型图:



Spring mvc请求处理过程详解:

(1)首先,客户端向服务器短发送一个HTTP请求,我们的应用服务器接收带这个请求后,先判断是否匹配DispatcherServlet的请求路径映射(这是在配置web.xml的时候指定),匹配成功则进入Spring mvc 的核心组件DispatcherServlet中进行处理

(2)DispatcherServlet接受到请求后,将根据请求信息以及HadlerMapping的路径配置找到处理该请求的处理器Handler(也就是我们写得Controller),在这个 过程中,HandlerMapping相当于一个路由控制器,帮助我们去找到“目标主机”Handler

( 3 )找到Handler后,会通过适配器HandlerAdapter对Handler经行封装,并调用统一的适配器接口调用这个Handler.

(4)处理器完成了业务逻辑后,会返回一个ModelAndView对象给DispatcherServlet,这个对象包含了视图逻辑名和后期用来渲染页面的数据信息。

(5)DispatcherServlet得到这个ModelAndView对象后并不能直接地得到 视图对象,注意上面说地是逻辑视图名,而要获取真正的视图名,DispatcherServlet需要借助视图解析器ViewResolver通过解析得到真正的视图名,从而获得视图对象(比如jsp)

(6)之后DispatcherServlet使用之前的Model数据对这个视图View进行页面渲染。

(7)最后返回客户端,可以是html、pdf、xml、json等。

对DispatcherServlet的源码分析

DispatcherServlet可以说是Spring mvc最核心的一个组件,而Spring是如何将业务层的spring mvc的上下文组件装配在这个统一的DispatcherServlet中的呢?我们可以通过对DispatherServlet的源码分析找到答案,下面是DispatcherServler的一些代码,首先DispatcherServlet是继承FramerworkServlet类,而这个FrameworkServlet类又是继承的HttpServletBean,我们现对这个FrameworkServlet的源码中重要方法进行分析,如下:

protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

String method = request.getMethod();
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
//这一这里
processRequest(request, response);
}
else {
super.service(request, response);
}
}

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

//注意这里
processRequest(request, response);
}

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

processRequest(request, response);
}


通过上面每个servlet的几个重要方法(service、do××),它们都依赖这个processRequest方法,而这个processRequest方法中主要依赖的是doService方法,这个doService方法是由protecte abstrac修饰,因此其具体的逻辑由其子类DispatcherServlet实现

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {

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 {
//注意这里
doService(request, response);
}
//省略一系列catch异常处理代码
finally {
//。。。。省略代码      }
}`


这是doService的源码:

protected abstract void doService(HttpServletRequest request, HttpServletResponse response)

throws Exception;

对FrameworkServlet源码总结:FrameworkServlet 中覆盖了 HttpServlet 的 doGet(),doPost()等方法,而 doGet(),doPost()等又直接调用方法 processRequest 来处理请求,进入 processRequest 方法,实际的请求处理是调用其抽象方法 doService(由子类完成),进入DispatcherServlet源码后,我们可以看见在doService里面执行最后最主要的方法代码如下:

@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.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String,
4000
Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}

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

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 {
//注意这里
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);
}
}
}
}


下面是对doDispatch(request,response)源码:

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.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}

// Determine handler adapter for the current request.
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;
}
}

if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

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

applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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);
}
}
}
}


DispatcherServlet 中doService具体实现请求的处理分发,先是把一些资源放到请求属性中,然后调用 doDispatch 实现请求分发到控制器的 handler。doDispatch 中首先会判断是否是文件传输流的请求(利用MultipartResolver),如果是的话就会转为 MultipartHttpServletRequest。接下来 getHandler(processedRequest) 根据请求获得对应的handler,最后调用 handle() 处理请求,会反射到在控制器中实现的方法

好了,我们接着分析DispatcherServlet的initStrategies方法的源码来接之前提出的问题

protected void initStrategies(ApplicationContext context) {
//初始化上传文件解析器
initMultipartResolver(context);
//初始化本地化解析器
initLocaleResolver(context);
initThemeResolver(context);
//初始化处理映射器
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}


因此我们可以得出结论,Spring 之所以能够将Spring mvc的组件组装到DispatcherServlet中是因为在WebAppicationContext执行后,DispatcherServlet会自动执行它里面的innitStrategies方法,而在这个方法里面,会自动地处理化一系列的组件。

private static final String DEFAULT_STRATEGIES_PATH = "DispatcherServlet.properties";

/** Additional logger to use when no mapped handler is found for a request. */
protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);

private static final Properties defaultStrategies;

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 'DispatcherServlet.properties': " + ex.getMessage());
}
}


这个静态代码块表明Spring mvc有一套默认的组件配置,如果我们不对这些组件进行显示地配置,那么这个DispatcherServlet就会初始化实现提供的默认组件配置,我们可以通过调用DispatcherServlet的getDefaultStrategies获取这个默认的组件配置信息。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息