[心缘地方]同学录
首页 | 功能说明 | 站长通知 | 最近更新 | 编码查看转换 | 代码下载 | 常见问题及讨论 | 《深入解析ASP核心技术》 | 王小鸭自动发工资条VBA版
登录系统:用户名: 密码: 如果要讨论问题,请先注册。

[整理]seam学习笔记

上一篇:[备忘]关于WEB代理的一点想法
下一篇:[备忘]北方人才市场地址,报到证

添加日期:2010/8/25 14:27:32 快速返回   返回列表 阅读7447次
JSF生命周期
JSF生命周期将一个servlet请求分为6个阶段:
Restore view
Apply request values
Process validations
Update model values
Invoke application
Render response
每个阶段之前和之后,都会抛出事件。
------------------------------------------------------
JSF可以处理两种请求:初始的请求(就是GET方式)和postback。

GET方式:
 
此时只有两个阶段:Restore view和Render response。
Restore view阶段没做什么,只是创建了一个空的
Render response:此阶段检查URL,得到viewID,定位关联的模板。
    组件被创建,通过encoding每个组件,准备好了response。
    Encoding可以生成标记,如XHTML,
    生成的response包括绑定到Server端组件的元素。

没有Seams时,组件必须在faces-config.xml中定义。

整个reesponse被渲染完毕时,UI组件树被序列化,发送到客户端,或保存在服务端session中。
保存在客户端,需要占用大量带宽,建议保存在服务端。

在web.xml中定义:
<context-param> 
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name> 
  <param-value>server</param-value> 
</context-param> 

PostBack:
验证错误,或conversation错误,或“immediate”事件,或调用FacesContext的renderResponse()方法,控制都会跳到Render Response阶段。
如果调用FacesContext的responseComplete()方法,则Render Response也会被跳过
 
Restore View阶段根据存储的状态恢复组件。
Apply Request Values阶段,捕获form提交的值,写入组件树,寻找触发的时间,将他们排队。
下两个阶段处理大量表单数据,最终注入到对象的属性中。

在Invoke Application阶段,控制转移给application,调用按钮或链接所触发的方法。
这个方法可以称为action handler,另外,还有一些action listener可以被调用。
后者只是简单的执行,不会触发,声明转向。

action handler执行后,根据导航规则决定下一个页面是哪个。
如果action handler返回值是null,或该方法是void,或返回值没有匹配的规则,那么当前页面会再次渲染一次。

反之,转移到对应的页面。
如果存在<redirect/>,那么是redirect过去的(通过GET方式访问),而不是直接渲染该页面(默认是这个)。
<navigation-rule> 
  <from-view-id>/register.xhtml</from-view-id> 
  <navigation-case> 
    <from-action>#{registerAction.register}</from-action> 
    <from-outcome>success</from-outcome> 
    <to-view-id>/welcome.xhtml</to-view-id> 
    <redirect/> 
  </navigation-case> 
</navigation-rule>


JSF生命周期的缺点:
<navigation-rule> 
  <from-view-id>/CourseEdit.xhtml</from-view-id> 
Licensed to Jaroslaw Gilewski <jgilewski@unizeto.pl> 
 
  <navigation-case> 
    <from-action>#{courseHome.update}</from-action> 
    <to-view-id>/Course.xhtml</to-view-id> 
  </navigation-case> 
</navigation-rule>
更新完后,浏览器显示的是Course.xhtml,但是地址栏中仍然是CourseEdit.seam,
这是因为JSF提交到本页面,但是选择了不同的模板显示。

原始的导航规则
只能根据viewID,action handler返回值和EL表达式进行导航,
但是它不能识别context等其他范围的变量。


Seam的页面导向的生命周期
使用pages.xml,在WEB-INF目录下。
也可以拆分为多个,如页面为aa.xhtml,那么对应的配置文件为aa.page.xml。

禁用Seam的导航规则:
WEB-INF/components.xml中添加<web:redirect-filter disabled="true"/>

<page view-id="/FacilityEdit.xhtml"> 
  <navigation from-action="#{facilityHome.persist}"> 
    <rule if-outcome="persisted" 
      if="#{facilityHome.addCourse}">    
     <redirect view-id="/CourseEdit.xhtml"/> 
        <param name="courseFrom" value="Facility"/>             #2 
        <message severity="INFO"> 
          Please enter course information for 
          #{facilityHome.instance.name}.                        #3 
        </message> 
      </redirect> 
    </rule> 
    <rule if-outcome="persisted" 
      if="#{!facilityHome.addCourse}">                          #4 
      <redirect view-id="/Facility.xhtml"/> 
    </rule> 
  </navigation> 
</page> 

