spring源码分析之MVC简介
来源:互联网 发布:mac死机怎么办 编辑:程序博客网 时间:2024/06/10 18:35
简介
springMVC遵循OCP(Open-Closed Principle,开放封闭原则)而实现MVC,平时项目要做某部分特殊功能时,只要实现相应的接口就可以了,非常灵活也易于扩展,MVC模型在JEE平台中属于描述层,JEE将MVC细分了更多的模式,后面有时间了再分析JEE模式在spring中如何实现的,这里做个大概的介绍。spring MVC模型如下:
spring通过DispatcherServlet(Front Controller,前端控制器模式)将各个层分离,各司其职,模块之间更易于扩展。为什么要用Front Controller?这个控制器就主要绑定映射、视图解析、异常处理、请求调用等待,如果没有这层,经常会有一个功能的增删改查,每个控制层就要去写一次增删改查的判断,现在servlet3以上版本支持注解了解决了这个问题,但是每个方法里面还是有视图层耦合,前端控制器模式就很好的解决了这些问题。
参考《spring源码分析之Ioc容器分析》,整个MVC相关的bean控制由XmlWebApplicationContext,一般项目里面对bean的配置有两种,参考下图:
这种配置是将web Ioc容器与应用的父级容器分离,这种方式如果没配置好,有时候会遇到一些问题,如事务失效、aop不起作用等等,大多数都是bean不在相应的IoC容器中造成的。
这种方式就是将web 中的bean和父级bean融合在一个容器中了。
前端控制器DispatcherServlet各个组件
DispatcherServlet在前面说了,作为web应用的入口点,控制着整个请求流程,大概包含了如下组件:
DispatcherServlet初始化以及分发请求的大致流程如下:
HandlerMapping
如上图所示,在DispatcherServlet初始化的时候,做了很多初始化工作,这就包含请求地址与应用层的Controller绑定了,HandlerMapping的类图如下:
可以看到具体的实现类实现了InitializingBean接口,在初始化的时候就要执行afterPropertiesSet,这个方法就是绑定映射了,以RequestMappingHandlerMapping为例,执行流程如下:
HandlerAdapter
这层主要将请求处理完(包含请求数据绑定)之后的结果返回给DispatcherServlet,再由视图解析器根据结果进行视图渲染。
在DispatcherServlet分发请求的时候会执行doDispatch方法:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 。。。 // 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; } } //执行链执行拦截器HandlerInterceptor.preHandle if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. //这时HandlerExecutionChain的handler对象为url映射的HandlerMethod mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); //请求执行完后拦截器再处理 mappedHandler.applyPostHandle(processedRequest, response, mv); 。。。}
以RequestMappingHandlerAdapter为例,执行流程如下:
最后参数绑定就在DataBinder类执行了,如下所示:
protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit()); if (this.conversionService != null) { result.initConversion(this.conversionService); } return result;}
参数转换的时候也用的BeanWrapper来封装的,绑定完参数就执行方法了,因为容器启动的时候就把相关的url绑定到具体的方法了,所以这里利用反射直接调用方法执行了,参考InvocableHandlerMethod:
public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //获取解析类处理后的方法参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { StringBuilder sb = new StringBuilder("Invoking ["); sb.append(getBeanType().getSimpleName()).append("."); sb.append(getMethod().getName()).append("] method with arguments "); sb.append(Arrays.asList(args)); logger.trace(sb.toString()); } //执行requestMapping绑定的方法 Object returnValue = doInvoke(args); if (logger.isTraceEnabled()) { logger.trace("Method [" + getMethod().getName() + "] returned [" + returnValue + "]"); } return returnValue;}
然后在ServletInvocableHandlerMethod把返回的结果用returnValueHandler把视图以及Model的设置到ModelAndViewContainer。最后在RequestMappingHandlerAdapter生成ModelAndView对象:
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer, ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception { modelFactory.updateModel(webRequest, mavContainer); if (mavContainer.isRequestHandled()) { return null; } ModelMap model = mavContainer.getModel(); ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model); if (!mavContainer.isViewReference()) { mav.setView((View) mavContainer.getView()); } if (model instanceof RedirectAttributes) { Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes(); HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes); } return mav;}
Resolving views
视图解析有两个重要的接口ViewResolver(解析指定标示的视图)和View(把视图结果写到HttpServletResponse返回给客户端)。
又回到DispatcherServlet,把返回的ModelAndView交给processDispatchResult处理,里面的解析器在配置文件加载的时候就初始化好了,如果需要解析自定义的视图,只要实现ViewResolver即可。spring内部也提供了大量的视图解析器,如下图:
解析完文件就会生成相应的视图,spring内部的视图如下:
jsp解析器对应的视图就为InternalResourceView,渲染结果如下:
protected void renderMergedOutputModel( Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception { // Expose the model object as request attributes. exposeModelAsRequestAttributes(model, request); // Expose helpers as request attributes, if any. exposeHelpers(request); // Determine the path for the request dispatcher. String dispatcherPath = prepareForRendering(request, response); // Obtain a RequestDispatcher for the target resource (typically a JSP). RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath); if (rd == null) { throw new ServletException("Could not get RequestDispatcher for [" + getUrl() + "]: Check that the corresponding file exists within your web application archive!"); } // If already included or response already committed, perform include, else forward. if (useInclude(request, response)) { response.setContentType(getContentType()); if (logger.isDebugEnabled()) { logger.debug("Including resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.include(request, response); } else { // Note: The forwarded resource is supposed to determine the content type itself. if (logger.isDebugEnabled()) { logger.debug("Forwarding to resource [" + getUrl() + "] in InternalResourceView '" + getBeanName() + "'"); } rd.forward(request, response); }}
如果是ajax异步请求,在处理返回结果的时候就已经处理了,ModelAndViewContainer设置属性requestHandled为true,表示已经处理了,spring3以上支持很多注解,异步返回使用@ResponseBody注解,就使用RequestResponseBodyMethodProcessor处理返回结果。spring4.2以上默认使用jackson和gson解析json格式的返回类型,参考AnnotationDrivenBeanDefinitionParser:
private Properties getDefaultMediaTypes() { Properties props = new Properties(); if (romePresent) { props.put("atom", MediaType.APPLICATION_ATOM_XML_VALUE); props.put("rss", "application/rss+xml"); } if (jaxb2Present || jackson2XmlPresent) { props.put("xml", MediaType.APPLICATION_XML_VALUE); } if (jackson2Present || gsonPresent) { props.put("json", MediaType.APPLICATION_JSON_VALUE); } return props; }
springMVC的大致流程就分析完了,从以上分析可以看出,spring严格按照OCP、SRP(单一责任原则)实现,各个模块各司其职,降低了耦合度,扩展也很方便。有些涉及到具体细节没有详细分析,后面针对具体复杂点的业务再详细说明。
---------------------------------------------------------------------------------------------- - - - - - - - - - -- - -- - - - - -- -转载于http://zeng233.github.io/2016/10/20/19spring%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%E4%B9%8BMVC%E5%88%86%E6%9E%90/
- spring源码分析之MVC简介
- Spring MVC源码分析
- Spring MVC源码分析
- Spring MVC源码分析
- Spring MVC源码分析
- Spring MVC源码解析简介
- spring mvc 3.2源码分析
- Spring MVC源码分析—Tomcat分析
- 【Spring MVC】Spring MVC启动过程源码分析
- 【Spring mvc】Spring MVC源码分析——初始化过程
- Spring MVC之ModelAndView分析
- spring mvc整合velocity源码分析
- Spring MVC初始化部分源码分析
- spring mvc源码分析(整个流程)
- Spring MVC 执行流程和源码分析
- Spring MVC源码分析—Servlet解析
- Spring---Spring MVC 简介
- Spring mvc 源码解析之初始化
- websocket -- 备面试装逼使用
- 【Dubbo二】Zookeeper安装与配置
- C# File.Copy 工作记录
- (POJ1129)Channel Allocation <涂色问题问最少颜色数 剪枝搜索 > || <四色定律>
- Oracle数据库学习12之函数
- spring源码分析之MVC简介
- 最小割基本模型及解决方案
- iOS 很酷的动画效果
- 4518: [Sdoi2016]征途
- android gradle学习的新知识 笔记一下
- 纯备忘 caffe依赖包安装
- Android 简单实现显示密码效果
- AS 多渠道打包 generateADebugSources
- iOS - PhotoKit框架使用说明(转)