1 首先该类有一静态语块,用以加载缺省策略。
static {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
缺省的策略文件为当前包下的DispatcherServlet.properties,主要包括
LocaleResolver(本地化解析器,AcceptHeaderLocaleResolver)
ThemeResolver(主题解析器,FixedThemeResolver)
HandlerMapping(处理器映射,BeanNameUrlHandlerMapping)
HandlerAdapter(控制适配器,多个)
ViewResolver(视图解析器,InternalResourceViewResolver)
RequestToViewNameTranslator(请求到视图名的翻译器,DefaultRequestToViewNameTranslator)
2 通过继承关系调用HttpServletBean类的init()初始化方法
在该方法体内,首先将这个servlet视为一个bean对其包装并将配置文件中当前servlet下的初始参数值赋倒这个servlet中。然后调用子类(FrameworkServlet)的initServletBean(),而子类通过模板模式再调用其子类(DispatcherServlet)的initFrameworkServlet()方法。在initServletBean()方法中主要代码片段如下:
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
而在initWebApplicationContext()中,它首先是从ServletContext中获得由ContextLoader类设置进的key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性的值(即Spring容器的实例),然后以实例为parent创建一个新的容器。这也是为什么说一个DispatcherServlet就对应一个WebApplicationContext(每个DispatcherServlet都有各自的上下文)。最后将该WebApplicationContext绑定到到ServletContext上,如果允许发布话。
由此可以看出:
WebApplicationContextUtils.getWebApplicationContext(servletContext)与
servletContext.getAttribute(SERVLET_CONTEXT_PREFIX+ getServletName())之间的主要区别在于:后者包括了该servlet的所有配置信息而前者是全局性的没有这部分信息
3 类DispatcherServlet中initFrameworkServlet()方法初始化工作
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
如果在配置文件没有设置将缺省的策略文件DispatcherServlet.properties。由此可见,因为DispatcherServlet是单例的,所以设置的信息是全局的也就是所有的解析器不能动态的变化。
4 当调用类FrameworkServlet中的doGet()/doPost()/doPut()/doDelete()方法时
以上方法均调用相同方法processRequest(request, response)。注意:Spring放弃了对HttpServlet中service()方法的覆盖而用具体的操作方法取代。
5 调用类FrameworkServlet中的processRequest(request, response)方法
try {
doService(request, response);
}
finally {
if (isPublishEvents()) {
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause));
}
}
通过代码摘要可以看出该类有一个webApplicationContext引用,除了执行doService()这个核心方法外,每次都会发布一个ServletRequestHandledEvent事件如果publishEvents为true,其值可以在web.xml文件中增加添加context参数,或servlet初始化参数(
<init-param>
<param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/application.xml</param-value>
</init-param>)。
DispatcherServlet初始化参数
参数
描述
contextClass
实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定,默认使用XmlWebApplicationContext。
contextConfigLocation
传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符)来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。
namespace
WebApplicationContext命名空间。默认值是[server-name]-servlet。
publishContext
我们发布的context是作为ServletContext的一个属性吗?默认值为true,属性名为SERVLET_CONTEXT_PREFIX+ getServletName()
publishEvents
在执行每个请求后是否发布一个ServletRequestHandledEvent事件?默认值为true
以上参数的初始加载是在HttpServletBean类的init()中,而所有DispatcherServlet的解析器是在initFrameworkServlet()方法中。
由此可见,DispatcherServlet的初始参数加载来源可分为两部分,
5.1 在web.xml文件对于该servlet配置的<init-param>初始参数中
5.2 通过Spring的IOC容器实例化后,通过该类的初始化方法,将容器内的实例一一引用
6 调用类DispatcherServlet中的doService(request, response)方法
暴露在request属性中DispatchServlet的一些特性,并且调用doDispatch(request, response)方法执行真正的分发动作。
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());
try {
doDispatch(request, response);
}
7 调用类DispatcherServlet中的doDispatch(request, response)方法
7.1 在区域上下文持有者(LocaleContextHolder)中当前线程下的值做一次替换。暴露当前localeResolver与request中的Locale
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(new LocaleContext() {
public Locale getLocale() {
returnlocaleResolver.resolveLocale(request);
}});
由源码可见,将当前线程下的LocaleContext取出,再从当前区域解析器(LocaleResolver)中得到Locale前创建为LocaleContext后再赋到LocaleContextHolder中
7.2 在请求上下文持有者(RequestContextHolder)中当前线程下的值做一次替换。暴露当前RequestAttributes到当前线程中
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
在此注意,ServletRequestAttributes类只能获得session或request有属性,而无法获得request的参数(parameter)
7.3 转换request为MultipartHttpServletRequest类型的实例,如果不是就简单的返回
protected HttpServletRequest checkMultipart(HttpServletRequest request) {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (request instanceof MultipartHttpServletRequest) {
}
else
returnthis.multipartResolver.resolveMultipart(request);
}
return request;
}
由代码可以看出,如果要支持文件上传要有两个前提
1.在应用的配置文件中添加MultipartResolver(分段文件解析器),目前Spring支持两种上传文件的开源项目Commons FileUpload(http://jakarta.apache.org/commons/fileupload)和COSFileUpload (http://www.servlets.com/cos)
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver"/>
注意:Bean的id必须为’multipartResolver’, DispatcherServlet中的成员属性this.multipartResolver是在初始化时与容器中的实例引用在一起的。
2. 通过multipartResolver.isMultipart(request)判断request的ContextType是否是’multipart/form-data’。所以表单格式应为
<form method="post" action="upload.form" enctype="multipart/form-data">
<input type="file" name="file"/><input type="submit"/>
这个元素的名字(“file”)和服务器端处理这个表单的bean(在下面将会提到)中类型为byte[]的属性名相同。在这个表单里我们也声明了编码参数(enctype="multipart/form-data")以便让浏览器知道如何对这个文件上传表单进行编码
最后由相应的MultipartResolver(分段文件解析器)将request封装为与解析器对应的MultipartHttpServletRequest实例并返回
7.4 根据当前请求URL为其找到指定的处理器(handler)与栏截器(HandlerInterceptor),并将它们封装为HandlerExecutionChain实例。
HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
在此过程中分为如下几个步骤
1. 循环所有已注册的HandlerMapping(处理器映射),对于不同的处理器映射对其映射的处理方式不同。BeanNameUrlHandlerMapping只要在配置文件中bean元素的name属性以/开头它都认为是一个映射如<bean name=”/editaccount”>。而SimpleUrlHandlerMapping只关注自身定义的<property name=”mappings”>下的那些映射,而且支持Ant风格的定义
2. 通过客户请求的URL与处理器映映射的键值做匹配(也可以使用Ant风格匹配,参见AbstractUrlHandlerMapping.lookupHandler()方法),如果满足匹配条件,就返回与其对应的处理器(Handler),一般情况下,都是Controller(控制器)接口子孙类的实例。
3. 根据匹配上的处理器映射取得拦截器数组,并与找到的处理器一同作为构建函数的参数创建出HandlerExecutionChain实例
7.5 执行所有拦截器的前置方法
HandlerInterceptor接口提供了如下三个方法:
1. preHandle(…)前置方法,在处理器执行前调用,当返回false时,DispatcherServlet认为该拦截器已经处理完了请求,而不再继续执行执行链中的其它拦截器和处理器
2. postHandle(…)后置方法,在处理器执行后调用
3. afterCompletion(…)完成前置方法,在整个请求完后调用。注意如果第n个拦截器的preHandle方法返回的是false则只会调用第n个拦截器之前所有该方法,而n++不会被调用
另:Spring还提供了一个adapter类HandlerInterceptorAdapter让用户更方便的扩展HandlerInterceptor接口
7.6 获得与当前处理器相匹配的HandlerAdapter(处理器适配器),
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
循环所有已注册的HandlerAdapter(处理器适配器),如果该适配器支持ha.supports(handler)当前处理器,就返回。由此可见,Spring通过HandlerAdapter使处理器解耦,实处理器(Handler)不只是仅仅局限于控制器(Controller)这一种形式。目前Spring可以支持,Servlet、HttpRequestHandler、ThrowawayController、Controller,从这一点上看Spring已为客户留出了很大空间作为扩展的口子。
7.7 根据处理器适配器调用处理器的指定方法,执行真正的处理操作。如Controller(控制器)接口调用handleRequest() 、ThrowawayController接口调用execute()、Servlet接口调用service()….但无论调用那种自理器,HandlerAdapter中的handle()方法都要求返回ModelAndView的一个实例
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在Spring中最常用的处理器是以Controller接口下的所有子孙类控制器,继承结构如图
在这些控制器中又以SimpleFormController控制器最为常用,下图为该类的完整执行过程
7.8 执行所有拦截器的后置方法
7.9 根据处理器(Handle)返回的ModelAndView对象,找到或创建View对象,并调用View对象的render()方法
render(mv, processedRequest, response)
在此有几个关键性的概念:
ModelAndView:它是模形(Model,实际上只是一个Map,最终会将模形存放到request的属性中,以Map的key为request属性的键,Map的value为值)与视图(View)的持有者。而View又提供了两种形态,即以View接口为祖先的实例对象,以字符串的视图的名字。该类通过isReference()方法判断是一个字符串的引用还是一个真正的View对象。注意:该类有些奇怪:它将有状态的Model与无状态的View合并到一个类中,这就导致每次请求都到对该类型的实例进行创建,而不无缓存。View对象Spring提供了缓存功能。
ViewResolver(视图解析器):主要提供的功能是从视图名称到实际视图的映射。也就是说只有ModelAndView的isReference()返回true时,才会对视图的名字做解析工作。这使视图解析与真正的视图相分离,这样我们就可以通过不同的方式配置视图,如可以采用properties、xml或是提供URI的前、后缀等形式配置视图。但要注意视图解析器与视图之间还是紧密的关系,Sping在此没实现全部的完全解耦
ViewResolver
描述
AbstractCachingViewResolver
抽象视图解析器实现了对视图的缓存。在视图被投入使用之前,通常需要进行一些准备工作。从它继承的视图解析器将对要解析的视图进行缓存。
InternalResourceViewResolver
作为UrlBasedViewResolver的子类,它支持InternalResourceView(对Servlet和JSP的包装),以及其子类JstlView和TilesView。通过setViewClass方法,可以指定用于该解析器生成视图使用的视图类。
UrlBasedViewResolver
UrlBasedViewResolver实现ViewResolver,将视图名直接解析成对应的URL,不需要显式的映射定义。如果你的视图名和视图资源的名字是一致的,就可使用该解析器,而无需进行映射。
ResourceBundleViewResolver
ResourceBundleViewResolver实现ViewResolver,在一个ResourceBundle中寻找所需bean的定义。这个bundle通常定义在一个位于classpath中的属性文件中。默认的属性文件是views.properties。
XmlViewResolver
XmlViewResolver实现ViewResolver,支持XML格式的配置文件。该配置文件必须采用与Spring XML Bean Factory相同的DTD。默认的配置文件是/WEB-INF/views.xml。
VelocityViewResolver / FreeMarkerViewResolver
作为UrlBasedViewResolver的子类,它能支持VelocityView(对Velocity模版的包装)和FreeMarkerView以及它们的子类。
View(视图):处理请求的准备工作,并将该请求提交给某种具体的视图技术。
如,InternalResourceView会通过RequestDispatcher类调用include()或forward()方法
RedirectView会通过HttpServletResponse类调用sendRedirect()方法
AbstractExcelView会产生一个输出流
区别:
1、forward与include区别在于forward本意是让第一个页面处理request,第二个页面处理response.调用第二个页面后,程序还会返回第一个页面继续执行,但是此时再使用response输出已经没有作用了;include服务器端的动态加载,执行完第二个页面的程序后可以回到第一个页面继续输出,即将第二个页面的输出拉到第一个页面中。
2、forward与include共享Request范围内的对象,而redirect则不行,即:如果一个javabean被声明为request范围的话,则被forward到的资源也可以访问这个javabean,而redriect则不行。
3、forward与include基本上都是转发到context内部的资源,而redirect可以重定向到外部的资源,如: req.sendRedriect("http://www.mocuai.com");
static {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
缺省的策略文件为当前包下的DispatcherServlet.properties,主要包括
LocaleResolver(本地化解析器,AcceptHeaderLocaleResolver)
ThemeResolver(主题解析器,FixedThemeResolver)
HandlerMapping(处理器映射,BeanNameUrlHandlerMapping)
HandlerAdapter(控制适配器,多个)
ViewResolver(视图解析器,InternalResourceViewResolver)
RequestToViewNameTranslator(请求到视图名的翻译器,DefaultRequestToViewNameTranslator)
2 通过继承关系调用HttpServletBean类的init()初始化方法
在该方法体内,首先将这个servlet视为一个bean对其包装并将配置文件中当前servlet下的初始参数值赋倒这个servlet中。然后调用子类(FrameworkServlet)的initServletBean(),而子类通过模板模式再调用其子类(DispatcherServlet)的initFrameworkServlet()方法。在initServletBean()方法中主要代码片段如下:
this.webApplicationContext = initWebApplicationContext();
initFrameworkServlet();
而在initWebApplicationContext()中,它首先是从ServletContext中获得由ContextLoader类设置进的key为WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性的值(即Spring容器的实例),然后以实例为parent创建一个新的容器。这也是为什么说一个DispatcherServlet就对应一个WebApplicationContext(每个DispatcherServlet都有各自的上下文)。最后将该WebApplicationContext绑定到到ServletContext上,如果允许发布话。
由此可以看出:
WebApplicationContextUtils.getWebApplicationContext(servletContext)与
servletContext.getAttribute(SERVLET_CONTEXT_PREFIX+ getServletName())之间的主要区别在于:后者包括了该servlet的所有配置信息而前者是全局性的没有这部分信息
3 类DispatcherServlet中initFrameworkServlet()方法初始化工作
initMultipartResolver();
initLocaleResolver();
initThemeResolver();
initHandlerMappings();
initHandlerAdapters();
initHandlerExceptionResolvers();
initRequestToViewNameTranslator();
initViewResolvers();
如果在配置文件没有设置将缺省的策略文件DispatcherServlet.properties。由此可见,因为DispatcherServlet是单例的,所以设置的信息是全局的也就是所有的解析器不能动态的变化。
4 当调用类FrameworkServlet中的doGet()/doPost()/doPut()/doDelete()方法时
以上方法均调用相同方法processRequest(request, response)。注意:Spring放弃了对HttpServlet中service()方法的覆盖而用具体的操作方法取代。
5 调用类FrameworkServlet中的processRequest(request, response)方法
try {
doService(request, response);
}
finally {
if (isPublishEvents()) {
this.webApplicationContext.publishEvent(
new ServletRequestHandledEvent(this,
request.getRequestURI(), request.getRemoteAddr(),
request.getMethod(), getServletConfig().getServletName(),
WebUtils.getSessionId(request), getUsernameForRequest(request),
processingTime, failureCause));
}
}
通过代码摘要可以看出该类有一个webApplicationContext引用,除了执行doService()这个核心方法外,每次都会发布一个ServletRequestHandledEvent事件如果publishEvents为true,其值可以在web.xml文件中增加添加context参数,或servlet初始化参数(
<init-param>
<param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/application.xml</param-value>
</init-param>)。
DispatcherServlet初始化参数
参数
描述
contextClass
实现WebApplicationContext接口的类,当前的servlet用它来创建上下文。如果这个参数没有指定,默认使用XmlWebApplicationContext。
contextConfigLocation
传给上下文实例(由contextClass指定)的字符串,用来指定上下文的位置。这个字符串可以被分成多个字符串(使用逗号作为分隔符)来支持多个上下文(在多上下文的情况下,如果同一个bean被定义两次,后面一个优先)。
namespace
WebApplicationContext命名空间。默认值是[server-name]-servlet。
publishContext
我们发布的context是作为ServletContext的一个属性吗?默认值为true,属性名为SERVLET_CONTEXT_PREFIX+ getServletName()
publishEvents
在执行每个请求后是否发布一个ServletRequestHandledEvent事件?默认值为true
以上参数的初始加载是在HttpServletBean类的init()中,而所有DispatcherServlet的解析器是在initFrameworkServlet()方法中。
由此可见,DispatcherServlet的初始参数加载来源可分为两部分,
5.1 在web.xml文件对于该servlet配置的<init-param>初始参数中
5.2 通过Spring的IOC容器实例化后,通过该类的初始化方法,将容器内的实例一一引用
6 调用类DispatcherServlet中的doService(request, response)方法
暴露在request属性中DispatchServlet的一些特性,并且调用doDispatch(request, response)方法执行真正的分发动作。
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());
try {
doDispatch(request, response);
}
7 调用类DispatcherServlet中的doDispatch(request, response)方法
7.1 在区域上下文持有者(LocaleContextHolder)中当前线程下的值做一次替换。暴露当前localeResolver与request中的Locale
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContextHolder.setLocaleContext(new LocaleContext() {
public Locale getLocale() {
returnlocaleResolver.resolveLocale(request);
}});
由源码可见,将当前线程下的LocaleContext取出,再从当前区域解析器(LocaleResolver)中得到Locale前创建为LocaleContext后再赋到LocaleContextHolder中
7.2 在请求上下文持有者(RequestContextHolder)中当前线程下的值做一次替换。暴露当前RequestAttributes到当前线程中
RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
在此注意,ServletRequestAttributes类只能获得session或request有属性,而无法获得request的参数(parameter)
7.3 转换request为MultipartHttpServletRequest类型的实例,如果不是就简单的返回
protected HttpServletRequest checkMultipart(HttpServletRequest request) {
if (this.multipartResolver != null && this.multipartResolver.isMultipart(request)) {
if (request instanceof MultipartHttpServletRequest) {
}
else
returnthis.multipartResolver.resolveMultipart(request);
}
return request;
}
由代码可以看出,如果要支持文件上传要有两个前提
1.在应用的配置文件中添加MultipartResolver(分段文件解析器),目前Spring支持两种上传文件的开源项目Commons FileUpload(http://jakarta.apache.org/commons/fileupload)和COSFileUpload (http://www.servlets.com/cos)
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
<bean id="multipartResolver" class="org.springframework.web.multipart.cos.CosMultipartResolver"/>
注意:Bean的id必须为’multipartResolver’, DispatcherServlet中的成员属性this.multipartResolver是在初始化时与容器中的实例引用在一起的。
2. 通过multipartResolver.isMultipart(request)判断request的ContextType是否是’multipart/form-data’。所以表单格式应为
<form method="post" action="upload.form" enctype="multipart/form-data">
<input type="file" name="file"/><input type="submit"/>
这个元素的名字(“file”)和服务器端处理这个表单的bean(在下面将会提到)中类型为byte[]的属性名相同。在这个表单里我们也声明了编码参数(enctype="multipart/form-data")以便让浏览器知道如何对这个文件上传表单进行编码
最后由相应的MultipartResolver(分段文件解析器)将request封装为与解析器对应的MultipartHttpServletRequest实例并返回
7.4 根据当前请求URL为其找到指定的处理器(handler)与栏截器(HandlerInterceptor),并将它们封装为HandlerExecutionChain实例。
HandlerExecutionChain mappedHandler = getHandler(processedRequest, false);
在此过程中分为如下几个步骤
1. 循环所有已注册的HandlerMapping(处理器映射),对于不同的处理器映射对其映射的处理方式不同。BeanNameUrlHandlerMapping只要在配置文件中bean元素的name属性以/开头它都认为是一个映射如<bean name=”/editaccount”>。而SimpleUrlHandlerMapping只关注自身定义的<property name=”mappings”>下的那些映射,而且支持Ant风格的定义
2. 通过客户请求的URL与处理器映映射的键值做匹配(也可以使用Ant风格匹配,参见AbstractUrlHandlerMapping.lookupHandler()方法),如果满足匹配条件,就返回与其对应的处理器(Handler),一般情况下,都是Controller(控制器)接口子孙类的实例。
3. 根据匹配上的处理器映射取得拦截器数组,并与找到的处理器一同作为构建函数的参数创建出HandlerExecutionChain实例
7.5 执行所有拦截器的前置方法
HandlerInterceptor接口提供了如下三个方法:
1. preHandle(…)前置方法,在处理器执行前调用,当返回false时,DispatcherServlet认为该拦截器已经处理完了请求,而不再继续执行执行链中的其它拦截器和处理器
2. postHandle(…)后置方法,在处理器执行后调用
3. afterCompletion(…)完成前置方法,在整个请求完后调用。注意如果第n个拦截器的preHandle方法返回的是false则只会调用第n个拦截器之前所有该方法,而n++不会被调用
另:Spring还提供了一个adapter类HandlerInterceptorAdapter让用户更方便的扩展HandlerInterceptor接口
7.6 获得与当前处理器相匹配的HandlerAdapter(处理器适配器),
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
Iterator it = this.handlerAdapters.iterator();
while (it.hasNext()) {
HandlerAdapter ha = (HandlerAdapter) it.next();
if (ha.supports(handler)) {
return ha;
}
}
循环所有已注册的HandlerAdapter(处理器适配器),如果该适配器支持ha.supports(handler)当前处理器,就返回。由此可见,Spring通过HandlerAdapter使处理器解耦,实处理器(Handler)不只是仅仅局限于控制器(Controller)这一种形式。目前Spring可以支持,Servlet、HttpRequestHandler、ThrowawayController、Controller,从这一点上看Spring已为客户留出了很大空间作为扩展的口子。
7.7 根据处理器适配器调用处理器的指定方法,执行真正的处理操作。如Controller(控制器)接口调用handleRequest() 、ThrowawayController接口调用execute()、Servlet接口调用service()….但无论调用那种自理器,HandlerAdapter中的handle()方法都要求返回ModelAndView的一个实例
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
在Spring中最常用的处理器是以Controller接口下的所有子孙类控制器,继承结构如图
在这些控制器中又以SimpleFormController控制器最为常用,下图为该类的完整执行过程
7.8 执行所有拦截器的后置方法
7.9 根据处理器(Handle)返回的ModelAndView对象,找到或创建View对象,并调用View对象的render()方法
render(mv, processedRequest, response)
在此有几个关键性的概念:
ModelAndView:它是模形(Model,实际上只是一个Map,最终会将模形存放到request的属性中,以Map的key为request属性的键,Map的value为值)与视图(View)的持有者。而View又提供了两种形态,即以View接口为祖先的实例对象,以字符串的视图的名字。该类通过isReference()方法判断是一个字符串的引用还是一个真正的View对象。注意:该类有些奇怪:它将有状态的Model与无状态的View合并到一个类中,这就导致每次请求都到对该类型的实例进行创建,而不无缓存。View对象Spring提供了缓存功能。
ViewResolver(视图解析器):主要提供的功能是从视图名称到实际视图的映射。也就是说只有ModelAndView的isReference()返回true时,才会对视图的名字做解析工作。这使视图解析与真正的视图相分离,这样我们就可以通过不同的方式配置视图,如可以采用properties、xml或是提供URI的前、后缀等形式配置视图。但要注意视图解析器与视图之间还是紧密的关系,Sping在此没实现全部的完全解耦
ViewResolver
描述
AbstractCachingViewResolver
抽象视图解析器实现了对视图的缓存。在视图被投入使用之前,通常需要进行一些准备工作。从它继承的视图解析器将对要解析的视图进行缓存。
InternalResourceViewResolver
作为UrlBasedViewResolver的子类,它支持InternalResourceView(对Servlet和JSP的包装),以及其子类JstlView和TilesView。通过setViewClass方法,可以指定用于该解析器生成视图使用的视图类。
UrlBasedViewResolver
UrlBasedViewResolver实现ViewResolver,将视图名直接解析成对应的URL,不需要显式的映射定义。如果你的视图名和视图资源的名字是一致的,就可使用该解析器,而无需进行映射。
ResourceBundleViewResolver
ResourceBundleViewResolver实现ViewResolver,在一个ResourceBundle中寻找所需bean的定义。这个bundle通常定义在一个位于classpath中的属性文件中。默认的属性文件是views.properties。
XmlViewResolver
XmlViewResolver实现ViewResolver,支持XML格式的配置文件。该配置文件必须采用与Spring XML Bean Factory相同的DTD。默认的配置文件是/WEB-INF/views.xml。
VelocityViewResolver / FreeMarkerViewResolver
作为UrlBasedViewResolver的子类,它能支持VelocityView(对Velocity模版的包装)和FreeMarkerView以及它们的子类。
View(视图):处理请求的准备工作,并将该请求提交给某种具体的视图技术。
如,InternalResourceView会通过RequestDispatcher类调用include()或forward()方法
RedirectView会通过HttpServletResponse类调用sendRedirect()方法
AbstractExcelView会产生一个输出流
区别:
1、forward与include区别在于forward本意是让第一个页面处理request,第二个页面处理response.调用第二个页面后,程序还会返回第一个页面继续执行,但是此时再使用response输出已经没有作用了;include服务器端的动态加载,执行完第二个页面的程序后可以回到第一个页面继续输出,即将第二个页面的输出拉到第一个页面中。
2、forward与include共享Request范围内的对象,而redirect则不行,即:如果一个javabean被声明为request范围的话,则被forward到的资源也可以访问这个javabean,而redriect则不行。
3、forward与include基本上都是转发到context内部的资源,而redirect可以重定向到外部的资源,如: req.sendRedriect("http://www.mocuai.com");
相关推荐
通过学习和分析这些代码,初学者可以了解 SpringMVC 的基本架构和工作流程,从而逐步掌握如何创建、配置和运行一个简单的 SpringMVC 项目。在实践中,你可以尝试修改代码,添加新的功能,以此加深理解。同时,不要...
SpringMVC是Spring框架的一个模块,专为构建Web应用程序提供模型-视图-控制器(MVC)架构。这个“springMVC练手代码”压缩包包含...通过分析和运行这些代码,你可以加深对SpringMVC的理解,掌握构建实际Web应用的能力。
在本项目中,"springmvc 完整工程代码" 提供了一个简单的示例,帮助开发者了解如何设置和运行一个基本的 Spring MVC 项目。 1. **Spring MVC 概述** Spring MVC 通过提供控制器、模型、视图的分离,使得开发者可以...
本资料"看透SpringMVC源代码-分析与实践"旨在深入剖析SpringMVC的工作原理,帮助开发者更好地理解和运用这个强大的工具。 首先,我们来探讨SpringMVC的核心概念: 1. **DispatcherServlet**:它是SpringMVC的前端...
- 解压缩文件"SpringMVC+Spring+myBatis框架整合示例代码.zip",包含了一个完整的整合项目结构。 - 在IDE中导入项目,配置相应的运行环境(如Tomcat服务器、JDBC驱动等)。 - 运行项目,通过浏览器访问测试接口,...
SpringMVC是一种轻量级的Web开发框架,它简化了Java Web应用的开发过程,提供了一种模块化和可测试性的结构。SpringMVC遵循MVC设计模式,将应用程序的业务逻辑、用户界面和数据访问层有效地分离开来。 2. **核心...
**SpringMVC快速入门基础代码详解** SpringMVC是Spring框架的一个模块,专门用于构建Web应用程序。它提供了模型-视图-控制器(MVC)架构,使得开发人员能够更高效地处理HTTP请求和响应,同时保持代码的清晰性和可...
这个“一个简单的可运行springMVC项目”提供了一个完整的环境,让你无需额外下载任何库就能直接在Eclipse JavaEE IDE上进行开发和运行。它依赖于Tomcat7作为应用服务器,以及Jdk1.7作为Java运行时环境。 1. **...
在"SpringMVC_源码分析代码.zip"这个压缩包中,我们可以找到一个名为"5.SpringMVC_output"的文件或目录,这可能包含的是一个示例项目的结果输出,或者是源码分析过程中的测试或运行产出。通常,这样的文件夹会包括...
在SpringMVC中,DispatcherServlet是入口点,负责调度请求到相应的处理器。 2. **配置文件**: 实例中的配置文件可能包括`web.xml`(部署描述符)和SpringMVC的配置文件(如`spring-mvc.xml`)。`web.xml`用于配置...
对于初学者来说,这是一个很好的实践平台,通过查看和运行这些代码,可以加深对SpringMVC工作原理的理解。同时,结合人工智能的标签,可能还涉及到如何将SpringMVC应用于AI相关的Web应用,如机器学习模型的API接口...
配置SpringMVC通常包括web.xml中的DispatcherServlet配置、SpringMVC的配置文件以及注解驱动的配置。 MyBatis则是一个轻量级的持久层框架,它避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。MyBatis允许...
SpringMVC通过DispatcherServlet接收请求,使用HandlerMapping映射到相应的Controller,Controller处理完业务逻辑后,通过ViewResolver返回视图,最后呈现给用户。 Mybatis是一个优秀的持久层框架,它支持定制化SQL...
SpringMVC是Spring框架的一部分,专门用于处理Web应用程序的请求-响应模型...你可以通过解压并运行该项目,来深入理解SpringMVC的工作原理和使用方式。这将是一个很好的学习资源,帮助你从实践中掌握SpringMVC的精髓。
- 测试和运行:编写测试用例验证各组件是否正常工作,如Service层的业务逻辑、DAO层的数据访问,以及Controller层的请求处理。 SSH集成使得开发者可以专注于业务逻辑,而不用过多关注底层的细节,提高了开发效率和...
在这个Demo中,你可以看到如何配置SpringMVC的DispatcherServlet,以及编写简单的Controller来处理登录请求。 2. **Hibernate框架**:Hibernate是一个强大的ORM框架,能够简化数据库操作,通过Java对象与数据库表...
- 配置SpringMVC的DispatcherServlet,设置URL映射和视图解析器。 - 设计良好的Controller接口,每个方法对应一个特定的HTTP请求,避免过于庞大的Controller。 - 使用注解驱动的编程方式,如@Controller、@...
《Spring MVC源代码分析与实践》是一份深入探讨Spring MVC框架内部机制的资源,适合对Java Web开发有深入了解并希望优化应用性能的开发者。Spring MVC是Spring框架的一部分,用于构建可扩展、模块化的Web应用程序。...
【标题】"SpringMVC+MyBatis+Maven实例可运行带数据库脚本"是一个典型的Java Web项目架构,它整合了三个主流的开源框架,为开发者提供了快速开发Web应用的解决方案。 【SpringMVC】是Spring框架的一部分,主要用于...
1. **配置 SpringMVC**:在 `web.xml` 文件中配置 DispatcherServlet 和 SpringMVC 配置文件的路径。 2. **编写 Controller**:创建一个简单的 Java 类,使用 `@Controller` 注解标记,定义一个处理方法,如 `hello...