Evaluate 计算后面的值,然后去匹配if-outcome,这样就不必管action的返回值了。
<page view-id="/FacilityEdit.xhtml"> 
  <navigation from-action="#{facilityHome.persist}" 
    evaluate="#{facilityHome.lastStateChange}"> 
    <rule if-outcome="persisted" 
      if="#{facilityHome.addCourse}"> 
      ... 
    </rule> 
    <rule if-outcome="persisted" 
      if="#{!facilityHome.addCourse}"> 
      ... 
    </rule> 
  </navigation> 
</page>             



Begin    将临时conversation提升为long-running Conversation。如果已经有long的,就报错。
Join     类似begin,只是简单标示没有long的,就不会报错了。
End    将long的降级为临时的conversation.在此次渲染完毕就结束了
Nest    如果有long了,挂起它,创建一个新的long,放入堆栈。如果没有,则将临时提升为long。
None    销毁当前conversation,之前的留下,同时创建一个新的临时conversation。
传播属性就决定了conversation的状态。

 
标记@Begin的方法成功执行之后,才会提升long-running conversation。
有两种情况@Begin不会生效,不会提升long-running  conversation
    该方法是非void的返回类型,但是返回了null,
    该方法抛出异常时。
@End同样。

以下几种写法都可以启动long-running conversation.
<page view-id="/CourseList.xhtml"> 
  <navigation from-action="#{courseWizard.addCourse}"> 
    <begin-conversation/> 
    <redirect view-id="/coursewizard/selectFacility.xhtml"/> 
  </navigation> 
</page>

<page view-id="/coursewizard/selectFacility.xhtml" 
  action="#{conversation.begin}"/> 
 
<page view-id="/coursewizard/selectFacility.xhtml"> 
  <begin-conversation/> 
</page>

在UI组件TAG中也可以控制conversation的传递。
可以在别的标签内嵌套用<s:conversationPropagation>(只是在URL中增加项目而已)或直接用<s:link>或<s:button>。


<h:commandButton action="#{courseWizard.addCourse}" 
  value="Add course..."> 
  <s:conversationPropagation propagation="begin"/> 
</h:commandButton>

<s:button action="#{courseWizard.addCourse}" propagation="begin" 
  value="Add course..."/>
如果不需要提交form,那么<s:link>和<s:button>是极好的替代品,它们内置了conversation的控制属性。


@Begin的方法执行第二次会报错,因为long-running conversation已经存在。
需要用join。

@Begin(join = true) 
public void addCourse() { 
    course = new Course(); 
}

<page view-id="/CourseList.xhtml"> 
  <navigation from-action="#{courseWizard.addCourse}"> 
    <begin-conversation join="true"/> 
    <redirect view-id="/coursewizard/selectFacility.xhtml"/> 
  </navigation> 
</page>

如果使用的是#{conversation.begin},那么什么都不用做,它本身是join safe的。

组件中,只要将属性改为join即可。
<s:button action="#{courseWizard.addCourse}" propagation="join" 
  value="Add course..."/>
推荐使用<page>,支持条件绑定……

<page view-id="/CourseList.xhtml"> 
  <navigation from-action="#{courseWizard.addCourse}"> 
    <begin-conversation if="#{!conversation.longRunning}"/> 
    <redirect view-id="/coursewizard/selectFacility.xhtml"/> 
  </navigation> 
</page>
Conversation 的保持。
也就是如何在下个request时恢复conversation。
诀窍就是传递conversation id,可能是通过URL,也可能通过JSF的组件树。
    Seam发现request中有conversation令牌,就通过它恢复已有的conversation,使用它处理请求。
    存在long conversation的话,Seam自动添加令牌到JSF组件树,后续的提交表单时就会自动提交过来。
    如果没有提交JSF组件树,那么还可以通过URL取得。

最初的页面访问都是从GET开始的,Seam不知道conversation id,所以创建临时conversation。
默认的参数名是conversationId。

<a href="preview.seam?conversationId=#{conversation.id}" 
  target="_blank">Preview</a>

<h:outputLink value="preview.seam" target="_blank"> 
  <f:param name="conversationId" value="#{conversation.id}"/> 
  <h:outputText value="Preview"/> 
</h:outputLink>

参数名可以自定义,如添加
<core:manager conversation-id-parameter="cid"/>
那么默认的conversationId就不起作用了,需要用cid。

Seam提供了一个特殊的TAG,<s:conversationId>,可以给JSF LINK自动添加参数。
<h:outputLink value="preview.seam" target="_blank"> 
  <s:conversationId/> 
  <h:outputText value="Preview"/> 
</h:outputLink>

但是,<s:link>和<s:button>更聪明,它们会自动添加的,nothing need to do.
但是记住,二者不会提交表单,用作普通的链接什么的还是不错的。
Conversation范围的组件必须实现java.io.Serializable接口,因为它要放在session中,特别是分布式的session。

