JSF 2.0阅读笔记:视图状态 (二)

(续一)

[b]三、可能的优化方案[/b]

撇开JSF2.0不谈,不妨先来想想针对JSF1.2的问题,有哪些可能的优化方案?

1. [u]组件的底层实现应改用可统一管理的,按名访问的,动态的弱类型属性集合。[/u]说白了,就是在组件类中用一个统一的Map来管理组件属性值。相对于强类型对象域,这种方法虽然在理论上会导致属性访问性能降低,但同时会给采集组件状态的性能带来提升,因为现在只需要采集被显式设置过的组件属性,而不是组件上的所有属性。进而,对后续的编码/解码速度,视图状态大小等等性能参数带来提升。并且,可以对saveState与restoreState的逻辑进行抽象,提升到继承层次更高的基类中甚至API中实现,而不需每个具体的组件类自行去覆盖。

2. API支持与业务逻辑无关的视图声明语言(例如Facelets),JSF引擎通过视图定义语言可随时获取到视图的初始状态。基于初始状态,对需要实际保存的视图状态进行优化。优化的方案有很多种,包括:

a. [b]增量视图状态。[/b]视图状态中仅保存当前组件树状态与初始状态的差异信息。在恢复组件树时,先通过视图声明语言获得视图初始状态,再根据视图状态中的差异信息进行调整,最终获得实际的当前组件树状态。这种方案的优点是在保持对应用开发者隐藏视图状态概念的同时,大幅降低了仅对组件树作少量局部修改的场景下的视图状态尺寸。对于动态性较强的页面,例如页面上仅放一个f:view,整棵组件树都是在运行期动态生成的场景,则提升不大。

b. [b]局部视图状态。[/b]视图状态中仅补存当前组件树状态的局部信息。在恢复组件树时,先通过视图声明语言获得视图初始状态,在根据视图状态中的局部信息进行调整,最终获得实际的当前组件树状态。这种方案需要把视图状态的概念暴露给应用开发者,应用开发者需要通过某种方式在组件树中指定需要保存的部分,并且必须清楚认识到,对于不保存的部分,每次请求开始时都会恢复为初始状态,在请求后续处理中所作的修改不能保留到下一请求中。此外,对于某些功能复杂的组件,很可能存在一些属性值是必须跨请求保持的,否则将导致其无法正常工作,例如说分页组件的当前页数。对于这些必须保存的状态,引擎需要提供机制上的支持,并且提供接口让组件开发者能方便地定义这些必须保存的属性。显然,这种方案会侵入组件开发者和应用开发者现有的编程模型,迫使他们必须关注视图状态的概念。但是,优势也是明显的,应用开发者拥有了对视图状态的完全控制能力。在大多数视图状态不会发生改变场景中,他可以指定根本不保存视图状态。

c. [b]分离视图状态。[/b]前面说过,JSF1.2支持让开发者选择把视图状态保存在服务器端还是客户端。在实际应用中,这种非黑即白的二分法只会让开发者顾此失彼,要么完全牺牲内存,要么完全牺牲带宽。但话又说回来,这种“以带宽换内存”的思路大体上是合理的,前提是,在换的比例上应能平滑过渡,而不是非此即彼。比如说,对于带宽吃紧的环境,应允许配置为“最多允许100k的视图状态传递到客户端,超出的保存在服务器端session中”,或者“40%的视图状态传递到客户端,60%保存在服务器端session中”等等。

需要注意的是,以上的a、b、c三种方案,并不是互斥的,完全可以结合起来使用。例如可以有局部增量视图状态,也可以把增量视图状态分离保存。

[b]四、JSF2.0中的视图状态[/b]

现在开始进入正题(传说中的三纸无驴?),JSF2.0提供了怎样的视图状态方案。

简单来说,JSF2.0提供了上面优化方案中的1和2a,也就是统一的弱类型属性集合,和增量视图状态。但其中增量视图状态在规范中被称为PartialState(局部状态),为了避免歧义,下文中仍然称其为增量视图状态。

结合JSF 2.0的API与参考实现(Mojarra项目)来看,与视图状态相关的类关系大致如下:

[img]http://dl.iteye.com/upload/attachment/376763/03ca9d6c-ba3c-3bbc-a45d-242581bb097f.png[/img]

其中,绿色为API中的接口和基类,黄色为RI中的实现类。

图中可以看出JSF 2.0中视图状态处理的主线流程。前面提到过,StateManager是视图状态处理的主入口。在图中可以看出,RI中有两个时机触发生成视图状态信息。一是在Form组件的渲染器中,调用ViewHandler的writeState方法,该方法则负责调用StateManager的writeState方法进入生产视图状态的流程。当页面刷新时,走这条路径。二是在PartialViewContext的processPartial方法中,直接调用StateManager的getViewState方法获取已编码的视图状态信息字符串。当渲染Ajax响应时,走这条路径。

