Ted Husted是Apache Software Foundation成员。他是Apache Struts和Apahce iBATIS的活跃成员,同样也是Apache Jakarta Commons的创始人。他有很多耳熟能详的著作:JUnit in Action, Struts In Action以及 Professional JSP Site Design。Ted在美国很多团队当过顾问,包括CitiGroup, Wells Fargo, 以及 Pepsi Bottling Group。他有一个提供Struts培训的网站Struts Mentor site (www.StrutsMentor.com),这篇文章是他Struts2系列文章中的第二篇。
正文
在这篇文章里,我们将废弃XML配置文件转而使用CoC(惯例优先)。通过使用Strtus2的SmartURLs插件,我们可以做到Action自动查找所映射的页面。
文章包括以下内容:
-
采用 CoC 风格
-
去掉 XML 配置文件
-
通过 Annotation 进行验证
成功孕育着成功。随着时间的流逝,成功的应用程序会变得越来越庞大,越来越复杂。太多数应用程序正因此如此庞大如此复杂,以及我们根本不可能马上就从整体上考虑应用程序本身。
一种化繁为简的办法就是将复杂的应用程序分层:将类似的模块放在一起,这样我们每次都可以很快的找到它们。我们喜欢将层次分为presentation logic(表示层) 和 business logic(业务层)。用page template(页面模板)就可很容易设计presentation logic,而用source code(源代码)可以很容易的设计business logic。每一层都各司其职,但我们须要通过一种方式将它们串起来。在一个传统的Struts应用程序里,我们是通过XML配置文件将我们所分好的层次串在一起的。在实践过程中,我们写了大量的XML然后说:“对这个请求来说嘛,运行这段Java代码,然后将其结果绑定到这个页面。” 下面的Example1中的struts.xml有点XML过度的感觉:
Example 1: struts.xml
<action name="hello-world" class="actions.HelloWorld"> <result>/results/hello-world.jsp</result> </action>
刚开始,有一点XML看上去是微不足道的。但随着应用程序的不断扩大,将会发现如同写Action或JSP页面一样,编写查找XML也需要浪费大量的时间。现在,一种流行的做法是避免过度使用XML配置文件,转而使用CoC风格。
什么是CoC?
替代编写XML配置文件的办法是使用一致的命名约定,并且将这种约定融入到应用程序框架里去。许多Struts开发人员已经使用这种命名约定来协同开发了。我们仅仅需要按照那些大多数已经被我们熟知的统一风格来编写代码即可,框架会帮我们处理好这一切。
比如说,如果有一个请求指向“hello-world.action”,这就意味着框架会帮我们找到“HelloWorld.class”以及“hello-world.jsp”。如果在这个Action类中,你的result code返回的是一个“small”,那么框架又会优先去考虑“hello-world-small.jsp”(或“hello-world-small.vm”,当然如果你更喜欢Velocity模板的话)。如果“hello-world-small.jsp”找不到的话,它又会去查找先前的“hello-world.jsp”。
这些匹配原则看上去好像太单纯了点,但实际上,一些简单的原则就足以让我们不编写任何XML代码就可以完成整个Struts应用程序。
Struts2是默认支持CoC吗?
Struts2核心部分提供了一些风格来简化配置文件,但想完全得利益于CoC,还得下会儿功夫,我们需要一个新插件支持。
SmartURLs插件为Struts2提供了一套完整支持CoC风格的特性。比如说有一个请求“/my-action”会自动映射到MyAction类上去并且返回my-action.jsp页面。为保正系统的灵活性,如果MyAction类或my-action.jsp其中一个不存在,那么SmartURLs还是会自动匹配它们中的一个。匹配原则请看下面的“准则1:SmartURLs原理”
"准则1: SmartURLs 原理"假设有一个URL请求一个Action (/my/package/hello-world):
-
A-1 提取最后的URL路径部分,即hello-world。
-
A-2 将其首字母大写,并且如果有连接符号(比如说“-”)出现在URL中,将连接符号后的第一个字母大写,然后将连接符号去掉,这样就变成了:HelloWorld。
-
A-3 将剩下的路径全部小写,并且“/”用“.”来代替,这样my/package/就变成了my.pachage.
-
A-4 检查上面操作后的以其actions为根目录开始的包名+类名,这样就变成了actions.my.package.HelloWorld.
-
A-5 如果所匹配的Action找不到,则去找用户包下的默认Action(ActionSupport)。
返回结果路径:
- B-1 加上result-code并按照Action最初的URL返回。比如:/my/package/hello-world-success。
- B-2 检查其results为根目录来匹配JSP,Freemarker或Velocity模板。比如:/WEB-INF/results/my/package/hello-world-success.jsp
- B-3 如果相应的模板页面没有找到,将去加上的result-code,再次查找。比如: /WEB-INF/result/my/package/hello-world.jsp。
- B-4 如果还找不到相应的匹配页面,则抛出404错误。
- 无扩展名的URIs:比如/my/package/hello-world,而不是/my/package/hello-world.action
- 自动绑定Action URLs到惯例的类和页面上去。
- Action URI 的格式符合SEO(搜索引擎优化)
- 用Annotations 来为Action指定一个别名,甚至多个别名。
- 自动支持JSP, Freemarker, and Velocity作为返回结果
- 强大的“index”页处理,比如:/products 会匹配actions.Products或 actions.products.Index
如何用SmartURLs编写一个最简单的“HelloWorld”呢?
像Struts这样的框架会将应用程序的流程分解成一系列Actions,而每一个Action呢又会关系到input validation(输入校验),business logic, persistence logic, message resources(消息资源), text formatting(文本格式化), and an output resource(输出资源)等,并且每一个Action又有自己的上下文环境,因与不同的Actions交互而采用不同的使用策略。
在下面这个最简单的例子中,我们的Action只关系其绑定的结果页面,其它什么business logic, persistence logic等都先不管,毕竟只是一个HelloWorld嘛。但在下面的例子中,我们慢慢加入别的特性。
"准则2: 绑定到页面模板"(注:因为可以用JSP,Freekmarker,Veolcity等,所以这里不指定具体的表示层,统称为页面模板)
SmartURL不需要自定义Action就可以绑定到页面模板上去。以下是该实例具体过程:
- 客户端发送一个请求
- 系统会检测到找不到自定义的Action
- 系统将匹配的页面模板绑定在一个默认的Action上去
- 将返回的结果组织成HTML显示在客户端上
"Example 2: hello-world.jsp"
<html>
<body>
<p>
Hello World!
</p>
<p>
It is now <%= new java.util.Date() %>.
</p>
</body>
</html>
好了。现在可以运行我们的Web容器了,输入“http://localhost:8080/smartapp/hello-world” (主机IP与端口因人而异)。这样SmartUTLs会自动绑定 "WEB-INF/results/hello-world.jsp"页面,然后结果看下面的图1
"图1: Hello World!"
通过这个实例,我们已经知道SmartURLs的基本工作原理了吧。
- 无扩展名的URIs
- 将页面模板放在相对较安全的WEB-INF文件下
- 不需要XML配置文件
文章实在太长了,先弄最简单的第一部分吧,后续会马上跟进的。 下面有本实例的代码,感兴趣的,可以下载试试。