用Stripes做Java Web开拓
副标题#e#
摘要:
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之中的简朴性。
图1是典范的用Stripes写的应用措施中的正常事件流程和组件。
图 1 典范Stripes流程
如你所见,其流程根基上就是一个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 = 13,
on = {"hello"})
})
private Person person;
private ActionBeanContext context;
@DefaultHandler
public Resolution index() {
return new ForwardResolution("Hello.jsp");
}
public Resolution hello() {
return new ForwardResolution("SayHello.jsp");
}
public void setPerson(String person) {this.person = person;}
public String getPerson() { return person;}
public void setContext(ActionBeanContext c) {this.context = c; }
public ActionBeanContext getContext() {return context; }
}
Controller类是一个实现了Stripes特有接口ActionBean的POJO(Plain Old Java Object,译注:读破粥)。所有的Stripes行动类都要实现这一接口以让StripesDispatcher servlet在运行处事时为其注入一个ActionBeanContext工具。ActionBeanContext工具可以让你存取的工具如request、response、和servlet context等servlet API。大大都时候在Stripes应用中是不消读取这些底层API工具的。ActionBeanContext类还提供当前行动的状态并可以添加信息动静和错误动静到当前行动中。ActionBeanContext的变量和其读写要领可以放在一个基类内里,因为所有的Stripes行动都要实现之。
Controller类的其他部门对付任何Java措施员来说都是很面熟的。有一个Person工具和其读写要领是用来读写用户的姓名给view的。固然这仅仅是一个简朴的嵌套工具,Stripes可以通过Java荟萃、泛型支持、和下标化的属性来实现更巨大完善的数据绑缚。因为Stripes可以处理惩罚巨大数据绑缚,你的规模工具(Domain Object)可以在其他需要它们的层重用。譬喻:通过Stripes你可以很容易的收集一个规模工具的信息,然后用其他的POJO框架,如Hibernate可能EJB3来对其举办耐久化。
Person工具变量上有一个Stripes验证annotation用来担保用户在激活hello要领的时候已经输入了姓名。假如用户没有输入这两个必须的变量,原始页会被返回,并显示一个相关的错误动静。该验证只有在hello事件被申请的时候才会被激活,因为annotation的属性中指定了(on = {"hello"})。Stripes还会利用实用默认法例,按照验证要领和变量名称发生一个错误信息。譬喻,假如Person类的firstName变量在提交的时候没有提供,用户将看到:
Person First Name is a required field.
这条动静是通过将Person.firstName举办刻读化处理惩罚后获得的。假如有须要,这些错误动静可以被重载来提供更多的客户自界说成果。
#p#副标题#e#
别的尚有一个Integer范例的变量age,是Person工具的一个属性。Stripes首先试图对request中命为person.age的parameter转换为Integer范例,并将其绑缚到Person工具上。在Person工具的age变量被付值今后,Stripes将验证该Integer值是否小于13。假如用户输入了一个字符串而非整数,用户获得这个动静:
The value (Mark) entered in field Person Age must be a valid number.
若是用户输入了一个小于13的整数,用户将看到这个动静:
The minimum allowed value for Age is 13.
#p#分页标题#e#
同样地,我们没有须要为这些错误动静提供任何外部的设置文件。Annotation提供的验证与你的变量在同一个位置上,使得措施员定位验证、领略验证的内容、和对验证举办维护变换更容易。
这个Stripes行动尚有两个可被激活的要领(称为事件)。事件是ActionBean类中有如下特征的要领:
public Resolution eventName
请留意index要领被标注为@DefaultHandler annotation。因为在本行动中有多个事件,个中一个必需被指定为默认事件。假如挪用本行动的URL没有指定哪个事件,Stripes则查找标注有@DefaultHandler annotation的事件,并执行。
显示层(View)
此刻让我们给Hello World例程加上显示层的逻辑。Stripes默认支持JSP为显示层的尺度技能,不外你也可以用其他的显示层技能,好比FreeMaker。除了Stripes的tag库以外,没有什么新的对象要学。Hello.jsp是初始的显示,可以让用户输入和提交姓名。
<%@ taglib prefix="stripes"
uri="http://stripes.sourceforge.net/stripes.tld" %>
……
<stripes:errors/>
<stripes:form
beanclass="com.
myco.
web.
stripes.
action.
example.
HelloWorldAction">
Say hello to: <br>
First name: <stripes:text name="person.firstName"/>
<br>
Age:<stripes:text name="person.age"/><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/HelloWorld.action">
有一个stripes:input tag指定了一个名为person.firstName属性,其浸染是将其储存的输入值付给controller的Person工具的firstName变量中。最后,stripes:submit tag指定一个name属性来汇报Stripes的HelloWorldAction类利用哪一个事件。
我们此刻已经完成了提交姓名的值给HelloWorldAction,剩下的就是在另一个view中将其反馈给用户了。
<%@ taglib prefix="stripes"
uri="http://stripes.sourceforge.net/stripes.tld" %>
……
<stripes:errors/>
<h2>Hello ${actionBean.person.firstName} your age is
${actionBean.person.age} </h2>
<p/>
<stripes:link beanclass="com.myco.web.stripes.action.
example.HelloWorldAction">
Say Hello Again
</stripes:link>
……
本JSP将本身通过一个对行动的引用读取person的姓名信息并显示。为到达这一目标,Stripes自动在request的属性中添加一个名为actionBean行动工具,以供JSTL存取。最后,我们用了一个strips:link tag来成立一个返回HelloWorldAction地链接从而可以让我们输入差异的姓名。我们以可以通过如下步伐显式地建设一个指向index事件的stripes:link:
<stripes:link
beanclass="com.myco.web.stripes.action.
example.HelloWorldAction"
event="index">Say Hello Again</stripes:link>
因为我们已经用annotation把index要领标志为@DefaultHandler,Stripes无须event属性也知道要执行哪一个要领。
用约定、不消设置文件
我们此刻有了Java组件,我们该做设置了,把行动映射的一个URL上,并将其毗连到我们的两个view上面去。等一下!我们在用Stripes,我们不需要外部设置文件!
固然这听来仿佛好得不像是真的,但这简直是Stripes的一项最具出产效率的成果。Stripes利用约定而非设置文件来映射行动到URL上。我们也无须利用一个外部设置文件来把view映射到一个个标志名字上。这意味着措施员不消再为了一个标志名字——例如说SUCCESS——的实际来历,而在设置文件中跳来跳去了。没有须要在Java和view组件的外部举办配线,因而导致更好地维护性和更高的出产率。
Stripes是如何提供隐式的URL映射而无需在外部设置每一个行动可能而外的annotation呢?这个可以从Stripes在web.xml中的设置以及它是如何通过实用默认法成立URL映射来表明。首先,我们来看看Servlet过滤器:StripesFilter。其在web.xml中的默认设置如下:
#p#分页标题#e#
<filter>
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class>
net.sourceforge.stripes.controller.StripesFilter
</filter-class>
<init-param>
<param-name>ActionResolver.UrlFilters</param-name>
<param-value>/WEB-INF/classes</param-value>
</init-param>
</filter>
当Servlet容器启动的时候,StripesFilter对其init-param元素执行初始化。个中最重要的init-param元素就是ActionResolver.UrlFilters参数。这个参数汇报Stripes到那边查找跟Stripes有关的类。这个例子内里,Stripes将查找/WEB-INF/classes目次下的所有实现ActionBean接口的类。每一个被找到的类和其绑定的URL都将被插手一个Map中。
让我们来看看Stripes是如那里理惩罚我们的HelloWorldAction行动为例子吧。因为HelloWorldAction类位于/WEB-INF/classes目次下,所以会被认为是一个Stripes servlet。在我们的例子傍边,其完整类名是com.myco.web.stripes.action.example.HelloWorldAction。随后,其完整类名将凭据如下法例被翻译成一个URL绑定。
1.将含有www、web、stripes、和action的部门及其以前的内容删掉。在我们的例子有三个上述单词,所以我们获得了example.HelloWorldAction。
2.假如类名中包容带Action或Bean的尾巴,删掉。因为我们的类名以Action末了,我们获得了example.HelloWorld。
3.将.替换为/,我们获得了example/HelloWorld。
4.最后,添加上一个尾缀(默认是.action)从而完成了URL绑定。最后的功效是example/HelloWorld.action。
此刻Stripes找到了ActionBean类并为其建设了一个URL绑定,然后存放在一个java.util.Map<String, Class< extends ActionBean>>之中。个中key参数是URL绑定,value参数是实现ActionBean的类名。下面是我们的例子中的Map:
URL绑定:/example/HelloWorld.action
ActionBean类:com.myco.web.stripes.action.example.HelloWorldAction
我们要看的第二个组件是Stripes如何把URL绑定翻译成你正在做的这个ActionBean类。这是Stripes调治servlet的职责,在web.xml中的设置如下:
<servlet>
<servlet-name>StripesDispatcher</servlet-name>
<servlet-class>
net.sourceforge.stripes.controller.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>StripesDispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
StripesDispatcher的一个职责就是将URL理会为Stripes的ActionBean类。当用户激活URL http://host/uri/example/HelloWorld.action的时候,Stripes调治servlet将在URL映射表中查询并找到com.myco.web.stripes.action.example.HelloWorldAction类,并实例化发生该类的一个实例。最后,index要领被激活,因为在annotation中它被界说为默认局柄而在该URL中并没有指定一个事件。
假如我们想要直接执行HelloWorldAction中的hello要领怎么办?应该象下面这个URL这样把事件的名字放在request的参数中:
http://host/uri/example/HelloWorld.actionhello=&firstName=Mark&age=13
请留意我们没有给hello这个request参数名指定任何值。在这个环境下,StripesDispatcher会把hello这个这个request参数名和HelloWorldAction类中个一个带有public Resolution hello()签名的函数识别并映射。该要领名城在初始化的时候为了机能而缓存在另一个Map中。
我们已经看到Stripes的基本以及假如建设简朴的行动和一些该框架是如何运作的细节。通过在web.xml中的初始化,我们可以或许制止用多个单独的XML设置文件来把我们的显示层组建写在一起。这很重要,原因如下:首先,假如你需要任何窜改,你可以看到一个URL就当即知道你该查察哪一个类。其次,我们不需要任何单独的东西来在你的设置文件过大并且不行打点的时候辅佐你。通过消除去设置文件,我们不再需要给框架一大堆的metadata。最后,我们不再需要为一个独立的用来描写我们的组件是假如彼此关联的文件来一刻不断维护了。
Ajax
要不要更高级的成果?那我们就来看看Stripes是怎么处理惩罚Ajax的。我们将把先前的Hello World例程改成利用Ajax挪用Stripes行动。本例子的源代码可以在本文资源中找到。首先,我们对Hello.jsp作窜改使其引用Prototype JavaScript函数库。我们还要为Ajax挪用增加一个JavaScript函数,并变动提交按钮为其添加一个onclick事件:
<%@ taglib prefix="stripes"
uri="http://stripes.sourceforge.net/stripes.tld" %>
……
<script
src="${pageContext.request.contextPath}/js/prototype.js"
type="text/javascript"></script>
#p#分页标题#e#
<script type="text/javascript">
function sayHelloAjax() {
var myAjax = new Ajax.Updater(‘hello’,
"<stripes:url
beanclass="com.
myco.
web.
stripes.
action.
example.
HelloWorldAction"
event="sayHelloAjax"/>",
{
method: ‘get’,
parameters: Form.serialize(‘helloForm’)
});
}
</script>
……
<stripes:errors/>
<stripes:form
beanclass="com.
myco.
web.
stripes.
action.
example.
HelloWorldAction"
id="helloForm">
Say hello to: <br>
First name: <stripes:text
name="person.firstName"/><br>
Age:<stripes:text name="person.age"/><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("SayHelloAjax.jsp");
}
这个要领没有几多事情,因为Stripes已经包袱了姓名内容的绑定。因此,本要领独一的责任就是转发到一个叫SayHelloAjax.jsp的页面片段上去。该叶面片断的内容如下:
<h2>Hello ${actionBean.person.firstName} your age is ${actionBean.person.age}!</h2>
Spring整合
Stripes还内置了对Spring支持。你可以自动地将Spring bean诸如到你的行动中。凭据Stripes的气势气魄,除了Spring上下文设置文件以外不需要任何外部设置文件。假如我们Spring的设置文件如下:
<bean id="personService" parent="abstractTxDefinition">
<property name="target">
<bean class="com.myco.service.impl.PersonServiceImpl"/>
</property>
</bean>
要把person处事注入到一个Stripes行动中,得增加一个跟Spring bean的名字一致的属性和setter。Stripes提供了@SpringBean annotation来查询正确的Spring bean以注入到行动之中。下面是我们要在行动类中包括的例子:
private PersonService personService;
@SpringBean
public void setBlogService(BlogService blogService) {
this.blogService = blogService;
}
本文无法席卷Stripes的所有高级成果。可是,Stripes有很是完整的文档。Stripes还包括了一个与Tiles雷同但无需外部设置文件的layout打点器。别的,拦截器还可以用于生命周期事件的遍地、文件上载等等等等。
结论
Stripes是一个既强大又简朴的Java web框架。Stripes操作了Java 5的annotation和泛型成果,从而使得Java措施员制止维护外部设置文件并增加事情效率。Stripes可以简化坚苦的web开拓事情,并使得简朴的事情越发简朴!