人有时间喜欢怀旧,既浪漫而且对将来很有指导意义。想来在国内早期的电子商务时期,8848非常火爆、卓越刚刚崛起之际,网络购物这个新潮淘金行业最吸引年青人的眼球。在网上冲浪的同时还能买到便宜实惠的商品,尤其是当时国内市场买不到的舶来品。
当年我也经常浪迹于8840和卓越网,一次偶然的机会发生了一个必然的趣事:在网站购买崭新的刻录光驱时,通过购物网站提供的工具栏把选好的刻录机装进购物篮后。在保持当前网页打开的状态下,我又打开了另一个网页开始在网站上闲逛,又选了几张音乐CD之后提交订单前自己计算了一下发现钱不够了,只得放弃了那几张经典CD,然后含泪提交订单,结果太雷人了!之前选择的刻录机赫然出现在选购商品清单中,俺的钱还是不够!
那时还不知道是 session捣得鬼,只是心中暗骂了一下网站开发者,怎么开发这么摧残人的程序!现在想来已经明白了原来开发者用了同一个session存储购物,而我在原来页面的基础上新开窗口的话还是使用同一个session,这就造成了俺的购物悲剧。从此之后每每购物时都小心翼翼地使用单页面了,直到前几年才发现这个bug已经灰飞烟灭了!感谢那些伟大的程序员!
你想知道他们怎么做到的么?那我就结合最近阅读的《Seam in action》谈一下。
如果具有数据库常识的话,你一定了解数据库事务(Transation)。而Seam的Conversation和Transation有异曲同工之妙:
打个形象地比喻,Conversation就像是我们每日上下班又恨又爱的地铁,它把乘客从起始地一直运送至目的站,而其间路经的各站可以看作是页面的不断跳转、一次次request请求。而我们传统的开发模式可比喻为一位拉肚子的生病乘客,每到一站,他都要跑下车冲进厕所,而痛快一下之后回到站台等待下一趟地跌,然后乘坐这辆地铁去往下一站,继续此循环。
从这个角度来讲,Conversation更加匹配用户需求,而且大大降低了由于临时数据存储而对数据库进行反复操作的压力。
提到Conversation的工作原理,当然不能离开Conversation Context!Conversation Context是Seam引入的两个Context之一,它和Servlet生命周期正相反,它服务于业务领域的时间框架。
Conversation Context逃出了HTTP Session的狱垒,在空旷的大地上狂啸着:“我自由了!”。这场景让我想起了电影《肖申克的救赎》。与《肖申克的救赎》中的主角一样的聪明精干,Conversation Context把Session分割为n等份的存储相互独立的内存片段,这些内存片段被称为“Workspace”。
Seam把Conversation Context作为其上下文变量的工作区域。虽然Conversation Context使用了Session进行谈话数据的存储,但并不会重蹈前面提到Session问题的覆辙。
首先,conversation的生命期按照分钟计算,而Session的生命期却大都以小时计算。造成这个不同的原因是蜗居于Session中的 Conversation具有自己的生命期。每个Conversation都具有自己的超时期限,这个期限的默认值取自全局超时设置。另外,并发的 Conversation也是相互保持独立的,而不像Session的属性那样脆弱地易于覆盖。
由于conversation存储在Session,有两个必要的条件要提一下:
每个Conversation有一套清晰的生命周期边界,此边界与对应的用户case一致。当用户触发条件开始进入Conversation时,一个新的 HTTP Session区域被分配给此Conversation。同时Conversation Id这个唯一标识也被生成并与分配好的Session区域相关联。接着,Conversation Id被作为参数传递给下一个request,而参数的形式不止一种:隐藏form field、JSF视图根属性。幸运的是,Conversation Id的传递是由Seam全权管理的,对于开发者和用户来讲由于水晶般透明。由于Conversation Id会和Session token一起发送给Server,因此Conversation就建立起了与Session、request之间的联系。Seam在接收到 request后,取出其中的Conversation Id,并根据后者方便地将Conversation从Session这个大抽屉中取出使用。
Conversation Context的确是在整个Conversation旅行过程中放置数据的一次性安全保险箱!它利用了Session的存储复杂对象的能力而又具有独立的工作空间和特有的生命期,不会造成内存泄漏或并发问题。
Conversation 通常有两种状态:临时(temporary)、长期运行(long-running)。当然还有第三种不大常用的状态:内嵌(nested),它就像寄生虫一样内嵌在长期运行的Conversation中。而状态之间的切换通常称为Conversation传递(Conversation Propagation)。我们可以通过设置Conversation传递指令来设置Conversation的边界。但我们不能够人工初始化、撤销 Conversation,而可以通过玩转盘游戏一般的不断地转换Conversation状态。
大多数情况,当人们谈起Seam的Conversation时,他们提及的是长期运行的Conversation。一个长期运行的 Conversation跨越了一系列request、通过传输Conversation Id来保持行动力,就像用Conversation Id作为一条丝线把N个闪亮的request穿在一起那样。与临时的Conversation相比,它很像一个签了合同(Conversation Id)的合约职员。
如果没有使用Conversation传递指令设置为长期运行的话,Seam将自动创建一个临时Conversation来为当前request服务。这个临时的Conversation就像一个临时工,它紧跟着JSF生命周期的恢复视图阶段进入工作状态(马上被初始化),在SF的渲染响应阶段之后被销毁。这位临时工的工作时间只在JSF生命周期之内。
如果被恢复的Conversation是无效的或者早已超时,那么用户将被提示并转发到在Seam中预先好的fallback页面。
分类: seam Tag: seam 技术 j2ee conversation session cleverpig 发表于 16:13:01 | 阅读全文 | 评论 0 | 编辑 | 分享 0
说起Seam2的另类,并不言过其实,因为它身为JSF与J2EE之间的“粘合剂”,当然要有所作为。而由于貌似尴尬地处于汉堡包肉饼的位置,这些行为有点古怪,也不难理解。通过我阅读《Seam2 in action》感觉Seam2浩如烟海般的组件库,初次见到让人直接晕掉,想必也是不少初学者碰壁之处。在我撞得头破血流之后,发现Seam2体现了“大道至简”的原则,在这里简单精炼地说一下体会,以供和大家交流。
四个周期:Servlet上下文的生命周期、Request的生命周期、JSF生命周期、Seam生命周期。
四个周期范围包含关系:Servlet上下文>Request>Seam>JSF
Seam作为JSF与J2EE间的“焊接机”,它通过一系列的基础设施完成“热焊接”:
Servlet 生命周期Lisener。它在l中配置为应用初始化启动的Servlet,只要应用启动它就会得到通知。Seam使用这个生命周期事件来自引导。每次Seam被调用时,它开始扫描组件所在的classpath。这个组件扫描器将组件的定义放置在Seam容器中。任何被标记为 application-scoped启动的组件(例如:使用@Startup和@Scope(ScopeType.APPLICATION)的组件)将被自动初始化。这种启动组件常用于执行引导逻辑,比如更新数据库或注册模块。
SeamListener同时也捕捉新HTTP session开始时的通知,并初始化在此session范围内的启动组件(例如:使用@Startup和 @Scope(ScopeType.SESSION)的组件)。其它所有组件在处理HTTP请求的过程中按需被初始化。
Seam要和JSF一起工作,当然就缺不了这个Servlet类,在处理JSF请求过程中和Seam相关的工作都在这里进行。FaceServlet的配置文件中定义的映射模板是*.seam,即此类负责处理以.seam为结尾的请求。
Seam 开发团队力荐开发者使用Facelet来代替传统的JSP作为JSF视图处理器。JSF和JSP存在一个令开发者超不爽的错配。JSP的目的是来生产动态数据,而JSF组件tag则企图生成一个具有自渲染能力的UI组件模式。这在runtime时会造成完全不同的目标冲突。而Facelet是一个轻量级、基于XML的视图技术,它通过解析XML文档单独生成JSF UI组件树。它提供组件tag,这些tag可以被本地转换为UI组件,并把那些非JSF标签(包括EL)封装到JSF text组件中。因此,来自JSP tag层的需求,以及JSP编译的整体消耗,都可以解决。
Facelet处理流程,可以比喻为“一场精彩的4X1接力赛”:
JSF 规范中没有提供如何推送如图片、CSS、JavaScript这类支持性资源给浏览器的指导。最常见的解决方式是使用自定义的JSF时期Listener 将这些请求匹配相应的路径。而这样却搅乱了JSF生命周期,Seam使用自定义Servlet处理这些资源,通过回避生命周期从而避免不必要的麻烦。使用 SeamResourceServlet是非常合理的,因为在处理资源过程中的调用步骤和那些处理应用页面截然不同,前者消除了复杂生命周期的需要。
Serlvet 过滤器把整个request的处理过程包裹得严严实实,可以在处理请求之前和处理请求之后执行逻辑。Seam使用单独一个SeamFilter类来封装 JSF Servlet,以使其在JSF生命周期之外“节外生枝”或者是处理JSF没有捕捉的请求。但这个过滤器并不限于处理JSF请求。它处置所有的请求,也允许非JSF请求访问Seam容器。Seam可以不依赖于过滤器工作,但这些被安装的过滤器对Seam额外添加的服务很有用处。
尽管只是安装了一个独立的过滤器,但Seam实际上使用此过滤器像传送带一般带动着一整条内建Filter反应链,通过委派将请求调配给相应安装在Seam容器内部的过滤器。这种委派模式使l的配置实现最小化。当SeamFilter被安装好后,剩下的Seam内建Filter配置就可在Seam组件描述符(/WEB-INF/components.
xml)中完成了!
这是将Seam打入JSF生命周期的“隐形侦察机”。在Seam 2.0,Seam phase listener被声明在facesconfig.
xml文件中,这个文件被包含在核心Seam JAR文件——jboss-seam.jar中。因此,只要你包含此JAR文件在你的应用里面,这个phase listener就有效。
和Spring的注入依赖(DI)类似,Seam2也有此类东东,但更加方便:
双向注入(Bijection):它是向内注入(injection)和向外注入(outjection)的合成物。Bijection通过一个方法拦截器管理。injection发生在组件方法调用之前,outjection发生在调用完成时。可以认为上下文(context)变量参与这个交互过程。 injection从Seam容器提取context变量,然后赋值给相应组件实例的属性;而outjection把组件实例的属性值“推”到相应 context变量中。
本文发布于:2024-01-29 09:20:20,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170649122314272.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |