springMVC源码浅析
因故,需要学习springmvc,现在demo已经跑起来。列文分析springmvc并简单对比struts2.
springmvc的核心对象dispatcherServlet。struts2的核心对象是strutsPrepareAndExecuteFilter,拦截的方式不同,但同为前段控制器(front controller)设计模式。
dispatcherServlet的初始化:
JVM加载dispatcherServlet时,会先执行静态代码块:
static{
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
这两行将springMVC包内的dispatcherServlet.properties文件中缺省的HandlerAdapter、HandlerMapping、ViewResolver等等。
容器初始化时,会调用HttpServlet的init()方法,这个方法被httpServletBean重写了,在httpServletBean中,调用frameworkServlet的initServletBean()方法,这个方法在调用dispatcherServlet真正的onRefresh()方法,到initStrategies()。
initStrategies()有所有的初始化工作:【老版本好像叫做initFrameworkServlet】
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);//初始化所有的请求映射,存储到dispatcherServlet的handlerMappings成员变量中(List)
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);//初始化所有视图解析器,存储到dispatcherServlet的viewResolvers成员变量中(List)
initFlashMapManager(context);
}
dispatcherServlet对请求的拦截:
一个请求到达后端时,若符合我们dispatcherServlet的servletMapping,则会进入dispatcherServlet。默认会调用其父类frameworkServlet的doGet、doPost方法,这两个方法再分发到protected final void processRequest(request,response)方法中。这个方法会调用dispatcherServlet的doService(request,response)方法。
然后在此方法中截取URI,然后把上下文等信息放在request的attribute中,调用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 {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = getHandler(processedRequest, false);//拿到HandlerExecutionChain
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());//拿到HandlerExecutionChain中封装的HandlerAdapter
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
//执行所有的interceptor的interceptor.preHandle(request, response, this.handler)方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());//执行真handlerAdapter中封装的controller真正的业务方法,返回modelAndView
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
} applyDefaultViewName(request, mv);
//执行所有拦截器的interceptor.postHandle(request, response, this.handler, modelAndView)方法
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()) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
} //遍历所有注册的handlerMappings,拿出一个支持当前handlerMapping的adapter,封装到HandlerExecutionChain中
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
for (HandlerMapping hm : this.handlerMappings) {
HandlerExecutionChain handler = hm.getHandler(request);//不同类型的handlerMapping有不同的getHandler实现
if (handler != null) {
return handler;
}
}
return null;
}
doDispatch业务处理方法:
1、通过URI,遍历成员变量handlerMappings,拿到URI对应的controller、interceptor放到HandlerExecutionChain中(不同类型的mapping拿的方式不一样,如SimpleUrlHandlerMapping是通过URI映射规则从map中拿到controller)。
从doDispatcher的代码中,很明显可以看出一条线索,整个方法是围绕着如何获取HandlerExecutionChain对象,执行HandlerExecutionChain对象得到相应的视图对象,再对视图进行渲染这条主线来展开的。HandlerExecutionChain对象显得异常重要。
因为Http请求要进入SpringMVC的处理体系,必须由HandlerMapping接口的实现类映射Http请求,得到一个封装后的HandlerExecutionChain对象。再由HandlerAdapter接口的实现类来处理这个HandlerExecutionChain对象所包装的处理对象,来得到最后渲染的视图对象。
2、循环所有的HandlerAdapter,通过support方法拿到支持目标controller的的一个adapter。
循环所有已注册的HandlerAdapter(处理器适配器),如果该适配器支持ha.supports(handler)当前处理器,就返回。由此可见,Spring通过HandlerAdapter使处理器解耦,实处理器(Handler)不只是仅仅局限于控制器(Controller)这一种形式。目前Spring可以支持,Servlet、HttpRequestHandler、ThrowawayController、Controller,从这一点上看Spring已为客户留出了很大空间作为扩展的口子。
http://www.blogjava.net/hao-zhang-hi/archive/2011/03/10/346081.html
3、调用adapter 的applyPreHandle(request,response),执行所有的interceptor的interceptor.preHandle(request, response, this.handler)方法。
4、调用adapter的handle(request, response, mappedHandler.getHandler());去执行controller中真正的业务方法,返回ModelAndView对象。
5、调用adapter的applyPostHandle(request,response,modelAndView)方法,执行所有拦截器的interceptor.postHandle(request, response, this.handler, modelAndView)方法。
6、调用processDispatchResult(request,response,HandlerExecutionChain,ModelAndView,Exception),如果exception不为空,返回配置的异常modelAndView。如果exception为空,根据modelAndView中的字符串拼接视图完整路径。
7、如我们配置了jsp的JstlView,调用view.render(Map model,request,response),调用InternalResourceView的renderMergedOutputModel方法,拿到requestDispatcher,forward到视图。最后执行mappedHandler.triggerAfterCompletion(request, response, null)
Spring MVC PK Struts2
我们用struts2时采用的传统的配置文件的方式,并没有使用传说中的0配置。spring3 mvc可以认为已经100%零配置了(除了配置spring mvc-servlet.xml外)。
Spring MVC和Struts2的区别:
1. 机制:spring mvc的入口是servlet,而struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这样就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
(区别:1,servlet 流程是短的,url传来之后,就对其进行处理,之后返回或转向到某一自己指定的页面。它主要用来在
业务处理之前进行控制.
2,filter 流程是线性的, url传来之后,检查之后,可保持原来的流程继续向下执行,被下一个filter, servlet接收等,而servlet 处理之后,不会继续向下传递。filter功能可用来保持流程继续按照原来的方式进行下去,或者主导流程,而servlet的功能主要用来主导流程。
filter可用来进行字符编码的过滤,检测用户是否登陆的过滤,禁止页面缓存等
3, servlet,filter都是针对url之类的,而listener是针对对象的操作的,如session的创建,session.setAttribute的发生,在这样的事件发生时做一些事情。
可用来进行:Spring整合Struts,为Struts的action注入属性,web应用定时任务的实现,在线人数的统计等
4,interceptor 拦截器,类似于filter,不过在struts.xml中配置,不是在web.xml,并且不是针对URL的,而是针对action,当页面提交action时,进行过滤操作,相当于struts1.x提供的plug-in机制,可以看作,前者是struts1.x自带的filter,而interceptor 是struts2 提供的filter.
与filter不同点:(1)不在web.xml中配置,而是在struts.xml中完成配置,与action在一起
( 2 ) 可由action自己指定用哪个interceptor 来在接收之前做事)
2. 性能:spring会稍微比struts快。spring mvc是基于方法的设计,而sturts是基于类,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,粒度更细,但要小心把握像在servlet控制数据一样。spring3 mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3 mvc中,一个方法对应一个request上下文。而struts2框架是类级别的拦截,每次来了请求就创建一个Action,然后调用setter getter方法把request中的数据注入;struts2实际上是通过setter getter方法与request打交道的;struts2中,一个Action对象对应一个request上下文。
3. 参数传递:struts是在接受参数的时候,可以用属性来接受参数,这就说明参数是让多个方法共享的。
4. 设计思想上:struts更加符合oop的编程思想, spring就比较谨慎,在servlet上扩展。
5. intercepter的实现机制:struts有以自己的interceptor机制,spring mvc用的是独立的AOP方式。这样导致struts的配置文件量还是比spring mvc大,虽然struts的配置能继承,所以我觉得论使用上来讲,spring mvc使用更加简洁,开发效率Spring MVC确实比struts2高。spring mvc是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上spring3 mvc就容易实现restful url。struts2是类级别的拦截,一个类对应一个request上下文;实现restful url要费劲,因为struts2 action的一个方法可以对应一个url;而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。spring3 mvc的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架方法之间不共享变量,而struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码,读程序时带来麻烦。
6. 另外,spring3 mvc的验证也是一个亮点,支持JSR303,处理ajax的请求更是方便,只需一个注解@ResponseBody ,然后直接返回响应文本即可。送上一段代码:
@RequestMapping(value="/whitelists") public String index(ModelMap map) { Account account = accountManager.getByDigitId(SecurityContextHolder.get().getDigitId()); List<Group> groupList = groupManager.findAllGroup(account.getId()); map.put("account", account); map.put("groupList", groupList); return "/group/group-index"; }
// @ResponseBody ajax响应,处理Ajax请求也很方便 @RequestMapping(value="/whitelist/{whiteListId}/del") @ResponseBody public String delete(@PathVariable Integer whiteListId) { whiteListManager.deleteWhiteList(whiteListId); return "success"; }
http://www.cnblogs.com/youngdream-ppj/archive/2013/03/20/2971873.html
springMVC源码浅析的更多相关文章
- 【深入浅出jQuery】源码浅析--整体架构
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- 【深入浅出jQuery】源码浅析2--奇技淫巧
最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐 ...
- SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现
SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...
- SpringMVC源码分析系列
说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时sp ...
- Struts2源码浅析-ConfigurationProvider
ConfigurationProvider接口 主要完成struts配置文件 加载 注册过程 ConfigurationProvider接口定义 public interface Configurat ...
- SpringMVC源码剖析(二)- DispatcherServlet的前世今生
上一篇文章<SpringMVC源码剖析(一)- 从抽象和接口说起>中,我介绍了一次典型的SpringMVC请求处理过程中,相继粉墨登场的各种核心类和接口.我刻意忽略了源码中的处理细节,只列 ...
- (转)【深入浅出jQuery】源码浅析2--奇技淫巧
[深入浅出jQuery]源码浅析2--奇技淫巧 http://www.cnblogs.com/coco1s/p/5303041.html
- HashSet其实就那么一回事儿之源码浅析
上篇文章<HashMap其实就那么一回事儿之源码浅析>介绍了hashMap, 本次将带大家看看HashSet, HashSet其实就是基于HashMap实现, 因此,熟悉了HashMap ...
- Android 手势识别类 ( 三 ) GestureDetector 源码浅析
前言:上 篇介绍了提供手势绘制的视图平台GestureOverlayView,但是在视图平台上绘制出的手势,是需要存储以及在必要的利用时加载取出手势.所 以,用户绘制出的一个完整的手势是需要一定的代码 ...
随机推荐
- 简单的doc命令
cd 切换目录 dir 显示目录列表 mkdir 创建目录(mkdir) rmdir 删除空目录(rmdir test) rmdir /s 删除非空目录(rmdir test /s) echo 创建 ...
- 浅谈.prop() 和 attr() 的区别
今天编码时遇到一个问题,通过后台查询的数据设置前端checkbox的选中状态,设置选中状态为.attr('checked','true');没有问题,但是当数据重新加载时,checkbox应清空即所有 ...
- [DevExpress]DxValidationProvider分享
前些日子从研究所临时调回公司,帮忙做另外一个项目的控件验证工作,其实内容非常的简单,就是将用户即将提交至服务器的数据先做一个本地验证,以达到减少服务器压力.提高用户体验的目的. 附上一张图片 这是官方 ...
- 结构型模式(Structural patterns)->外观模式(Facade Pattern)
动机(Motivate): 在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化.那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子 ...
- about hadoop-eclipse-plugin used by IDE
Apache Hadoop Development Tools (HDT) is still in development phase. So, no official distribution of ...
- OrderedDict
OrderedDict 使用dict时,Key是无序的.在对dict做迭代时,我们无法确定Key的顺序. 如果要保持Key的顺序,可以用OrderedDict: >>> from c ...
- STM32库函数开发使用总结
一.外设常具备的几类寄存器 控制寄存器xxx_CR (Control/Configuration Register): 用来配置.控制响应外设的工作方式,如GPIOx_CRL.AFIO_EXTICR1 ...
- 自定义modal一个控制器的效果, presentViewController
presentViewController 一.主要用途 弹出模态ViewController是IOS变成中很有用的一个技术,UIKit提供的一些专门用于模态显示的ViewController,如UI ...
- Android之TextView组件学习
一.基础学习 1.findViewById返回View类,该类是所有View组件的父类. 2.子类比父类拥有更多的属性和方法,不过子类找不到的话去父类找 3.marquee:华盖,跑马灯效果:orie ...
- RTP
RTP学习(三)RTP/RTCP/RTSP数据包格式 h264RTP打包描述的较为详细(含SDP中sps等信息的描述) UDP.TCP.RTP三种协议的总结 http://super-and-star ...