前一段时间我写过一篇
共享我在项目中使用jsf的一些经验,主要是概要的提出了一些jsf使用上的建议,这次我想在文章里主要是把seam在jsf中的使用经验提一下,能让更多的人了解seam的实际应用和优势。
1.seam配置时要注意的地方:
(1)faces-config.xml里面要加入一个seam的阶段监听:
<lifecycle>
<phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
<!-- <phase-listener>com.future.egov.jsf.ext.event.DebugPhaseListener</phase-listener> -->
</lifecycle>
seam动起来的条件就是从这里发起的,seam通过这个监听器对jsf的各个阶段进行必要的增强以及植入自己的CONVERSATION生命周期,对于这个监听器的具体细节工作,我还需要更多时间研究,仍在了解中!
(2) 一定要在工程类路径的根下放置一个seam.properties文件,你可以设置为空内容,主要是引导seam在初始化的时候加载这个路径下所有标注为seam组件的对象(通过@Name注释),
http://www.ibm.com/developerworks/cn/java/j-seam1/ seam无缝集成jsf给我了提示!
(3)web.xml下最小配置是加入seam监听器
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
在容器加载工程的时候,初始化seam框架。
以上三处的配置,你就可以在任何容器中使用seam了!更多的配置大家可以找参考了解吧,我目前在项目中就使用了以上配置。其他配置主要是在seam对ajax,ejb3的支持上,不过seam很新,什么事都会发生!
2.常用的注释:(所有被seam定义的领域对象都可以认为是seam组件)
(1)@Name(XXX),需要在你的领域类上定义,定义了seam组件的名称,对于jsf来说就是backing-bean,也就是说你不用在faces-config中配置managedbean了!
(2)@Scope(ScopeType.XXX),可以在你的领域类上定义,表示这个被定义的seam组件在什么上下文中,jsf中主要包括page,event,session,application,conversation这些Scope。我在项目中主要使用event,session,conversation。event就是把组件放入了request中,session同理,conversation是seam独创的声命周期,conversation短声命周期类似request,但是会保存一些jsf容易在请求中丢失的数据(jsf只是保存组件,不保存组件渲染的数据,除非是EditableValueHolder组件,有时候需要通过myfaces的save组件和updateActionListener组件来恢复这些数据),具体保存细节,需要看使用的情况,我有这样一个经验:当定义成event上下文,在页面的一次请求中,有些数据请求时还在,但是到渲染时就不见了,常见在dataTable组件,myfaces的commandNavigation组件,但是换成conversation上下文,这些数据在渲染时又找回来了。但是对于跨度比较大层面,我还是推荐使用myfaces提供的保持机制,我一般使用updateActionListener,而save组件在seam1.2.1的环境下会出错。如果再有更大跨度,就可以使用conversation上下文的长会话了。
(3)@Begin(join=true),@End(beforeRedirect=true),当触发了带有@Begin标记的方法,conversation的长会话就这样开始了,主要是为了长时间使用已经加载到conversation域中的对象或者属性(如果定义了conversation但并没有加载的可不算):join=true就是告诉你会话中有同名值时继续赋值,还有一个注释参数ifOutcome=XXX,就是看你的方法返回的字符串是否和ifOutcome定义的字符串相匹配,如果匹配就开始长会话。当在长会话期间执行到某个方法带有@End标记那么这个长会话就会结束,这样防止了内存泄漏问题,我认为这是一个权衡的结果,也许用户真的会点击那个带有结束标记方法的按钮。beforeRedirect为真就会在结束时清掉conversation上下文所有的信息,如果beforeRedirect=false,conversation只是变成短会话,在结束后的那次请求中还可以使用conversation中的数据,一般会用在messages提示这个应用场景中使用,但是如果要返回数据列表有时就需要清空所有数据,防止数据列表还会重现长会话开始前的情况。
(4)In(value=XXX,rquest=false,ScopeType=XXX),Out(value=XXX,rquest=false,ScopeType=XXX) seam把它定义为双射。In是最常用的标注,你可以使用In导入一个jsf的EL变量来获取jsf模型,例如我要获取spring的业务bean,而且业务bean已经定义成backing bean,利用spring与jsf集成的方法:
<!-- Managed Beans for options.jsp -->
<application>
<variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver
</variable-resolver>
<locale-config>
<default-locale>gbk</default-locale>
</locale-config>
<message-bundle>resources/MessageBundle</message-bundle>
<!-- <view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler
</view-handler> -->
</application>
在seam组件中这样声明:
@In(value="#{userService}", request=false)
private UserService userService;
这个示例为seam组件注入了由spring管理的用户服务对象,它的value是从jsf EL变量中获取,request=false在告诉seam,如果当前的值没有找到,那么设置为空,否者当出现没有找到的情况,seam会抛出空异常。
@Out属性主要是把处理过的属性值会由seam重新再付给上下文也就是Out中所定义的ScopeType上下文,我认为虽然是seam的一个特点,但是在我的应用中不多,主要是注入而非双射!如果它真的能在短期Conversation中有所作用来代替Myfaces的数据保持机制,我想会好些,我目前只是在长Conversation有所应用。
3 .@Factory,@DataModelSelection,@DataModel,它们主要来代替数据列表的使用,主要是减少了代码量,Factory是在请求值阶段就对需要实例化的对象进行创建,DataModelSelection定义的属性,可以透明的抓取数据列表选择的单行数据,DataModel属性减少了不必要的get,set。然而我在实际的使用中由于很多不定的情况,大部分的使用上又回到jsf标准的get方式。 这种开发方式我认为seam的目的是想屏蔽与页面不必要的关系细节,让开发只需要重视真正的业务,是一个标准的面向对象式结构,当jsf的体系结构的不断优化,类似这种开发方式我想会越来越有用。
4. @RequestParameter是个很有用的注释,它自动把当前属性和页面同名的request提交值绑定在一起,虽然这样使用违背了jsf所追求的面向对象化,http透明化,但是实际开发中会后很多意想不到的情况,有时候在集成式页面这样的做法会很有用,当你的页面中不仅仅有jsf标签就清楚了!
seam的其他方面问题我会抽空整理一下,seam目前也是在不断更新当中,明年出台的webBeans规范的前身就是seam,其实我更关注的是seam在整个j2ee体系中的角色,它到底是想替代struts的application?还是想替代spring的manager?也许有更多的想法!