【SpringMVC框架学习】简介

一.  MVC框架简介

  • Model(模型):表示领域信息的对象,包含除了用于UI部分的所有数据和行为。在纯面向对象的概念里,模型师领域模型的一个对象。
  • View(视图):表示UI中模型的显示。因此,视图可能是一个html页面UI小部件,通过渲染模型提供的信息得到界面的信息。视图仅用来展示信息,任何信息的更新有MVC中的Controller来处理。
  • Controller(控制器):接收用户输入,操作模型,并使视图更新。从这个角度来说,UI是视图和控制器的组合。

  • 这里有两个主要的分离:模型中分离表现,和视图中分离控制器。


1. 模型与表现分离

这是软件设计方案中最基本的启发式方法,分离的原因包括:

  • 从根本上讲,表现和模型的关注点不同当你设计一个视图,你考虑得是UI的机制以及如何提供一个好的用户界面。当你设计模型的时候,你考虑的是业务逻辑,可能是数据库交互等。通常来说,人们倾向于每个领域都是更专注于一点的
  • 根据上下文,对于同样的基本模型信息,人们喜欢从不同的角度看待。表现和视图的分离允许你开发多个表现,而模型的代码是相同的。
  • 不可见对象通常比可见对象容易测试。把表现和模型分离,可以轻松测试所有的领域逻辑,设计不同的测试用例。

这种分离关键是,表现依赖模型,但是模型不以来表现

但这个分离有一个常见问题,如果一个模型的多个视图同时展现在一个屏幕上时,一旦用户通过某个视图改变模型,其他视图需要相应的更新。在不建立依赖的情况下,就需要一个观察者来达到这个需求。例如,视图充当模型的观察者,一旦发生改变,模型发送一个时间,表现层随之更新。

2.视图与控制器分离

虽然一个视图可以有两个控制器策略,但实践中,大部分系统每个视图只有一个控制器,因此这种分离尝尝没有做到。直到之后Web界面上分离控制器和视图变得有用,才又重新开始流行(后面解释)。

二、Page Controller

  • 大部分人基本的web经验是使用静态html页面。当你请求一个静态html页面的时候,你传给web服务html文件的名字和路径,网站上的每个页面在服务器上都是一份单独的文档,很方便对应。
  • 当使用动态页面时,路径名和文件之间的对应关系就会变得复杂。
  • 页面控制器(Page Controller)用来解决路径名和文件之间复杂的对应关系。Page Controller 在Web站点上为每个页面都准备了一个输入控制器,这个控制器可能就是页面本身,也可能是对应这个页面的独立对象。

Page Controller 为web网站上所有页面都在Web服务器上准备了一个模块充当控制器的角色。实际上,不是每个页面对应一个模块,因为当你点击一个页面时,可能根据不同的动态信息,得到不同的页面。页面控制器的基本职责:

  • URL解码并获取必要的数据,以便为下一步动作计算出需要的信息
  • 创建调用模型对象处理数据,所有从HTML请求中得到的相应数据都被送到模型中,这样模型对象就不需要和HTML请求有任何关联
  • 决定那个视图用来显示结果页面,并把模型的相关信息传递给视图

三、 Front Controller

  • 在一个复杂的网站中,每个请求都可能有很多相似的操作需要做。包括 安全检查、国际化操作、提供给特定的用户某个特定的视图等等。如果输入控制器的行为分散到多个对象上,那么这些行为很可能存在重复拷贝。并且,很难进行统一的更新。
  • Front Controller让所有的请求都经过一个handler对象来处理所有的请求。这个对象能够处理公共的行为,可以在运行时通过装饰类进行自定义。handler处理完之后,不同的请求分配到不同的command对象来处理某一请求的特定行为。


运行机制

一个Front Controller处理网站的所有调用,通常分为两个部分:一个web handler和一个command 层次结构。web handler处理所有的post/get请求,从url和request中获取足够的信息来决定下一步动作是 什么,之后分配一个command来执行这个动作。

