Wicket模型的干净方法

Apache Wicket Web框架的核心概念之一是模型和IModel作为其编程接口。 Wicket组件严重依赖模型,这使它们成为体系结构的重要组成部分。 Apache Wicket是一个有状态框架,将页面及其组件存储到通常位于HTTP会话中的页面存储中。 组件根据模型的内容创建面向公众的视图。 错误使用模型可能会导致尴尬的副作用,例如页面无法更新其内容或应用程序占用大量内存。 从我所看到的情况来看,新的Wicket用户遇到的最多问题是正确使用模型。 在这篇博客文章中,我将尝试解释在不同的用例中应该如何以及使用哪种模型。

Wicket IModel实现中最基本的是Model类。 它基本上是模型内容的简单占位符。 这非常适合模型内容不占用太多内存且更像是常量的情况。 一个简单的String可能是Model内容的理想选择。

IModel<String> name = new Model<String>("John");

您必须了解,即使创建包含模型的页面,创建模型对象时其内容也将保持不变。 此行为是由于使用静态模型引起的。 每次从模型询问值时,动态模型都可以更改实际内容。 新的Wicket用户经常使用静态模型而不是动态模型。 如果您不清楚静态模型和动态模型的概念,则应阅读《 Wicket in Action 》一书中的第4章“了解模型” 。

IModel<Date> date = new Model<Date>() {@Overridepublic Date getObject() {return new Date();}
};

通过覆盖getObject()方法,可以从Model创建上述动态日期模型为匿名类。 模型将始终返回包含当前时间的Date的新副本。

Wicket是有状态的Web框架,模型通常存储在用户的HTTP会话中。 模型中的数据很可能是从数据库或通过某些Web服务获取的。 如果使用静态模型方法,则很快将消耗大量内存,并且如果重新加载页面,将无法从数据源获得全新视图。 如果我们决定像对Date对象那样使用动态模型,则最终可能会在一页加载中进行大量数据库搜索或Web服务调用。 匿名内部类也不是很好看的代码。 Wicket有一个针对此问题的内置解决方案,LoadableDetachableModel可以缓存模型的内容,直到可以安全丢弃为止,从而提供了有效但仍动态的模型。

public class PersonModel extends LoadableDetachableModel<Person> {private Long id;private PersonService personService;public PersonModel(final Long id, final PersonService personService) {super();this.id = id;this.personService = personService;}public PersonModel(final Person person, final PersonService personService) {super(person);this.id = person.getId();this.personService = personService;}@Overrideprotected Person load() {return personService.findById(id);}
}

上面的示例构造了一个可加载和可拆卸的模型,用于通过其唯一标识符加载一个人。 该模型有两个构造函数,这是有原因的。 之一来创建在拆卸状态下的延迟加载模式,将加载内容当getObject()方法被调用第一次,和一个以在安装状态的模型,不需要到PersonService呼叫时的getObject()方法是叫。 最好为所有LoadableDetachableModels提供这两个构造函数。 当您已经拥有内容时,可以在附加状态下创建模型,或者在只有标识符可用时以分离状态创建模型。

可装载可分离模型的警告是模型的私有字段。 Wicket将模型以及组件存储在页面存储中,这可能需要对模型进行序列化。 当模型被序列化时,私有字段也被序列化(实际内容被分离)。 我们的id字段不是问题,因为它是可序列化的,但是PersonService可能是一个问题。 通常,服务层的接口默认情况下是不可序列化的。 至少有两个不错的解决方案:使服务可序列化,或者更好的方法是将服务包装在可序列化的代理中。 代理行为可以例如通过与不同的依赖项注入框架(例如)集成来实现。 春天(wicker-spring)或Guice(wicket-guice)。 集成模块确保在注入服务代理时将它们可序列化。

public class ProfilePage extends WebPage {@SpringBeanprivate PersonService personService;public ProfilePage(final PageParameters parameters) {super(parameters);Long id = parameters.get("id").toLongObject();IModel<Person> personModel =new PersonModel(id, personService);add(new ProfilePanel("profilePanel", personModel));}
}

上面的示例使用了wicket-spring集成,将人员服务注入到所需的页面。 @SpringBean批注提供了一个可序列化的代理,因此,当我们创建人员模型时,我们不必担心服务的序列化。 Wicket不允许构造函数注入,因为当我们调用super()构造函数时,所有注入魔术实际上都会发生。 这意味着我们在Component的基本构造函数完成之后初始化注入的值。 只需记住对数据使用可序列化的标识符或定位符,对服务使用可序列化的代理。

