常用MVC框架
J2EE开常用的SSH或SSI框架,对应解决表示层、业务逻辑层、持久化层的问题,其中对表示层的解决方案最多,常见的有Struts1/2,Spring MVC等,实际上都是在最底层的Servlet规范中发展而来的。而不同MVC框架其M(对应JAVA的POJO类)和V(如JSP,HTML等)一般是相同的,最主要的区别在C,它是HTTP处理请求、响应的关键。
我们可以看到,Servlet的基本接口定义中:
参数列表 —— Http请求被封装为一个HttpServletRequest对象(或者ServletRequest对象),而Http响应封装为一个HttpServletResponse对象(或者ServletResponse对象)
返回值 —— 方法不存在返回值(返回值为void)
在这个设计中,HttpServletRequest和HttpServletResponse承担了完整的处理Http请求的任务。而这两个Servlet对象的职责也有所分工:
HttpServletRequest对象 —— 主要用于处理整个Http生命周期中的数据。
HttpServletResponse对象 —— 主要用于处理Http的响应结果。
这里实际上有一点“数据与行为分离”的意味。也就是说,在Servlet处理请求的过程中,其实也是Servlet中响应方法内部的逻辑执行过程中,如果需要处理请求数据或者返回数据,那么我们需要和HttpServletRequest打交道;如果需要处理执行完毕之后的响应结果,那么我们需要和HttpServletResponse打交道。 这样的设计方式,是一种骨架式的设计方式。因为Servlet是我们进行Web开发中最底层的标准,所以我们可以看到接口设计中的返回值对于一个最底层标准而言毫无意义。因为不存在一个更底层的处理程序会对返回值进行进一步的处理,我们不得不在Servlet的过程中自行处理浏览器的行为控制。
【Struts1.X】
Struts1.X是一个较为早期的MVC框架实现,它的历史最早可以追溯到2000年,作为Apache开源组织的一个重要项目,取名为“Struts”,有“基础构建”的含义。在那个程序框架尚处于朦胧阶段的年代,“基础构建”无疑是每个程序员梦寐以求的东西。
对于Struts1.X,我们还是把关注的重点放在Struts中的Controller层的定义上:
如果和之前的Servlet模型加以比较我们就可以发现,Struts1.X对于基本的Servlet模型做了一定的扩展和重构:
由于Struts1.X已经不再是一个底层的实现规范,于是响应方法“返回值”被框架引入,加入到了整个处理过程之中。我们可以看到,在这里最大的进步之处就在于:引入了新的编程元素,从而优化整个逻辑处理过程。编程元素的引入非常重要,因为对于一个任何一个程序员而言,充分调用所有可以利用的编程要素是衡量一个程序写得好坏的重要标准。之后,我们还可以看到其他的框架在引入编程元素这个方面所做的努力。
【Webwork2 / Struts2】
Apache社区与Opensymphony开源组织在2005年底宣布未来的Struts项目将与Webwork2项目合并,并联合推出Struts2,通过Apache社区的人气优势与OpenSymphony的技术优势,共同打造下一代的Web层开发框架。这也就是Struts2的由来。 从整个过程中,我们可以发现,Webwork2和Struts2是一脉相承的Web层解决方案。优势突出表现为对Controller的彻底改造:
请求映射的配置:
从上面的代码中,我们可以看到Webwork2 / Struts2对于Controller最大的改造有两点:
当然,这种改造的前提条件在于Webwork2 / Struts2引入了另外一个重要的编程概念:ThreadLocal模式。使得Controller成为一个线程安全的对象被Servlet模型所调用,这也就突破了传统Servlet体系下,Servlet对象并非一个线程安全的对象的限制条件。
从引入新的编程元素的角度来说,Webwork2 / Struts2无疑也是成功的。因为在传统Servlet模式中的禁地Controller中的属性变量被合理利用了起来作为请求处理过程中的数据部分。这样的改造不仅使得表达式引擎能够得到最大限度的发挥,同时使得整个Controller看起来更像是一个POJO。因而,这种表现形态被笔者冠以的名称是:POJO实现模式。
POJO实现模式是一种具有革命性意义的模式,因为它能够把解耦合这样一个观点发挥到极致。从面向对象的角度来看,POJO模式无疑也是所有程序员所追求的一个目标。这也就是Webwork2 / Struts2那么多年来经久不衰的一个重要原因。
【SpringMVC】
相比较Webwork2 / Struts2,SpringMVC走了一条比较温和的改良路线。因为SpringMVC自始至终都没有突破传统Servlet编程模型的限制,而是在这过程中不断改良,不断重构,反而在发展中开拓了一条崭新的道路。
我们可以看看目前最新版本的SpringMVC中对于Controller的定义:
我们在这里引用了在之前的讲解中曾经使用过的代码片段。不过这一代码片段刚刚好可以说明SpringMVC在整个Controller改造中所涉及到的一些要点:
1. 使用参数-返回值(Param-Return)实现模式来打造Controller
方法的参数(email和password)被视作是Http请求参数的概括。而在这里,它们已经被SpringMVC的框架有效处理并屏蔽了内在的处理细节,呈现出来的是与请求参数名称一一对应的参数列表。而返回值ModelAndView则表示Http的响应是一个数据与视图的结合体,表示Http的处理结果。
2. 引入Annotation来完成请求-响应的映射关系
引入Annotation来完成请求-响应的映射关系,是SpringMVC的一个重大改造。在早期的SpringMVC以及其他的MVC框架中,通常都是使用XML作为基础配置的。而Annotation的引入将原本分散的关注点合并到了一起,为实现配置简化打下了坚实的基础。
3. 泛化参数和返回值的含义
这是一个蕴含的特点。事实上,SpringMVC在响应方法上,可以支持多种多样不同的参数类型和返回值类型。例如,当参数类型为Model时,SpringMVC将会自动将请求参数封装于Model内部而传入请求方法;当返回值类型是String时,直接表示SpringMVC需要返回的视图类型和视图内容。当然,这些泛化的参数和返回值的内容全部都由SpringMVC在框架内部处理了。
SpringMVC虽然是一个温和的改良派,却是在改良这个领域做得最为出色的。以引入Annotation为例,引入Annotation来完成请求-响应映射,不正是我们反复强调的引入并合理使用新的编程元素来完成处理任务嘛?而泛化后的参数和返回值,则可以让程序员在写Controller的代码时可以随心所欲,不再受到任何契约的束缚,这样一来接口的逻辑语义也就能够更加清晰。
MVC模型的发展轨迹
目前,Struts1.X这一条路被证明已经穷途末路;另外的两条发展轨迹总体来说实力相当,SpringMVC大有赶超之势。
那么,为什么曾经一度占领了大部分市场的Struts2会在近一段时间内被SpringMVC大幅赶超呢?这里面的原因多种多样,有自身架构上的原因,有设计理念上的原因,但是笔者认为,其本质原因还是在于Struts2对于技术革新的力度远不及SpringMVC,后者试用更简单,开发、运行速度更快。
如果我们回顾一下Struts2过去能够独占鳌头的原因就可以发现,Struts2的领先在于编程模型上的领先。其引入的POJO模型几乎是一个杀手级的武器。而基于这一模型上的拦截器、OGNL等技术的支持使得其他的编程模型在短时间很难超越它。 但是随着时代的发展,Struts2在技术革新上的作为似乎步子就迈得比较小。我们可以看到,在JDK1.5普及之后,Annotation作为一种新兴的Java语法,逐渐被大家熟知和应用。这一点上SpringMVC紧跟了时代的潮流,直接用于请求-响应的映射。而Struts2却迟迟无法在单一配置源的问题上形成突破。
【Servlet规范】
在Servlet规范中所定义的请求处理响应接口是这样的:
我们可以看到,Servlet的基本接口定义中:
参数列表 —— Http请求被封装为一个HttpServletRequest对象(或者ServletRequest对象),而Http响应封装为一个HttpServletResponse对象(或者ServletResponse对象)
返回值 —— 方法不存在返回值(返回值为void)
在这个设计中,HttpServletRequest和HttpServletResponse承担了完整的处理Http请求的任务。而这两个Servlet对象的职责也有所分工:
HttpServletRequest对象 —— 主要用于处理整个Http生命周期中的数据。
HttpServletResponse对象 —— 主要用于处理Http的响应结果。
这里实际上有一点“数据与行为分离”的意味。也就是说,在Servlet处理请求的过程中,其实也是Servlet中响应方法内部的逻辑执行过程中,如果需要处理请求数据或者返回数据,那么我们需要和HttpServletRequest打交道;如果需要处理执行完毕之后的响应结果,那么我们需要和HttpServletResponse打交道。 这样的设计方式,是一种骨架式的设计方式。因为Servlet是我们进行Web开发中最底层的标准,所以我们可以看到接口设计中的返回值对于一个最底层标准而言毫无意义。因为不存在一个更底层的处理程序会对返回值进行进一步的处理,我们不得不在Servlet的过程中自行处理浏览器的行为控制。
Struts1.X是一个较为早期的MVC框架实现,它的历史最早可以追溯到2000年,作为Apache开源组织的一个重要项目,取名为“Struts”,有“基础构建”的含义。在那个程序框架尚处于朦胧阶段的年代,“基础构建”无疑是每个程序员梦寐以求的东西。
对于Struts1.X,我们还是把关注的重点放在Struts中的Controller层的定义上:
- public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response);
如果和之前的Servlet模型加以比较我们就可以发现,Struts1.X对于基本的Servlet模型做了一定的扩展和重构:
- 保留了HttpServletRequest和HttpServletResponse这两大接口作为参数
- 将返回值改为ActionForward,并由Struts1.X框架通过处理ActionForward完成对响应结果的处理
- 增加了ActionMapping和ActionForm两大参数,前者表示Http请求的一个简要概括,后者表示一个数据模型,用以承载整个请求生命周期中的数据
由于Struts1.X已经不再是一个底层的实现规范,于是响应方法“返回值”被框架引入,加入到了整个处理过程之中。我们可以看到,在这里最大的进步之处就在于:引入了新的编程元素,从而优化整个逻辑处理过程。编程元素的引入非常重要,因为对于一个任何一个程序员而言,充分调用所有可以利用的编程要素是衡量一个程序写得好坏的重要标准。之后,我们还可以看到其他的框架在引入编程元素这个方面所做的努力。
【Webwork2 / Struts2】
Apache社区与Opensymphony开源组织在2005年底宣布未来的Struts项目将与Webwork2项目合并,并联合推出Struts2,通过Apache社区的人气优势与OpenSymphony的技术优势,共同打造下一代的Web层开发框架。这也就是Struts2的由来。 从整个过程中,我们可以发现,Webwork2和Struts2是一脉相承的Web层解决方案。优势突出表现为对Controller的彻底改造:
- public class UserController {
- private User user
- public String execute() {
- // 这里加入业务逻辑代码
- return "success";
- }
- // 这里省略了setter和getter方法
- }
- <action name="register" class="com.demo2do.sandbox.web.UserController" method="register">
- <result name="success">/register-success.jsp</result>
- </action>
- 在Controller中彻底杜绝引入HttpServletRequest或者HttpServletResponse这样的原生Servlet对象。
- 将请求参数和响应数据都从响应方法中剥离到了Controller中的属性变量。
当然,这种改造的前提条件在于Webwork2 / Struts2引入了另外一个重要的编程概念:ThreadLocal模式。使得Controller成为一个线程安全的对象被Servlet模型所调用,这也就突破了传统Servlet体系下,Servlet对象并非一个线程安全的对象的限制条件。
从引入新的编程元素的角度来说,Webwork2 / Struts2无疑也是成功的。因为在传统Servlet模式中的禁地Controller中的属性变量被合理利用了起来作为请求处理过程中的数据部分。这样的改造不仅使得表达式引擎能够得到最大限度的发挥,同时使得整个Controller看起来更像是一个POJO。因而,这种表现形态被笔者冠以的名称是:POJO实现模式。
POJO实现模式是一种具有革命性意义的模式,因为它能够把解耦合这样一个观点发挥到极致。从面向对象的角度来看,POJO模式无疑也是所有程序员所追求的一个目标。这也就是Webwork2 / Struts2那么多年来经久不衰的一个重要原因。
【SpringMVC】
相比较Webwork2 / Struts2,SpringMVC走了一条比较温和的改良路线。因为SpringMVC自始至终都没有突破传统Servlet编程模型的限制,而是在这过程中不断改良,不断重构,反而在发展中开拓了一条崭新的道路。
我们可以看看目前最新版本的SpringMVC中对于Controller的定义:
- @Controller
- @RequestMapping
- public class UserController {
- @RequestMapping("/register")
- public ModelAndView register(String email, String password) {
- // 在这里调用具体的业务逻辑代码
- return new ModelAndView("register-success");
- }
- }
1. 使用参数-返回值(Param-Return)实现模式来打造Controller
方法的参数(email和password)被视作是Http请求参数的概括。而在这里,它们已经被SpringMVC的框架有效处理并屏蔽了内在的处理细节,呈现出来的是与请求参数名称一一对应的参数列表。而返回值ModelAndView则表示Http的响应是一个数据与视图的结合体,表示Http的处理结果。
2. 引入Annotation来完成请求-响应的映射关系
引入Annotation来完成请求-响应的映射关系,是SpringMVC的一个重大改造。在早期的SpringMVC以及其他的MVC框架中,通常都是使用XML作为基础配置的。而Annotation的引入将原本分散的关注点合并到了一起,为实现配置简化打下了坚实的基础。
3. 泛化参数和返回值的含义
这是一个蕴含的特点。事实上,SpringMVC在响应方法上,可以支持多种多样不同的参数类型和返回值类型。例如,当参数类型为Model时,SpringMVC将会自动将请求参数封装于Model内部而传入请求方法;当返回值类型是String时,直接表示SpringMVC需要返回的视图类型和视图内容。当然,这些泛化的参数和返回值的内容全部都由SpringMVC在框架内部处理了。
SpringMVC虽然是一个温和的改良派,却是在改良这个领域做得最为出色的。以引入Annotation为例,引入Annotation来完成请求-响应映射,不正是我们反复强调的引入并合理使用新的编程元素来完成处理任务嘛?而泛化后的参数和返回值,则可以让程序员在写Controller的代码时可以随心所欲,不再受到任何契约的束缚,这样一来接口的逻辑语义也就能够更加清晰。
MVC模型的发展轨迹
目前,Struts1.X这一条路被证明已经穷途末路;另外的两条发展轨迹总体来说实力相当,SpringMVC大有赶超之势。
那么,为什么曾经一度占领了大部分市场的Struts2会在近一段时间内被SpringMVC大幅赶超呢?这里面的原因多种多样,有自身架构上的原因,有设计理念上的原因,但是笔者认为,其本质原因还是在于Struts2对于技术革新的力度远不及SpringMVC,后者试用更简单,开发、运行速度更快。
如果我们回顾一下Struts2过去能够独占鳌头的原因就可以发现,Struts2的领先在于编程模型上的领先。其引入的POJO模型几乎是一个杀手级的武器。而基于这一模型上的拦截器、OGNL等技术的支持使得其他的编程模型在短时间很难超越它。 但是随着时代的发展,Struts2在技术革新上的作为似乎步子就迈得比较小。我们可以看到,在JDK1.5普及之后,Annotation作为一种新兴的Java语法,逐渐被大家熟知和应用。这一点上SpringMVC紧跟了时代的潮流,直接用于请求-响应的映射。而Struts2却迟迟无法在单一配置源的问题上形成突破。