java常用MVC框架说明与对比

开发中各个层次的框架

  • 用户接口层:Struts、JSF、WebWork、SpringMVC、Jquery、Extjs
  • 业务层:Spring
  • 数据库持久层:Hibernate、Mybatis

MVC

  • 模型封装了应用程序数据,并且通常它们由 POJO 组成。javaBean组件等价于 域模型层 + 业务逻辑层 + 持久层
  • 视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
  • 控制器主要用于处理用户请求,委托给模型进行处理(状态改变),并且构建合适的模型并将其传递到视图呈现。

Struts1与Struts2

Struts1

struts1流程图:
Struts1流程图

struts1活动图:
Struts1活动图

  • ActionServlet是核心控制器,URL 地址映射、ActionForm的匹配、 Action 的执行都需要这个类来进行导航
  • ActionMapping:它们可将请求URI映射到Action类,并且将Action类与ActionForm相关联
  • ActionForm:封装浏览器传输过来的参数
  • Action: 完成所需的业务逻辑,最终确定要跳转的页面
  • ActionForward:用来封装转发路径的

Struts2

Struts2 是一个MVC拉动的(或MVC2)框架,Struts2 的模型-视图-控制器模式是通过以下五个核心部分进行实现的:

  • 操作(Actions)
  • 拦截器(Interceptors)
  • 值栈(Value Stack)/OGNL
  • 结果(Result)/结果类型
  • 视图技术

而Struts2 与传统的MVC框架略有不同,因为它由Action扮演模型的角色,而不是控制器

Struts2架构图

控制器是通过Struts2 分派servlet过滤器以及拦截器进行实现,模型是通过Actions进行实现,而视图则是结果类型和结果的结合。值栈和OGNL提供共同的路线、链接以及与其他组件之间的集成。
除了上述部分,还有许多组件相关的信息。web应用程序组件、Actions组件、拦截器组件、结果组件等等。

struts2不是struts1的升级,而是继承的webwork的血统,它吸收了struts1和webwork的优势。

Struts1和Struts2的区别和对比:

  1. Servlet依赖性由于Action在被调用的时候,HttpServletRequest和HttpServletResponse被传递到execute()方法,struts1的Action对Servlet API有依赖 性,但如果在struts2中,Action就不会对容器有依赖性了,因为struts2的Action是由简单的POJO组成,在struts2中,Servlet上下文以简单的Map的形式表现出来,这使得Action可以得到独立的测试,如果需要,struts2也可以访问原始的请求与响应。
  2. Action类struts1要求action类继承一个基类,struts2 Action要求继承ActionSupport基类
  3. 验证struts1和struts2都支持通过validate方法的手动验证,struts1使用ActionForm中的validate方法,而struts2支持通过Validate方法和Xwork校验框架的手动验证
  4. 线程struts1是单例模式的并且必须是线程安全的,因为仅有一个Action的实例来处理所有请求,而struts2为每一个请求产生一个实例
  5. 易测性struts1一个主要问题是execute方法暴露了Servlet API,一个叫Struts TestCase的第三方扩展,提供了一个struts1测试用的模拟对象,但是struts2中,Action可以经由创建Action实例,设置属性,和调用方法来得到测试
  6. 获取输出struts1使用ActionForm来捕获输入,而所有的ActionForm需要继承一个框架依赖的基类,由于javabean不能当作ActionForm来用,开发人员不得不创建冗繁的类来获取出入,不过struts2用Action属性,这避免了需要创建第二个输入对象
  7. 表达式语音struts1与JSTL整合,struts2不仅支持jstl 还支持OGNL
  8. 将绑定值到视图中在视图层,struts1使用标准的JSP来绑定对象到页面上下文来访问,然而struts2使用一种叫值栈的技术,这使得标签可以访问值而不需将视图与正在呈递的对象类型连接起来,值栈允许重用一些属性名相同但类型不同的视图类型
  9. 类型转换通常struts1的ActionForm属性都是string类型的,struts1使用Commons-Beanutils进行类型转换,这些针对每一个类的类型转换无法为每一个实例配置,然而struts2使用OGNL来进行类型转换,框架包含了针对基础类型,常见对象类型与原始类型的转换器
  10. Action执行控制struts1支持每一个模块的请求处理器的分离,但是同一模块下的所有Action必须共享相同的生命周期,struts2支持通过拦截器栈为每一个Action创建不同的生命周期,自定义栈可以视需要对不同的Action使用

Spring MVC

Struts2架构图

Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应。
对于不同的表示技术,Spring MVC 支持许多类型的视图。这些包括 JSP、HTML、PDF、Excel 工作表、XML、Velocity 模板、XSLT、JSON、Atom 和 RSS 提要、JasperReports 等等

  • 收到一个 HTTP 请求后,DispatcherServlet 根据 HandlerMapping 来选择并且调用适当的控制器。
  • 控制器接受请求,并基于使用的 GET 或 POST 方法来调用适当的 service 方法。Service 方法将设置基于定义的业务逻辑的模型数据,并返回视图名称到 DispatcherServlet 中。
  • DispatcherServlet 会从 ViewResolver 获取帮助,为请求检取定义视图。
  • 一旦确定视图,DispatcherServlet 将把模型数据传递给视图,最后呈现在浏览器中。

DispatcherServlet核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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;
Object dispatchException = null;

try {
// checkMultipart 文件上传
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// HandlerMapping映射
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}

// 处理器包装成相应的适配器
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}

if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}

// 拦截器的预处理
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 适配器执行处理器
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}

this.applyDefaultViewName(processedRequest, mv);
// 拦截器的后处理
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}

// ModelAndView处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}

}

}