一. 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
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
- 管理当前Controller所支持的请求方法类型,即 HttpMethod.OPTIONS
- 管理页面的缓存 设置,是否允许浏览器缓存当前页面
- 管理执行流程在SEssion上的同步
实现AbstractController的类,只需要实现 handlerRequestInternal用来实现具体的请求处理逻辑即可。
4.3. ModelAndView
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 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
渲染工作通过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