StateManager的实现类主要负责视图状态的收集和恢复过程。针对生产视图状态,StateManager提供了writeState与getViewState两个接口方法,其中writeState负责在收集好当前组件树状态信息后,调用ResponseStateManager进行编码和输出,而getViewState方法则只以字符串方式返回编码后的视图状态信息,并不实际向响应流中输出。

在JSF2.0之前的版本,收集和恢复的逻辑直接由StateManager的实现类负责。JSF2.0中加入了VDL的概念,因此在这里把收集和恢复代理给当前ViewDeclarationLanguage所持有的StateManagementStrategy实例来完成。换句话说,通常情况下在JSF2.0中视图状态的收集和恢复逻辑的入口,在StateManagementStrategy的实现类中;编码和解码逻辑的入口,在ResponseStateManager的实现类中。

StateManagementStrategy提供了两个抽象方法:

public abstract class StateManagementStrategy {

public abstract Object saveView(FacesContext context);

public abstract UIViewRoot restoreView(FacesContext context, String viewId,
String renderKitId);
}


其中saveView方法的实现负责遍历组件树。把组件树的当前状态收集到一个可序列化数据结构中返回。在JSF2.0之前版本,API上硬编码了该结构至少应包括组件树结构(TreeStructure),组件属性(Attributes),与附加对象(AttachedObject)三部分。从JSF2.0版本开始,在API代码层面上去除了对该数据结构的限制。

restoreView方法则负责从视图状态中恢复出组件树。对于这个方法,API通过javadoc详细指明了实现步骤。实现类应先从页面(markup)中构造出原始的组件树,对于自动生成的组件id,必须与首次请求时所生成的id一致。然后调用ResponseStateManager上的getState方法(解码)获取到与上次请求中调用saveView方法所返回的数据结构一致的视图状态信息。最后,根据这个视图状态信息对先前构造的原始组件树进行调整,最终得出实际的组件树,返回其根节点。

从这里的javadoc可以看出,JSF2.0 API所推荐的视图状态方案,就是增量方案。顺带一提,我个人觉得在API的javadoc上写这种实现细节并不妥当,难道别人的实现不用增量视图状态就不符合规范了吗?说不定有比增量方案更好的呢?

以上是处理视图状态的主线流程,下面具体来看看JSF2.0是如何实现弱类型属性集合和增量视图状态的。

4.1. 弱类型属性集合

从上面的类图中的“组件状态关系结构”部分可以看出,所有组件(UIComponent是所有组件的公共基类)都实现了PartialStateHolder接口,而PartialStateHolder又继承自StateHolder接口。这个StateHolder是什么?简单来说,就是希望在JSF视图状态中保存自身状态,并且希望控制自身状态的采集和恢复过程的类必须实现的接口。StateHolder提供了两个经典接口方法:

public Object saveState(FacesContext context);

public void restoreState(FacesContext context, Object state);


StateManagementStrategy在收集视图状态时,事实上是通过遍历组件树,调用各个组件上的saveState方法来获取组件的当前状态的。因此组件可以覆盖此方法,控制自身的状态应包括哪些属性值和保存的方式。restoreState则是saveState的逆操作,负责根据上一次调用saveState所收集的组件状态数据,来恢复组件的属性值。

StateHolder对于组件开发者来说是一个非常重要的接口,除了组件以外,任何希望在视图状态中保存的附加对象,都可以考虑实现该接口,例如Validator等等。JSF规范中规定(Sec7.7.1)

[quote]Validators, Converters, FacesListeners, and other objects attached to a component. The manner in which these attached objects are saved is up to the component implementation. For attached objects that may have state, the StateHolder interface (see Section 3.2.4 “StateHolder”) is provided to allow these objects to preserve their own attributes and properties. If an attached object does not implement StateHolder, but does implement Serializable, it is saved using standard serialization. Attached objects that do not implement either StateHolder or Serializable must have a public, zero-arg constructor, and will be restored only to their initial, default object state.[/quote]

简单来说,就是任何一个附加到组件上对象,在保存/恢复状态时,优先考虑是否实现StateHolder接口,如是则调用saveState/restoreState来处理。否则,看是否可序列化,如是则通过序列化方式保存。如果均没有,则只调用其默认构造器,构造出一个初始状态的对象。

从类图中可以看到,UIComponent持有另一个StateHolder的子接口,StateHelper。而且API提供了该接口的默认实现:ComponentStateHelper。这个就是前面所说的弱类型属性集合了。

(待续)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值