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