Strus 1存在的问题主要有两个:
(1)令人头痛的ActionForm;
(2)单元测试困难。
下面我们具体看一下Struts 1的这两个问题。
1.令人头痛的ActionForm
使用Struts 1框架开发过大型Web应用程序的开发人员说起ActionForm,都是头痛加无奈。为什么这么说呢?要理解ActionForm为什么让人头痛,就要从现代软件开发的分层结构说起。
在现代企业软件开发中,分层与解耦是两个必须要考虑的要素。经典的软件分层结构如图1-5所示。
其中,数据访问层实现对数据库操作的封装,以隔离具体业务和数据库之间的联系;而业务逻辑层则实现对业务逻辑的封装,隔离用户操作的界面和具体业务逻辑;表现层即用户界面层,提供用户操作接口。这种分层封装的好处是分化了复杂的系统,同时也提高了系统的可维护性,使得开发过程中的分工协作更加方便快捷。采用这种层次结构,上层应该只依赖于它的下层结构,而不应该跨层依赖;同时,下层不应该依赖于它的上层结构,也就是说,业务逻辑层和数据访问层绝对不要依赖于表现层。在业务逻辑层和数据访问层的代码中,不要出现调用表现层代码的情况。遵循这个原则将简化在相同的基础上替换表现层的代价,也使得表现层的修改所带来的连锁反应尽可能小。
Struts是属于表现层的技术,在Struts中,为了接收表单的数据,我们必须编写一个从Struts的ActionForm类继承的类,否则你就只能自己从HttpServletRequest中提取数据了。然而,不使用ActionForm,意味着你需要自己对表单数据做初始化,以及自己编写代码对表单数据进行验证。使用从ActionForm继承的类,如果更换Web框架,这个类也将被废弃。ActionForm中的数据往往需要传递给业务逻辑层和数据访问层进行处理,为了避免业务逻辑层和数据访问层依赖于Struts,通常我们会再编写一个和ActionForm类具有相同属性的普通JavaBean类,在ActionForm对象接收到数据后,将其中的数据原封不动地复制到JavaBean对象中,然后将这个JavaBean对象向下层传递。想一想,如果能够直接使用普通的JavaBean对象来接收表单数据该多好,既可以避免多余的数据复制步骤,又可以不依赖于Struts框架。你可能会认为额外增加的这一步骤也没有什么,考虑到程序中可能还会存在着PO(Persistent Object,持久化对象,位于数据访问层,对应着数据库表中一条记录)和JavaBean对象之间的数据复制,加上众多的表单,对象与对象之间的数据复制会让你不胜其烦。
2.单元测试困难
在现代软件开发中,测试的重要性已经毋庸置疑。Struts 1中的Action类与Servlet API耦合在一起,它的核心方法execute依赖于Servlet API中的HttpServletRequest和HttpServletResponse,其方法签名如下:
public ActionForward execute(ActionMapping mapping,
ActionForm form,
javax.servlet.http.HttpServletRequest request,
javax.servlet.http.HttpServletResponse response)
我们知道,HttpServletRequest和HttpServletResponse是由Servlet容器负责实例化的,因此Acton类的测试就要依赖于Web容器,单元测试很难实现。当然,也可以使用第三方的测试工具——JUnit的扩展工具StrutsTestCase来对Action进行单元测试。
除了上述两个问题之外,Struts 1还存在着一些其他设计上的不足,在这里,我们就不一一列举了。