通常,在MVC Web框架中,视图层使用某种数据传输对象来构建视图。 DTO组成并继承以创建不同类型的视图。 为这些对象建立工厂或映射器可能容易出错或令人沮丧。 Wicket针对此问题提供了不同的解决方案。 在Wicket中,您可以认为IModel接口的工作方式类似于关系数据库视图,允许您以不同方式显示域的所需部分。

public class PersonFriendsModel extends AbstractReadOnlyModel<String> {private IModel<Person> personModel;public PersonFriendsModel(final IModel<Person> personModel) {super();this.personModel = personModel;}@Overridepublic String getObject() {Person person = personModel.getObject();Iterable<String> friendNames =Iterables.transform(person.getFriends(),new PersonNameFunction());return person.getName()+ "'s friends are "+ Joiner.on(", ").join(friendNames);}@Overridepublic void detach() {personModel.detach();}private class PersonNameFunction implements Function<Person, String> {public String apply(final Person input) {return input.getName();}}
}

在这里,我们建立一个模型,该模型可以构成我们先前创建的PersonModel。 我们将其用作来源,以建立该人的好友列表。 我们正在扩展的模型是Wicket提供给我们的AbstractReadOnlyModel。 它基本上是普通模型,但不允许设置模型内容。 这非常有道理,因为我们正在构建一个联接的String,并且解析一个相似的列表并从该列表中设置该人的朋友会很尴尬。 我们仅将此模型用于只读目的,以在视图中显示朋友列表。 您可以在此处看到视图的类比,我们仍在使用相同的Person域模型,但是在模型的帮助下公开了自定义视图。 请注意,我们将detach方法委托给了组合模型。 这可以确保,如果我们在任何组件中都没有直接引用组成的模型,则仍然可以将其与好友模型分离。 多次调用detach()方法无害,因此即使从多个组件中使用也很安全。

我们仅创建了一个简单的模型合成示例。 您应该探索Wicket的内置模型实现,并花一些时间来研究如何使用它们从领域模型中创建合理的模型。 只需记住一件事:组成和扩展模型,而不是域对象。

我最后要谈的是属性模型。 它们在许多Wicket应用程序中得到了广泛使用,甚至《 Wicket in Action》一书也鼓励使用它们,但是它们具有一些不需要的功能。 属性模型的代码编写速度快且易于使用,但是它们使您的代码成为“字符串类型”。 这是我们不希望使用Java这样的类型安全语言的东西。

PropertyModel<String> nameModel =new PropertyModel<String>(personModel, "name");

我们使用PropertyModel从人的名字创建一个模型。 名称模型可以使用直接的Person对象或为我们提供人物的IModel。 可以通过实现Wicket的IChainingModel接口来实现此功能。 我们这里有两个问题。 第一个是名称模型的类型。 我们定义名称必须为String,但是实际上编译器无法确保该名称已绑定到String getName() JavaBean属性。 第二个问题来自重构,我们使用String值定义属性名称。 如果使用IDE重构Person对象,并将getName()方法挂接getLastName() ,则应用程序将损坏 ,编译器将再次无法注意到这一点。

让我们在这里停留片刻,看看IChainingModel接口。 其主要目的是允许使用普通对象或链接模型作为模型的内容。 PropertyModel可以与提供人物的模型或普通人物对象一起使用。 如果查看PropertyModel的源代码,则会注意到实现IChainingModel需要转换,而我认为还需要样板代码。 可以重构我们先前创建的PersonFriendsModel,以实现IChainingModel,这样,与其仅采用模型作为内容,还可以直接采用人员。这真的必要吗? 并不是的。 如果我们要使用普通人,则可以从该人创建一个新的静态模型,从而为我们提供与IChainingModel提供的功能相同的功能。

CompoundPropertyModel为模型处理增加了更多的魔力。 它是许多组件的根模型,可以通过将属性名称与组件ID匹配来自动绑定到该组件。

public class PersonForm extends Form<Person> {public PersonForm(final String id, final IModel<Person> personModel) {super(id, new CompoundPropertyModel<Person>(personModel));add(new TextField<String>("name"));add(new TextField<Integer>("age"));}
}

在这里,我们创建一个表单以显示该人的姓名和年龄的输入字段。 这两个文本字段均未附加任何模型,但是我们仍然能够将“名称”部分绑定到该人的姓名,并将“年龄”部分绑定到该人的年龄。 我们在构造函数中创建的复合属性模型是表单的根模型,并通过组件ID将表单组件自动绑定到对象的属性。 复合属性模型和属性模型都存在相同的问题,但我们甚至还要添加一个。 现在,我们将组件ID直接锁定为属性名称。 这意味着我们具有从HTML模板到域对象属性名称的直接依赖关系。 这听起来不太合理。