@Conversational 
public String submitBasicInfo() { 
    // ... 
}
有了此标记,此方法只能在long-running conversation中被调用,否则报错。
容器  一个类如何称为组件
    Seam,  用@Name注解,或在components.xml中声明
    EJB 3,  用@Stateful, @Stateless, 或 @MessageDriven 注解
    JSF,  作为 managed-beans 在faces-config.xml 中声明
    Spring,  作为Spring bean 在applicationContext.xml 中声明
    servlet container,  在web.xml中声明servlets, filters and listeners

组件,组件实例,二者类似与java的类和实例对象的关系。

根据组件,实例化出一个组件对象后,它就作为一个属性被存在了指定的Context中。
组件对象,就是一个Java对象而已,但是,Seam可以通过拦截器对它进行管理。当调用它时,Seam就可以根据组件的注解进行一些额外的动作。
如注入,管理事务,执行安全约束,调用生命周期方法,等等等……

当请求使用组件时,如果组件实例已经有了,那么就用它,没有就新建一个。
Seam容器就像一个组件对象工厂。

Seam中定义组件可以用注解,也可以用xml定义,推荐用注解。

    @Name,组件的名字,有了它,一个类就称为组件啦。
    @Scope,组件所在的范围
    @Role(@Roles),定义交替的name/scope对,可以创建平行的实例,用作不同的用途。
多个@Role必须包括在@Roles内。
    @Startup,如果是application级别的,那么容器启动时就创建组件对象,如果是session级别的,那么session开始时,就创建。
    @Namespace 绑定一个URI,给xml用的。
    @Install,条件性的安装组件,如某个类的存在,其它组件的存在,或debug模式
    @Autocreate 当Context变量第一个次被请求时,就创建对象,即使不是用它。

基本上,@Name和@Scope就够了,其他都是辅助性的。

一个组件可能是:
    JavaBean(POJO)
    JPA entity class
    EJB 组件(Stateless session bean、Stateful session bean、Message driven bean)
    Groovy class
    Spring bean

默认的Scope:
EJB stateful session bean  :conversation 
JPA entity class          :conversation 
EJB stateless session bean  :stateless 
EJB message driven bean  :stateless 
JavaBean (POJO)  :event

组件扫描器只扫描特定的classpath或jar文件:
    Classpath的根目录有seam.properties文件
    META-INF目录有seam.properties文件或组件描述文件(如components.xml)
可以大大节省扫描时间。

扫描器发现class有@Name注解后,默认的动作就是把它做成组件。
@Install可以设置这个条件。

@Install(value=false) 默认是true,是否安装组件。
@Install(dependencies =”xxx”) 指定名字的组件必须被安装了
@Install(classDependencies =”aa.bb.cc”) 指定的类必须存在
@Install(genericDependencies =”aa.bb.cc”) 指定的类必须作为组件存在
@install(precedence=30) 如果组件同名的话,值大的优先,默认20
@install(debug=true) 默认为false,是否debug模式时才安装组件。

Seam的一些内置组件就是@Install(false)的,需要时可以打开它。
    Seam managed persistence context 
    jBPM session factory 
    POJO cache 
    Asynchronous dispatcher (Quartz, EJB 3, Spring) 
    non-JTA transaction manager 
    JMS topic publisher 
    Spring context loader 
    GWT service integration 
    Meldware (embeddable mail server) configuration

比如,可以定义一个接口,和两个类(组件名相同),分别实现不同的功能。
@Name("jsfAdapter") 
@Install(classDependencies = 
  "com.sun.faces.context.FacesContextImpl") 
public class SunJsfAdapter implements JsfAdapter { 
... 
}

@Name("jsfAdapter") 
@Install(classDependencies = 
  "org.apache.myfaces.context.servlet.ServletFacesContextImpl") 
public class MyFacesJsfAdapter implements JsfAdapter { 
... 
}
Seam会自动调用合适的组件。


Seam的debug模式是org.jboss.seam.core.init组件的debug属性控制的,可以在
Components.xml中使用<core:init debug="true"/>来控制。

Deubg模式时,Seam将激活@Install(debug=true)的组件,debug组件优先级较高。
这样,debug时调用debug的组件,非debug时调用正式的组件。

优先级,Seam所有的内置组件的优先值都是Install.BUILT_IN (0),所以,可以轻松覆盖它们。未指定的话,就是默认的Install.APPLICATION (20)。

如果两个组件的名字和优先级都相同,将会报错。

 

评论 COMMENTS
没有评论 No Comments.

添加评论 Add new comment.
昵称 Name:
评论内容 Comment:
验证码(不区分大小写)
Validation Code:
(not case sensitive)
看不清?点这里换一张!(Change it here!)
 
评论由管理员查看后才能显示。the comment will be showed after it is checked by admin.
CopyRight © 心缘地方 2005-2999. All Rights Reserved