对于前端控制器来说,一个非常有用的模式是 Intercepting Filter(拦截器)。它允许你建立一条筛选器链去处理认证、登陆等问题。

4. SpringMVC中的角色

  • SpringMVC结合Front Controller模式以及Page Controller模式,将原来单一的Servlet作为整个应用的 Front controller,该Servlet接收到具体的Web请求之后,按照可配置的映射信息,将待处理的web请求转发给次级控制器处理:(sub-controller即 org.springframework.web.servlet.mvc.Controller)

  • DispatcherServlet:HttpServlet接口的实现类,SpringMVC框架中的 Front Controller角色;负责接收并处理所有的web请求,但具体的处理逻辑,委派给次级控制器实现,即 org.springframework.web.servlet.mvc.Controller(本节之后简称Controller);
  • HandlerMapping:管理Web请求到具体的处理类(Controller)之间的映射关系;
  • Controller:实现对某个具体的Web请求的处理逻辑;处理方法执行完成之后,返回一个 org.springframework.web.servlet.ModelAndview实例;
  • ModelAndView:包含两部分信息,一是视图逻辑名称,DispatcherServlet根据视图逻辑名称决定使用哪个视图;二是模型数据,用以将模型数据并入视图渲染显示;
  • ViewResolver:org.springframework.web.servlet.ViewResolver根据ModelAndView中的逻辑视图名查找对的View实现类,反馈给DispatcherServlet;
  • View:使用ModelAndView的模型数据处理最终的视图渲染工作;

4.1. HandlerMapping

public interface HandlerMapping {
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

AbstractHandlerMapping是HandlerMapping的抽象实现,所有的HandlerMapping都继承自AbstractHandlerMapping。

AbstractHandlerMapping提供了一个抽象接口 getHandlerInternal用于继承类实现。

AbstractHandlerMapping根据request查找Handler和Interceptors构造执行链(HandlerExecutionChain)。在获取Handler之后会自己根据从request中提取的信息将匹配的Interceptors装配到执行链上。

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
 
    protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
 
    /**
     * Look up a handler for the given request, falling back to the default
     * handler if no specific one is found.
     * @param request current HTTP request
     * @return the corresponding handler instance, or the default handler
     * @see #getHandlerInternal
     */
    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = getApplicationContext().getBean(handlerName);
        }
 
        // 构造一个执行链,并获取interceptor
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
        ... ...
        return executionChain;
    }
     
 
    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
                (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
 
        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            ... ...
            chain.addInterceptor(interceptor);
        }
        return chain;
    }
    ... ...
}


在Spring 2.5开始,SpringMVC引入了基于注解的配置方式,通过DefaultAnnotationHandlerMapping实现注解RequestMapping和Controller之间的关联。Spring 3.2开始,改用RequestMappingHandlerMapping。

@RequestMapping("/hello")
public class HelloController {
    ... ...
}

这里标注说明 HelloController用于URl开头为 /hello 的所有请求。

4.2. Controller

public interface Controller {
    ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
public abstract class AbstractController extends WebContentGenerator implements Controller {
 
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        if (HttpMethod.OPTIONS.matches(request.getMethod())) {
            ... ...
        }
        checkRequest(request);
        prepareResponse(response);
        ... ...
        return handleRequestInternal(request, response);
    }
 
    protected abstract ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
            throws Exception;
}

AbstractController是整个Controller继承层次的起源,通过模板方式解决通用问题:
  • 管理当前Controller所支持的请求方法类型,即 HttpMethod.OPTIONS
  • 管理页面的缓存 设置,是否允许浏览器缓存当前页面
  • 管理执行流程在SEssion上的同步

实现AbstractController的类,只需要实现 handlerRequestInternal用来实现具体的请求处理逻辑即可。

 

4.3. ModelAndView