如果要使用属性模型,则取决于您,但是在我看来,由于前面所述的问题,应避免使用它们。 有诸如bindgen-wicket之类的项目尝试在不损失类型安全性的情况下实现属性模型的行为。 但是,即使bindgen在重构中也不能很好地起作用。 我们如何以类型实现名称模型并以安全的方式重构呢?

public class NameModel implements IModel<String> {private IModel<Person> personModel;public NameModel(final IModel<Person> personModel) {this.personModel = personModel;}@Overridepublic String getObject() {return personModel.getObject().getName();}@Overridepublic void setObject(String object) {personModel.getObject().setName(object);}@Overridepublic void detach() {personModel.detach();}
}

该模型比属性模型具有更多的行。 是的,的确如此,但是您是否想失去类型安全性和重构功能,并可能很容易破坏应用程序。 该模型位于不同的文件中,因此您仍然可以将其用作一个衬纸,因此,如果使用属性模型或我们刚刚创建的模型,则没有任何区别。 您永远可以记住Java是非常冗长的语言这一事实​​。

Wicket还为我们提供了ResourceModel和StringResourceModel,它们为创建组件的本地化内容提供了强大的工具。 我不会在本文中讨论它们,因为《 Wicket in Action》一书对此有很好的参考。 我试图提出一些现实生活中的示例,说明如何使用不同类型的模型及其目的。 我希望这能给您更多有关Wicket模型以及如何正确使用它们的知识。

参考:来自RAINBOW WORLDS博客的JCG合作伙伴 Tapio Rautonen 对Wicket模型的一种干净方法 。

翻译自: https://www.javacodegeeks.com/2013/09/a-clean-approach-to-wicket-models.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367539.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jquery选择器大全

jQuery 选择器大全选择器 实例 选取 * $("*") 所有元素 #id $("#lastname") id"lastname" 的元素 .class $(".intro") 所有 class"intro" 的元素 element $("p") 所有 <p> 元素 .class.class $(".intr…

第7章 输入/输出系统

总览 I/O系统基本概念 外部设备 输入设备&#xff1a;键盘、鼠标 输出设备&#xff1a;显示器、打印机 外存储器&#xff1a;硬盘存储器、磁盘阵列、光盘存储器 I/O接口 I/O接口的功能和基本结构 I/O端口及其编址 I/O方式 程序查询方式 程序中断方式 中断的基本概念&#xff0c…

python第一个程序HelloWorld

在写第一个python程序之前&#xff0c;我们还需要了解的一个东西就是python解释器 解释器&#xff0c;顾名思义&#xff0c;就是解释一段代码的机器&#xff0c;程序运行的平台&#xff0c;例如Java的解释器就是jdk。 我们在写好的python代码&#xff0c;需要在解释器上执行&am…

Java字符串的十大问题

以下是有关Java字符串的十大常见问题 。 1.如何比较字符串&#xff1f; 使用“ ”还是使用equals&#xff08;&#xff09;&#xff1f; 简而言之&#xff0c;“ ”测试引用是否相等&#xff0c;而equals&#xff08;&#xff09;测试值是否相等。 除非您要检查两个字符串是否…

php原生sql语法,thinkphp执行原生SQL语句的实现方法

怎样在thinkphp里面执行原生的sql语句&#xff1f;$Model new Model();//或者 $Model D(); 或者 $Model M();$sql "select * from order";$voList $Model->query($sql);只是需要new一个空的模型继承Model中的方法。注意query是查功能&#xff0c;execute是增…

Jquery中如何获取元素的文本,值,属性和内容

jQuery DOM 操作jQuery 中非常重要的部分&#xff0c;就是操作 DOM 的能力。jQuery 提供一系列与 DOM 相关的方法&#xff0c;这使访问和操作元素和属性变得很容易。提示&#xff1a;DOM Document Object Model&#xff08;文档对象模型&#xff09;DOM 定义访问 HTML 和 XML …

Python中的__new__()方法的使用

__new__() 函数只能用于从object继承的新式类。 先看下object类中对__new__()方法的定义&#xff1a; class object:staticmethod # known case of __new__def __new__(cls, *more): # known special case of object.__new__""" T.__new__(S, ...) -> a new …

什么是消息中间件

面向消息的中间件&#xff1a;Message-oriented Middleware, MOM 基本功能&#xff1a; 将信息以消息的形式&#xff0c;从一个应用程序传送到另一个或多个应用程序 主要特点&#xff1a; 消息异步接受&#xff0c;类似手机短信的行为&#xff0c;消息发送者不需要等待消息接受…

