apachewicket_教程–ApacheWicket:有趣的Web框架
apache wicket
选择Java技术堆栈时,不乏多样性。 为了摆脱“滚动⾃⼰的[⼀切]”的想法,以⾄于许多公司都沦为猎物,⽽“⼀切必须成为标准”的哲学,则对每⼀层都采取了各种各样的⽅法过去⼏年中,Java体系结构如⾬后春笋般涌现。 UI层对于竞争技术的奋⽃并不陌⽣。 有了提供的所有选项,您将如何选择? 尽管没有作家能为您做出选择,但我们可以做的是向您介绍您可能要考虑的另⼀种选择,希望它会引起您⾜够的兴趣并尝试⼀下。 在本⽂中,我将向您介绍Apache Wicket。
您可能会对⽂章的标题感到怀疑,对于我编写的⼤多数应⽤程序,Wicket是我偏爱的UI框架。 您是否最终对这个伟⼤的框架抱有相同的热情–还是它适合您想要编写的应⽤程序类型–取决于您和您的情况。 但是,继续阅读,您可能会发现另⼀种很棒的技术可以添加到您的“go-to”堆栈中。
Wicket是⼀个⾯向组件的Java Web框架,致⼒于简化编写可重⽤代码的过程。 它不需要XML配置(除了标准的l部署描述符中的⼏⾏内容),因此可以刷新使⽤。 它⿎励逻辑与标记之间的清晰区分,从⽽满⾜⼤多数程序员对顺序感的需求。 使⽤Wicket,您的代码
是“仅Java”,⽽标记是“仅HTML”。 这使“ HTML专家”更容易地协助该项⽬,⽽不必担⼼弄乱某些东西,⽽不必学习复杂的模板语⾔,参数名称和值等。
Wicket吸引了⼀个热爱⾯向对象编程的⼈。页⾯上的每个组件都是⼀个真实的Java对象。它可以像任何Java对象⼀样保持状态并执⾏有⽤的功能。这样做的好处是,此状态通过Web请求保持不变,以向您抽象⽆状态超⽂本传输协议的复杂性。想象⼀下,您的页⾯在侧⾯导航栏中具有⼿风琴样式的⾯板,并且在主内容区域中具有复杂的数据表。⽤户进⼊此页⾯并展开⼿风琴的⼀部分。在您的代码中,您可以调⽤tExpandedSection(index),也可以在其上设置tExpanded(true),在其他⽰例上设置fal。⽆论是对组件进⾏建模,还是可以将⽤户选择作为常规Java成员变量存储在⼿风琴组件或其⼦组件中。现在,当⽤户单击⼀个链接来还原您的复杂数据表时,该表本⾝不需要了解⼿风琴组件或页⾯上任何其他组件的状态。但是,⼿风琴组件以及所有其他组件会在⽤户提出的各种其他请求中⾃动保留其状态。
为了达到上述级别的UI复杂性,许多⽅法要求页⾯上的每个组件都具有页⾯上其他组件的⼀定程度的知识。 在这些其他技术中,当我单击⼀个组件中的链接时,它需要将其他组件的状态嵌⼊到其请求中。 也许它在URL中,或者它存储在会话中。 也许每个组件都有⼀个cookie 前缀,并将其状态存储在cookie中。 或者,也许您的程序员会花⼤量时间在⼀个复杂的XML⽂件中配置组件之间的交互,该XML⽂件将各种组件的输⼊和输出联系在⼀起。 ⽆论如何,这都不是简单的编程-有些甚⾄根本都不是编程-它需要不断地意识到您正在基于⽆状态协议编写有状态应⽤程序这⼀事实。
Wicket通过为我们的Web应⽤程序的HTTP交互引⼊⼀种状态引擎来解决此问题。当⽤户⾸次访问页
⾯时,将实例化Java类的实例。构造该类时,就像实例化任何其他Java类⼀样,即构造新的MyComponent(“ SomeID”)时,可以构造其组件。
当这些组件的状态更改时,它们会将状态存储为具有实际类型的真实变量。但是,在初始页⾯呈现之后,Wicket不会丢弃实例化的页⾯对象。整个组件层次结构存储在⽤户会话中(以及本⽂中未介绍的有效⽅式以及磁盘)中。因此,当他们单击链接以在页⾯上执⾏某些操作,提交表单等时,所有状态仍包含在这些类中。这就是使它变得如此简单(和有趣!)的原因。
现在,⼤多数⼈认为的第⼀件事是“不使⽤⼤量内存吗?”答案是否定的!许多⼈迷失了这种⽅法是浪费内存的陷阱,但是随后发现这等于过早的优化。这并不是说您在使⽤Wicket时不能踢⾃⼰–您当然可以做会使会话⼤⼩爆炸的事情。我们将在下⾯的“什么是模型”⼀节中讨论更多有关此的内容。如果您真的担⼼能够验证我的主张,即Wicket不会⽐其他框架使⽤更多的内存,那么您可能需要查看和另⼀篇⽂章,以(均由彼得·托马斯(Peter Thomas)。如果您现在只能信任我,请继续。
原型已经为您完成了“ hello world”部分。因此,让我们创建⼀个简单的链接⽰例,该链接可以增加页⾯上的计数器。要开始使⽤(假设您具有上⾯运⾏的简单快速⼊门),请找到HomePage.java和HomePage.html⽂件,然后在编辑器中将其打开。请注意,除⾮另有说明,否则本⽂中的所有⽰例都将基于最新的稳定版本1.4.18(在撰写本⽂时)
⾸先让我们看⼀下HTML。您可能注意到的第⼀件事是它只是常规HTML。这是设计使然。标记不应包含逻辑-这是Wicket的理念。要将标记与控制它的相应Java代码绑定在⼀起,您只需向其添加wicket:id属性。在这个标记中,我们有两件事是动态的。最明显的是链接。第⼆个是跨度,可在每个页⾯呈现时呈现计数器的值。我们之所以使⽤span标签,仅仅是因为我们需要⼀个标记占位符来插⼊值,并且span标签不会破坏其他标记的结构。六的笔顺
HomePage.html
<html xmlns:wicket="/dtds.data/wicket-xhtml1.4-strict.dtd" >
<head>
<title>Wicket Quickstart Archetype Homepage</title>
</head>
<body>
<strong>Wicket Quickstart Archetype Homepage</strong>
<br/><br/>
Click <a href="#" wicket:id="increment">this link</a> to increment
the following counter:<br />
Counter: <span wicket:id="counter">123</span>
</body>
</html>
现在,让我们看⼀下相关的Java代码。您应该注意的第⼀件事是⽂件的名称和位置。默认情况下,对于任何Wicket组件(我们在此处使⽤的页⾯也是组件,因此适⽤相同的规则),Wicket会在类路径的相同包⽬录中查找名称相同,扩展名为的⽂件。 html(如果要呈现XML,则仍然适⽤,但扩展名为.xml)。可以重新配置此⾏为,但是对于本⽂,我们不再赘述。有在线资源可以在需要时为您提供帮助。
在检查实际代码时,您将看到⼏个部分。您⾸先看到的是counterModel。您会注意到它实现了IModel。模型在Wicket中⾮常重要。当程序员第⼀次采⽤Wicket时,它们通常也是⼈们最不了解的概念之⼀。我们将在以后再讨论。就⽬前⽽⾔,⾜以说它们是⽤于检索或设置数据的包装器,⽽顾名思义,这个AbstractReadOnlyModel仅仅是⽤于检索数据的包装器。我们实现getObject⽅法以在每次调
⽤时检索counter的当前值。然后将此模型传递给Component。在这种情况下,组件是标签。标签只是获取标记中与标签相关联的任何标签,并将标签的主体替换为从给定模型中获取的值。在这种情况下,我们的span标签将⽤计数器的值替换“ 123”(标记中的静态主体),该值在第⼀页呈现为零。
接下来看到的是链接。它让您仅了解Wicket的功能。我们在这⾥使⽤匿名内部类来实现链接。如果您的应⽤程序中有多个位置需要增加⼀个整数,则可以通过将匿名内部类提取到⼀个真正的顶级类中,⽽⽤⼀个具体的类替换它。
现在,看看在链接上实施各种操作有多么简单。您只需覆盖onClick⽅法,然后将业务逻辑放⼊其中。显然,增加计数器是⼀个简单的⽰例,但是您也可以轻松调⽤服务层⽅法,该⽅法删除或编辑您域中的某些内容,发送电⼦邮件或进⾏Web服务调⽤。Wicket为您完成了艰巨的任务。您可以实现逻辑,⽽不必担⼼如何获取将指⽰运⾏少量逻辑的URL。您不必担⼼计数器的当前状态存储在哪⾥。您所要做的就是编写代码,以执⾏应⽤程序设计的⼯作。这就是为什么我喜欢Wicket!
HomePage.java
public class HomePage extends WebPage {
private static final long rialVersionUID = 1L;
private int counter = 0;
public HomePage() {
IModel<Integer> counterModel = new AbstractReadOnlyModel<Integer>() {
private static final long rialVersionUID = 1L;
@Override
public Integer getObject() {
return counter;
}
};
Label label = new Label("counter", counterModel);
add(label);
Link<Void> link = new Link<Void>("increment") {
private static final long rialVersionUID = 1L;
@Override
public void onClick() {
counter++;
}
};
add(link);
}
}
那AJAX呢?
好的,建⽴链接很容易。那⼜怎样呢?现在,查看使该链接成为完全⽀持⾮AJAX请求的AJAX链接有多么容易。⾸先,我们将标签实例定为最终实例,以便我们可以在匿名内部类中对其进⾏访问,并调⽤tOutputMarkupId(true),以便在span标签中呈现其唯⼀ID,并可以在客户端的DOM中找到它。 。我们将Link类更改为AjaxFallbackLink,并更改onClick⽅法签名以匹配超类。可以将传⼊的AjaxRequestTarget视为以XML形式发送回客户端进⾏处理的事物队列。通过将标签添加到该队列中,它将被重新呈现,以XML格式通过流传输,并在客户端的DOM中被替换。重新渲染时,它将使⽤其模型(还记得counterModel?)来获取要渲染的最新值。所有这些都⽆需我们编写⼀⾏代码即可进⾏任何转换或DOM操作!
我们为什么要检查AjaxRequestTarget是否为null?因为此链接将在没有启⽤JavaScript的浏览器上⾃动为您⼯作。在这种情况下,页⾯将像使⽤常规链接⼀样简单地重新呈现。是的,您听到了我的声⾳-它⽆需任何其他⼯作即可正常降级!太好了,难以置信,对吧?您会发现,默认情况下,Wicket在⼤多数情况下都会以这种⽅式⼯作。可以使⽤常规HTTP或AJAX进⾏表单提交甚⾄⽂件上传,⽽⼏乎不需要额外的⼯作。在1.5版本中,添加了⼀个⾮常⽅便的事件机制,使向AjaxRequestTarget添加组件的⼯作变得更加容易。
HomePage.java(注意:仅显⽰构造函数)
public HomePage() {
IModel<Integer> counterModel = new AbstractReadOnlyModel<Integer>() { … }
// above code folded for brevity
final Label label = new Label("counter", counterModel);
add(label.tOutputMarkupId(true));
肠息肉怎么治最好
AjaxFallbackLink<Void> link = new AjaxFallbackLink<Void>("increment") {
private static final long rialVersionUID = 1L;
@Override
public void onClick(AjaxRequestTarget target) {
counter++;
if (target != null)
target.addComponent(label);
}
};
add(link);
}
在许多Web应⽤程序中,构建应⽤程序要完成的许多⼯作都变成接受⽤户输⼊的形式。在Wicket中,表单与其他所有组件⼀样都是常规对象。为了查看⽰例,让我们看⼀个简单的表单,该表单接受⽤户的姓名,电⼦邮件和出⽣⽇期
查看HTML,您不会发现有什么特别之处。 您的设计师可以给您⼀个HTML表单,您可以将wicket:id属性添加到输⼊字段和表单本⾝。带有wicket:id“ feedback”的div是内置Wicket组件的占位符,它向⽤户显⽰该表单⽣成的所有反馈消息。 在上图中,⽤户刚刚提交了带有错误数据的表单,这导致验证消息出现在我们的反馈⾯板组件中。 可以轻松地⾃定义这些字段,以便在每个表单字段附近都有⼀个反馈⾯板,该⾯板仅显⽰该特定字段的消息-这是许多应⽤程序中的常见要求。
单⼀表格的例⼦
<html xmlns:wicket="/dtds.data/wicket-xhtml1.4-strict.dtd" >
<head>
<title>Quickstart Homepage</title>
</head>
<body>
Add / Edit Person:
<div wicket:id="feedback"></div>
<form wicket:id="form">
First Name: <input type="text" wicket:id="firstName" /><br />
Email Address: <input type="text" wicket:id="email" /><br />
Date of Birth: <input type="text" wicket:id="dob" /><br />
<input type="submit" value="Save" />
</form>
</body>
</html>
现在查看Java代码,您会看到我们创建了两个构造函数。这样,该表格既可以⽤作“添加⼈”表格,也可以⽤作“编辑现有⼈”表格。默认情况下,将调⽤⽆参数构造函数,该构造函数使其成为“添加个⼈”形式,因为它使⽤的是持有新个⼈的模型。但是,只需将模型传递给现有⼈员,即可轻松地编写⼀个向⽤户显⽰“编辑⼈员”页⾯的链接。(请注意,我们在本⽂中没有介绍该⽰例,但是如果您这样做,则可能需要使⽤LoadableDetachableModel,在模型部分中将对此进⾏详细讨论。)
接下来,您看到我们实例化了Form对象。我们实现onSubmit⽅法并使其包含我们的业务逻辑-在这种情况下,是对服务层类的简单调⽤。继续前进,您会看到我们在哪⾥创建每个字段并使⽤UI规则对其进⾏配置-哪些字段是必填字段,以及哪些字段是必填字段(例如,有效的电⼦邮件地址格式)。您看不到的是令⼈⽿⽬⼀新的信息-⽆需调⽤HttpRequest即可获取字符串,⽆需将字符串转换为⽇期,⽆需⼿动从表单发布中检索数据,也⽆需⼿动设置背景Person对象上的数据。在长格式中,这节省了数百⾏复制和粘贴以及⾼度易出错的代码。这是因为我们正在处理真正的Java对象,⽽不是⼀堆必须⼿动操作的字符串。有关更多信息,请参见“前⼗名”列表中的表单处理图像以及下⾯指向前⼗名完整列
表的链接。
请注意,我们正在使⽤服务层对象(IPersonService)进⾏持久化。这是⼀个Spring管理的bean,并且由于Wicket的内置Spring(如果需要,可以选择Guice)集成,使⽤起来⾮常简单。
public class HomePage extends WebPage {
@SpringBean
IPersonService personService;
public HomePage() {
this(new Model<Person>(new Person()));
}
public HomePage(IModel<Person> model) {
Form<Person> form = new Form<Person>("form", model) {
简历写作
protected void onSubmit() {
Person person = ModelObject();
孔雀收屏System.out.println("Saving: " + person);用也许造句
personService.savePerson(person);
频度副词的位置
}
};
TextField<String> firstName = new TextField<String>("firstName", new PropertyModel<String>(model, "firstName"));
firstName.tRequired(true);
form.add(firstName);
TextField<Date> dob = new TextField<Date>("dob", new PropertyModel<Date>(model, "birthDate"));
dob.tRequired(true);
form.add(dob);海豚卡通
TextField<String> email = new TextField<String>("email", new PropertyModel<String>(model, "emailAddress"));
email.tRequired(true);
email.Instance());
form.add(email);
add(form);
add(new FeedbackPanel("feedback"));
}
}
在代码⽰例中,您已经看到了多次提到模型的信息,前⾯将它们描述为“⽤于检索或设置数据的包装器”。 那么,什么是模型,为什么它如此重要? 如前所述,Wicket将整个组件层次结构(页⾯和添加到其中的所有组件以及添加到这些组件的组件等等)存储在内存中,然后存储到磁盘中,以便您有状
态地⼯作通过⽆状态HTTP使⽤Java对象。 但是显⽰这些数据的这些组件不⼀定需要在两次请求之间存储数据本⾝。 他们只需要存储状态-可能是⼀个标志,指⽰它们是可见还是不可见,已启⽤或已禁⽤,已扩展还是已折叠。 他们所显⽰的实际数据很可能已经保存在其他位置(可能在数据库中),这意味着我们实际上不需要将其保存到⽤户的HTTP会话中。 这是模型存在的原因之⼀–减少了保持组件层次结构所需的内存量。
二年级语文造句同样重要的第⼆个原因是从组件本⾝提取数据的检索和存储。考虑上⾯的表单⽰例中的简单TextField组件。它不需要知道它正在编辑的字符串是来⾃会话,来⾃Web服务还是来⾃POJO上的字段。它仅需要能够从某处获取字符串并在某处设置编辑后的字符串。该IModel接⼝有两个⽅法本-的getObject和⽅法tObject-这允许它能够做到这⼀点。在开始使⽤Wicket之前,您还应该查看有关它们的 。
每个Wicket程序员应该知道的⼗件事
1. 模型 –在⽤Wicket编写任何⼤型应⽤程序之前,请务必确保您了解模型。
2. 拉动,不要推 -通常,您的组件应该能够拉动其状态,⽽不是让其他组件将状态推到其上。下⾯的⽰例演⽰了这意味着什么以及
为什么如此重要。关键是 构造函数只运⾏⼀次 ,但是 构造函数运⾏后 ,页⾯上的许多内容可能会更
改状态。如果将状态推⼊构造函数中的组件,则⼀旦另⼀个组件更改了状态,它们就会过时。唯⼀的选择是,每个组件都必须知道共享状态的所有其他组件–这不是“ Wicket⽅法”。您的组件应独⽴且可重⽤。
3. Wicket是⼀个UI框架 –不要在Wicket中编写中间层或持久性代码。
4. 保持标记和代码整洁,紧凑 –熟悉并使⽤Wicket的可重⽤性的各个⽅⾯–⾯板,标记继承和⽚段为例。
5. 匹配您的层次结构 -完全不需要您花费太多时间来学习该规则,但是标记中组件的层次结构(带有wicket:id属性的标签)必须与