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/

0 0