springmvc(1)DispatcherServlet源码简单解析
springmvc的简单配置
1.首先需要在web.xml中配置DispatcherServlet,这个类是springmvc的核心类,所以的操作都是由这里开始,并且大部分都是在这里面实现的,比如各种视图的解析,视图的
映射等等。配置文件:
<servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
既然是和spring一套的,那肯定是有配置文件的,但是我们却没有配置文件路径,文件的默认路径是可以修改的,如果我们的这个文件没有配置路径的话,默认的路径是
/WEB-INF/springServlet-servlet.xml // 默认的路径就是:/WEB-INF/[servlet的名称]-servlet.xml
但是我们是可以自己设置属性的,除了文件路径,还可以设置一下的几个属性:
contextClass:实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定, 默认使用XmlWebApplicationContext。 contextConfigLocation:传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符) 来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。 namespace:WebApplicationContext命名空间。默认值是[server-name]-servlet。
文件既可以放在WEB-INF下面,也可以放在classpath下面,比如:
<servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
注意一点的是,这个classpath指的是编译后的位置,刚开始学习框架的时候,总会犯这样的错误,找不到配置文件,就是因为classpath的问题。
对于web项目,还有一种通用的写法,就是放在ContextLoaderListener下面:
restful风格的
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:/spring-context*.xml</param-value> </context-param>
支持多文件的:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:spring-common-config.xml, classpath:spring-budget-config.xml </param-value> </context-param>
这样子,springmvc基本上是配置好了,这里没有讲spring的配置。
上面配置好了简单的web.xml以后,我们来讲一下DispatcherServlet.
从上面我们应该就明白了,其实它就是一个servlet,既然是servlet,那么理解起来就简单多了。
DispathcherServlet最主要的就是下面几个方法,下面一一来简单看看:
/** * Initialize the strategy objects that this servlet uses. * <p>May be overridden in subclasses in order to initialize further strategy objects. */ protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
这个方法是初始化各种解析器,适配等等的方法,这个方法是在其父类的初始化方法中调用的,没记错的话,其实就是相当于Servlet的init方法执行时调用。首先一起了解下上面每一个方法初始化的都是什么:
1.MultipartResolver指的是文件上传解析类,对于文件上传的时候,需要使用到,我们看他的方法:
private void initMultipartResolver(ApplicationContext context) { try {
//这里获取bean 注意这个常量在前面的是定义的:MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver";
//所以我们配置的时候 bean的名称必须是上面的这个 不然是不能识别的哦 注意咯
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class); if (logger.isDebugEnabled()) { logger.debug("Using MultipartResolver [" + this.multipartResolver + "]"); } } catch (NoSuchBeanDefinitionException ex) { // Default is no multipart resolver. this.multipartResolver = null; if (logger.isDebugEnabled()) { logger.debug("Unable to locate MultipartResolver with name '" + MULTIPART_RESOLVER_BEAN_NAME + "': no multipart request handling provided"); } } }
如果没有配置 那么默认就是没有文件上传解析的。这个时候,如果上传,那么会出一些问题的。
2.LocaleResolver这个是本地化解析,也就是做国际化的,如果没有设置,那么会默认为我们设置一个
3.ThemeResolver 看名字也知道,就是风格和主题解析类,默认也会有为我们设置一个
4.HandlerMappings :请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器;从下面的代码中可以看出
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象);
//如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器,注意这里处理器值得是handler,而不是handlerMapping,形象的将
//就是handlerMapping是一个模板,处理器handler就是根据你的bean刻出的成型产品。
for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
5.HandlerAdapters:看到适配器了吧,对各种处理器包装成适配器,这样就能支持多种类型的处理器了。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//将处理器进行包装,后面的操作都是都是由适配器进行工作
6HandlerExceptionResolvers:一场解析器,就比如出现错误的时候,可能统一调到某一个页面。默认也有实现,这个需求有太多中解决方案了,没什么好讲
7.RequestToViewNameTranslator:当处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名;
8ViewResolvers:视图解析器。我们的配置中都会一般都会配置一个视图,就像这样:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="suffix" value=".jsp"></property> <property name="prefix" value="/WEB-INF/views/module/"></property> </bean>
视图解析器就是将上面的进行解析。超类只有一个方法
View resolveViewName(String viewName, Locale locale) throws Exception;
9.FlashMapManager值得是redirect的时候保存数据的一个相当于map分装的一个容器吧,但是官方是说在程序中一般不会使用的,比如在controller,就经常是用RedirectAttributes 来传输数据
讲完了几个比较重要的属性,然后将主要的几个放大,既然是servlet,那么我们就来看看service方法吧,这里的service是这样的:
protected void doService(HttpServletRequest request, HttpServletResponse response)
里面有一个这样的依据代码需要引起重视,其他的代码都是配置一些属性什么的
try { doDispatch(request, response); }
这个方法其实就是DispatchServlet的核心,我们前面讲到的解析器,适配器等等都是在这里操作的:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;//前面讲过。handlerMapping映射成功,都会返回这个东西,其中包括处理器和拦截器 boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try {
//查看请求是否是Multipart(如文件上传) 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; }
//解析视图 最终会调用view.render(mv.getModelInternal(), request, response);将model传进去 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); } } } }
上面也就简单的了解了一下springmvc的入口,接下来几天在研究一下springmvc其他的一些功能。好多东西都是自己理解的,不知道对不对==。