Stripes是一个以让程序员的web开发简单而高效为准则来设计的基于动作的开源Java web框架本文将介绍Stripes与其它如Struts之类基于动作的框架的区别和其提供的一些存在于Ruby on Rails之中的简单性
Stripes是一个以让程序员的web开发简单而高效为准则来设计的基于动作的开源Java web框架传统的Java web开发着眼于借去耦(Decoupling)来实现其灵活性但导致多个的配置文件额外的对象和其他资源的分散这些困难造成相当多的程序员的更高的学习时间和低下的效率其结果是有些Java程序员被一些非Java的框架所吸引去了Ruby on Rails或者Django一些Java web框架如Stripes正在开始从这些非Java框架中汲取其成功经验简单而高效的开发本文将介绍Stripes与其它如Struts之类基于动作的框架的区别和其提供的一些存在于Ruby on Rails之中的简单性
其流程基本上就是一个MVC框架Stripes和其他的基于动作的框架的一个主要的区别是没有一个外部的配置文件我们随后将看到Stripes用annotation和约定而非配置来提高产出和减少杂乱
编写你的第一个Stripe动作(Action)
让我们现在就开始通过创建Hello World例程来了解Stripes框架和理解其运作HelloWorldAction类将提示用户输入姓氏和名字然后在另一个View里面显示首先我们来编写controller类
public class HelloWorldAction implements ActionBean
{
@ValidateNestedProperties(
{
@Validate(field = "firstName" required = true on = {"hello"})
@Validate(field = "age" required = true minvalue = on = {"hello"})
})
private Person person;
private ActionBeanContext context;
@DefaultHandler public Resolution index()
{
return new ForwardResolution("Hellojsp");
}
public Resolution hello()
{
return new ForwardResolution("SayHellojsp");
}
public void setPerson(String person) {thisperson = person;}
public String getPerson() { return person;}
public void setContext(ActionBeanContext c) {thiscontext = c; }
public ActionBeanContext getContext() {return context; }}
Controller类是一个实现了Stripes特有接口ActionBean的POJO(Plain Old Java Object译注读破粥)所有的Stripes动作类都要实现这一接口以让StripesDispatcher servlet在运行服务时为其注入一个ActionBeanContext对象ActionBeanContext对象可以让你存取的对象如requestresponse和servlet context等servlet API大多数时候在Stripes应用中是不用读取这些底层API对象的
ActionBeanContext类还提供当前动作的状态并可以添加信息消息和错误消息到当前动作中ActionBeanContext的变量和其读写方法可以放在一个基类里面因为所有的Stripes动作都要实现之
Controller类的其他部分对于任何Java程序员来说都是很面熟的有一个Person对象和其读写方法是用来读写用户的姓名给view的虽然这仅仅是一个简单的嵌套对象Stripes可以通过Java集合泛型支持和下标化的属性来实现更复杂完善的数据捆绑因为Stripes可以处理复杂数据捆绑你的领域对象(Domain Object)可以在其他需要它们的层重用例如通过Stripes你可以很容易的收集一个领域对象的信息然后用其他的POJO框架如Hibernate或者EJB来对其进行持久化
Person对象变量上有一个Stripes验证annotation用来保证用户在激活hello方法的时候已经输入了姓名如果用户没有输入这两个必需的变量原始页会被返回并显示一个相关的错误消息该验证只有在hello事件被申请的时候才会被激活因为annotation的属性中指定了(on = {"hello"})Stripes还会使用实用默认法则根据验证方法和变量名称产生一个错误信息例如如果Person类的firstName变量在提交的时候没有提供用户将看到
Person First Name is a required field
这条消息是通过将PersonfirstName进行刻读化处理后得到的如果有必要这些错误消息可以被重载来提供更多的客户自定义功能
另外还有一个Integer类型的变量age是Person对象的一个属性Stripes首先试图对request中命为personage的parameter转换为Integer类型并将其捆绑到Person对象上在Person对象的age变量被付值以后Stripes将验证该Integer值是否小于如果用户输入了一个字符串而非整数用户得到这个消息
The value (Mark) entered in field Person Age must be a valid number
若是用户输入了一个小于的整数用户将看到这个消息
The minimum allowed value for Age is
同样地我们没有必要为这些错误消息提供任何外部的配置文件Annotation提供的验证与你的变量在同一个位置上使得程序员定位验证理解验证的内容和对验证进行维护变动更容易
这个Stripes动作还有两个可被激活的方法(称为事件)事件是ActionBean类中有如下特征的方法
public Resolution eventName
请注意index方法被标注为@DefaultHandler annotation因为在本动作中有多个事件其中一个必须被指定为默认事件如果调用本动作的URL没有指定哪个事件Stripes则查找标注有@DefaultHandler annotation的事件并执行
显示层(View)
现在让我们给Hello World例程加上显示层的逻辑Stripes默认支持JSP为显示层的标准技术不过你也可以用其他的显示层技术比如FreeMaker除了Stripes的tag库以外没有什么新的东西要学Hellojsp是初始的显示可以让用户输入和提交姓名
<%@ taglib prefix="stripes" uri="%>
<stripes:errors/>
<stripes:form beanclass="com myco web stripes action example HelloWorldAction"> Say hello t <br> First name:
<stripes:text name="personfirstName"/> <br> Age:<stripes:text name="personage"/><br>
<stripes:submit name="hello" value="Say Hello"/>
</stripes:form>
这个JSP易读易维护而Stripes用于form和input的tag跟对应的HTML代码非常相似stripes:form tag包含一个beanclass属性其值为我们前面定义的controller类的完整类名我们可以用stripes:form中的action属性来替换beanclass属性但是beanclass属性可以让你在以后对Stripes动作进行重构的时候更加方便如果你要用在stripes:form tag中使用action属性方法如下
<stripes:form action="/example/HelloWorldaction">
有一个stripes:input tag指定了一个名为personfirstName属性其作用是将其储存的输入值付给controller的Person对象的firstName变量中最后stripes:submit tag指定一个name属性来告诉Stripes的HelloWorldAction类使用哪一个事件
我们现在已经完成了提交姓名的值给HelloWorldAction剩下的就是在另一个view中将其反馈给用户了
<%@ taglib prefix="stripes" uri="%>
<stripes:errors/>
<h>Hello {actionBeanpersonfirstName} your age is {actionBeanpersonage} </h> <p/>
<stripes:link beanclass="commycowebstripesaction exampleHelloWorldAction"> Say Hello Again
</stripes:link>
本JSP将自己通过一个对动作的引用读取person的姓名信息并显示为达到这一目的Stripes自动在request的属性中添加一个名为actionBean动作对象以供JSTL存取最后我们用了一个strips:link tag来建立一个返回HelloWorldAction地链接从而可以让我们输入不同的姓名我们以可以通过如下办法显式地创建一个指向index事件的stripes:link
<stripes:link beanclass="commycowebstripesaction exampleHelloWorldAction" event="index">
Say Hello Again
</stripes:link>
因为我们已经用annotation把index方法标记为@DefaultHandlerStripes无须event属性也知道要执行哪一个方法
用约定不用配置文件
我们现在有了Java组件我们该做配置了把动作映射的一个URL上并将其连接到我们的两个view上面去等一下!我们在用Stripes我们不需要外部配置文件!
虽然这听来好像好得不像是真的但这的确是Stripes的一项最具生产效率的功能Stripes使用约定而非配置文件来映射动作到URL上我们也无须使用一个外部配置文件来把view映射到一个个标记名字上这意味着程序员不用再为了一个标记名字——比方说SUCCESS——的实际来源而在配置文件中跳来跳去了没有必要在Java和view组件的外部进行配线因而导致更好地维护性和更高的生产率
Stripes是如何提供隐式的URL映射而无需在外部配置每一个动作或者而外的annotation呢?这个可以从Stripes在webxml中的配置以及它是如何通过实用默认法建立URL映射来解释首先我们来看看Servlet过滤器StripesFilter其在webxml中的默认配置如下
<filter>
<displayname>Stripes Filter</displayname>
<filtername>StripesFilter</filtername>
<filterclass> netsourceforgestripescontrollerStripesFilter </filterclass>
<initparam>
<paramname>ActionResolverUrlFilters</paramname>
<paramvalue>/WEBINF/classes</paramvalue>
</initparam>
</filter>
当Servlet容器启动的时候StripesFilter对其initparam元素执行初始化其中最重要的initparam元素就是ActionResolverUrlFilters参数这个参数告诉Stripes到哪里查找跟Stripes有关的类这个例子里面Stripes将查找/WEBINF/classes目录下的所有实现ActionBean接口的类每一个被找到的类和其绑定的URL都将被加入一个Map中
让我们来看看Stripes是如何处理我们的HelloWorldAction动作为例子吧因为HelloWorldAction类位于/WEBINF/classes目录下所以会被认为是一个Stripes servlet在我们的例子当中其完整类名是commycowebstripesactionexampleHelloWorldAction随后其完整类名将按照如下法则被翻译成一个URL绑定
. 将含有wwwwebstripes和action的部分及其以前的内容删掉在我们的例子有三个上述单词所以我们得到了exampleHelloWorldAction
. 如果类名中包涵带Action或Bean的尾巴删掉因为我们的类名以Action结尾我们得到了exampleHelloWorld
. 将替换为/我们得到了example/HelloWorld
. 最后添加上一个尾缀(默认是action)从而完成了URL绑定最后的结果是example/HelloWorldaction
现在Stripes找到了ActionBean类并为其创建了一个URL绑定然后存放在一个javautilMap<String Class<? extends ActionBean>>之中其中key参数是URL绑定value参数是实现ActionBean的类名下面是我们的例子中的Map
URL绑定/example/HelloWorldaction
ActionBean类commycowebstripesactionexampleHelloWorldAction
我们要看的第二个组件是Stripes如何把URL绑定翻译成你正在做的这个ActionBean类这是Stripes调度servlet的职责在webxml中的配置如下
<servlet>
<servletname>StripesDispatcher</servletname>
<servletclass>
netsourceforgestripescontrollerDispatcherServlet </servletclass>
<loadonstartup></loadonstartup>
</servlet>
<servletmapping>
<servletname>StripesDispatcher</servletname>
<urlpattern>*action</urlpattern>
</servletmapping>
StripesDispatcher的一个职责就是将URL解析为Stripes的ActionBean类当用户激活URL 的时候Stripes调度servlet将在URL映射表中查询并找到commycowebstripesactionexampleHelloWorldAction类并实例化产生该类的一个实例最后index方法被激活因为在annotation中它被定义为默认局柄而在该URL中并没有指定一个事件
如果我们想要直接执行HelloWorldAction中的hello方法怎么办?应该象下面这个URL这样把事件的名字放在request的参数中
请注意我们没有给hello这个request参数名指定任何值在这个情况下StripesDispatcher会把hello这个这个request参数名和HelloWorldAction类中个一个带有public Resolution hello()签名的函数识别并映射该方法名城在初始化的时候为了性能而缓存在另一个Map中
我们已经看到Stripes的基础以及如果创建简单的动作和一些该框架是如何运作的细节通过在webxml中的初始化我们能够避免用多个单独的XML配置文件来把我们的显示层组建写在一起这很重要原因如下首先如果你需要任何改动你可以看到一个URL就立即知道你该查看哪一个类其次我们不需要任何单独的工具来在你的配置文件过大而且不可管理的时候帮助你通过消除掉配置文件我们不再需要给框架一大堆的metadata最后我们不再需要为一个独立的用来描述我们的组件是如果相互关联的文件来一刻不停维护了
Ajax
要不要更高级的功能?那我们就来看看Stripes是怎么处理Ajax的我们将把先前的Hello World例程改成使用Ajax调用Stripes动作本例子的源代码可以在本文资源中找到首先我们对Hellojsp作改动使其引用Prototype JavaScript函数库我们还要为Ajax调用增加一个JavaScript函数并更改提交按钮为其添加一个onclick事件
<%@ taglib prefix="stripes" uri="%>
<script src="{pageContextrequestcontextPath}/js/prototypejs" type="text/javascript">
</script>
<script type="text/javascript">
function sayHelloAjax(){
var myAjax = new AjaxUpdater(hello "<stripes:url beanclass="com myco web stripes action example HelloWorldAction" event="sayHelloAjax"/>" { method: get parameters: Formserialize(helloForm) });
}
</script>
<stripes:errors/>
<stripes:form beanclass="com myco web stripes action example HelloWorldAction" id="helloForm">
Say hello t
<br> First name:
<stripes:text name="personfirstName"/><br>
Age:<stripes:text name="personage"/><br>
<stripes:button name="helloAjax" value="Say Hello" onclick="sayHelloAjax()"/>
<div id="hello"></div>
</stripes:form>
stripes:button有一个onclick事件将会调用HelloWorldAction类中的sayHelloAjax方法并将其结果返回在一个叫hello的div tag中下面是我们要在HelloWorldAction中介绍的一个新方法
public Resolution sayHelloAjax()
{
return new ForwardResolution("SayHelloAjaxjsp");
}
这个方法没有多少工作因为Stripes已经承担了姓名内容的绑定因此本方法唯一的责任就是转发到一个叫SayHelloAjaxjsp的页面片断上去该叶面片段的内容如下
<h>Hello {actionBeanpersonfirstName} your age is {actionBeanpersonage}!</h>
Spring整合
Stripes还内置了对Spring支持你可以自动地将Spring bean诸如到你的动作中按照Stripes的风格除了Spring上下文配置文件以外不需要任何外部配置文件如果我们Spring的配置文件如下
<bean id="personService" parent="abstractTxDefinition">
<property name="target">
<bean class="commycoserviceimplPersonServiceImpl"/>
</property>
</bean>
要把person服务注入到一个Stripes动作中得增加一个跟Spring bean的名字一致的属性和setterStripes提供了@SpringBean annotation来查询正确的Spring bean以注入到动作之中下面是我们要在动作类中包含的例子
private PersonService personService;
@SpringBeanpublic void setBlogService(BlogService blogService)
{
thisblogService = blogService;
}
本文无法囊括Stripes的所有高级功能但是Stripes有非常完整的文档Stripes还包含了一个与Tiles类似但无需外部配置文件的layout管理器另外拦截器还可以用于生命周期事件的各处文件上载等等等等
结论
Stripes是一个既强大又简单的Java web框架Stripes利用了Java 的annotation和泛型功能从而使得Java程序员避免维护外部配置文件并增加工作效率Stripes可以简化困难的web开发工作并使得简单的工作更加简单!