用Java分割大型XML文件

上周&#xff0c;我被要求用Java编写一些东西&#xff0c;该东西能够将单个30GB XML文件拆分为可配置文件大小的较小部分。 该文件的使用者将是一个中间件应用程序&#xff0c;该应用程序存在XML较大的问题。 在后台&#xff0c;它使用某种DOM解析技术&#xff0c;使它在一段时…

am82.top 1.php,Droppy v2.1.3 – PHP在线网盘系统

更新跨度比较大从1.4.6-2.1.3的更新日志我都贴出来利V2.1.9 (10 July, 2019)- Added option to define maximum upload chunk size in the admin panel- Fixed an issue where downloads were corruptedV2.1.8 (10 July, 2019)- Fixed an issue where upload password field wa…

抛出异常–缓慢而丑陋

这篇文章是关于历史经验以及最近应用的性能优化技术的。 几年前&#xff0c;我在特定的应用程序中发誓&#xff0c;我不得不发现隐藏在真正聪明的工程“技术”之下的无证行为。 它是一个典型的用于发票的单片Java EE应用程序。 最好忘记确切的代码&#xff0c;但是我记得开发人…

信号与线性系统翻转课堂笔记9——傅里叶变换概念

信号与线性系统翻转课堂笔记9——傅里叶变换 The Flipped Classroom9 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&#xff09;…

abstract修饰符

1、一个类不能同时被 abstract 和 final 修饰。 2、如果一个类包含抽象方法&#xff0c;那么该类一定要声明为抽象类&#xff0c;否则将出现编译错误。 3、抽象类可以包含抽象方法和非抽象方法。 4、抽象方法是由子类来实现。 5、抽象方法不能被声明成 final 和 static。 6、任…

埃及分数问题(带乐观估计函数的迭代加深搜索算法-IDA*)

#10022. 「一本通 1.3 练习 1」埃及分数 【题目描述】 在古埃及&#xff0c;人们使用单位分数的和&#xff08;形如 $\dfrac{1}{a}​$​​ 的&#xff0c;$a$ 是自然数&#xff09;表示一切有理数。如&#xff1a;$\dfrac{2}{3} \dfrac{1}{2} \dfrac{1}{6}​$​​&#xff0c…

玩转Jquery中的动画效果(animate方法)

jQuery 动画 - animate() 方法jQuery animate() 方法用于创建自定义动画。语法&#xff1a;$(selector).animate({params},speed,callback);必需的 params 参数定义形成动画的 CSS 属性。可选的 speed 参数规定效果的时长。它可以取以下值&#xff1a;"slow"、"…

matlab 等分矩阵,用matlab根据列拆分矩阵.

使用logical indexingBA(A(:,end)10,:);CA(A(:,end)2,:);回报>> BB 1 4 2 5 102 1 5 6 10>> CC 2 4 5 6 22 3 5 4 2编辑&#xff1a;在回复丹的评论这里是一般情况的扩展e unique(A(:,end));B cell(size(e));for k 1:numel(e)B{k} A(A(:,end)e(k),:);end或者更…

点击网页跟踪php代码的工具,使用ltrace工具跟踪PHP库函数调用的方法

本文实例讲述了使用ltrace工具跟踪PHP库函数调用的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;可能大家已经很熟悉使用strace来跟踪系统调用&#xff0c;今天介绍一个跟踪库函数的利器ltrace比如我有这么一段PHP代码test.php&#xff1a;$y 1380;$arr array…

HDU 2841 Visible Trees(容斥)题解

题意&#xff1a;有一块&#xff08;1,1&#xff09;到&#xff08;m&#xff0c;n&#xff09;的地&#xff0c;从&#xff08;0&#xff0c;0&#xff09;看能看到几块&#xff08;如果两块地到看的地方三点一线&#xff0c;后面的地都看不到&#xff09;。 思路&#xff1a;…

Jquery获取服务器端控件ID的方法

<asp:textbox runat"server" id"txtMessage" textmode"SingleLine">some strings here...</asp:textbox>但是当这段代码输出到客户端时就变成了这样&#xff1a;<input name"ctl00$txtMessage" id"ctl00_txtMessa…

安全地创建和存储密码

几乎每次涉及用户配置文件时&#xff0c;都必须管理用户凭据&#xff0c;从而能够创建和存储用户密码。 通常应该使用散列密码和盐分密码来准备数据库公开和通过使用Rainbow表进行散列反转的密码。 但是&#xff0c;找到以明文形式存储的密码并不少见&#xff08;很不幸&#…