public class ModelAndView {
 
    /** View instance or view name String */
    private Object view;
 
    /** Model Map */
    private ModelMap model;
 
    /** Optional HTTP status for the response */
    private HttpStatus status;
 
    /** Indicates whether or not this instance has been cleared with a call to {@link #clear()} */
    private boolean cleared = false;
}

ModelAndView中如果返回了具体的view示例,那么DispatcherServlet会直接使用该实例并渲染视图,否则会使用 ViewResolver,根据ModelAndView的逻辑视图名获取View示例:

public class DispatcherServlet {
    protected void render(ModelandView vm, HttpServletRequest requet, HttpServletResponse response) {
        View view;
        if (mv.isReference()) {
          view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
          ... ...
        } else {
          view = vm.getView();
          ... ...
        }
        view.render(mv.getModelInternal(), request, response);
        ... ...
    }
}
 
public class DispatcherServlet {
    protected View resolveViewName(...) {
        ... ...
        View view = viewResolver.resolveViewName(viewName, locale);
        return view;
    }
}

 

4.4. ViewResolver

public interface ViewResolver {
    View resolveViewName(String viewName, Locale locale) throws Exception;
}

最常见的是使用 BeanNameViewResolver实现。因为针对每次请求都要重新实例化view可能对web程序带来性能的损失,所以SpringMVC在 AbstractCachingViewResolver上加入的View实例的缓存功能。

public abstract class AbstractCachingViewResolver extends WebApplicationObjectSupport implements ViewResolver {
 
    public static final int DEFAULT_CACHE_LIMIT = 1024;
    private volatile int cacheLimit = DEFAULT_CACHE_LIMIT;
    private final Map<Object, View> viewAccessCache = new ConcurrentHashMap<Object, View>(DEFAULT_CACHE_LIMIT);
     
    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {
        if (!isCache()) {
            return createView(viewName, locale);
        }
        else {
            Object cacheKey = getCacheKey(viewName, locale);
            View view = this.viewAccessCache.get(cacheKey);
            if (view == null) {
                synchronized (this.viewCreationCache) {
                    view = this.viewCreationCache.get(cacheKey);
                    if (view == null) {
                        view = createView(viewName, locale);
                        if (view == null && this.cacheUnresolved) {
                            view = UNRESOLVED_VIEW;
                        }
                        if (view != null) {
                            this.viewAccessCache.put(cacheKey, view);
                            this.viewCreationCache.put(cacheKey, view);
                            if (logger.isTraceEnabled()) {
                                logger.trace("Cached view [" + cacheKey + "]");
                            }
                        }
                    }
                }
            }
            return (view != UNRESOLVED_VIEW ? view : null);
        }
    }
}

 

4.5. View

 

public interface View {
    String getContentType();
    void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

View是将SpringMVC中原本可能存在与DispatcherServlet中的视图渲染逻辑玻璃出来的关键组件。通过View,我们可以灵活的支持各种视图渲染。

渲染工作通过render实现,但对 DispatcherServlet来说是透明的,DispatcherServlet只是直接接触viewResolver返回的view实例,将相应的渲染工作交给view实例。

所有的View实现类,都直接或间接的继承自AbstractView。

public abstract class AbstractView extends WebApplicationObjectSupport implements View, BeanNameAware {
    ... ...
    @Override
    public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (logger.isTraceEnabled()) {
            logger.trace("Rendering view with name '" + this.beanName + "' with model " + model +
                " and static attributes " + this.staticAttributes);
        }
 
        // 将添加的静态属性导入到现有的模型数据Map中, 如果设置了requestContextAttribute,也导入模型数据Map中
        Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
        prepareResponse(request, response);
        // 需要继承类实现该方法
        renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
    }
     
    protected abstract void renderMergedOutputModel(
            Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}

5. 相关资料

模式资料:《Patterns of Enterprise Application Architecture》

SpringMVC:SpringFramework


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值