JSF组件开发指南

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>

JSF组件开发指南

Author:Sonic

Email: SonicJin@Gmail.com

 

JSF 组件模型

JSF 组件模型与 AWT GUI 组件模型类似。它有事件和属性,就像 Swing 组件模型一样。它也有包含组件的容器,容器也是组件,也可以由其他容器包含。从理论上说,JSF 组件模型分离自 HTML JSPJSF 自带的标准组件集里面有 JSP 绑定,可以生成 HTML 渲染。

JSF 组件的示例包括日历输入组件和 HTML 富文本输入组件。您可能从来没时间去编写这样的组件,但是如果它们已经存在,那会如何呢?通过把常用功能变成商品,组件模型降低了向 Web 应用程序添加更多功能的门槛。

组件的功能通常围绕着两个动作:解码和编码数据

解码(decoding 是把进入的请求参数转换成组件的值的过程。

编码 (encode) 是把组件的当前值转换成对应的标记(也就是 HTML)的过程。

 

JSF 框架提供了两个选项用于编码和解码数据

使用直接实现 方式,组件自己实现解码和编码

使用委托实现 方式,组件委托渲染器进行编码和解码。

如果选择委托实现,可以把组件与不同的渲染器关联,会在页面上以不同的方式渲染组件;例如多选列表框和一列复选框。因此,JSF 组件由两部分构成:组件和渲染器。JSF 组件 类定义 UI 组件的状态和行为;渲染器 定义如何从请求读取组件、如何显示组件 —— 通常通过 HTML 渲染。渲染器把组件的值转换成适当的标记。事件排队和性能验证发生在组件内部。

在图 1 中可以看到数据编码和解码出现在 JSF 生命周期中的什么阶段


1. JSF 生命周期和 JSF 组件

所有 JSF 组件的基类是 UIComponent。在开发自己的组件时,需要继承 UIComponentBase,它扩展了 UIComponent 并提供了 UIComponent 中所有抽象方法的默认实现。组件拥有双亲和标识符。每个组件都关联着一个组件类型,组件类型用于在 face 的上下文配置文件(faces-config.xml)中登记组件。可以用 JSF-EL (表达式语言)把 JSF 组件绑定到受管理的 bean 属性。可以把表达式关联到组件上的任何属性,这样就允许用 JSF-EL 设置组件的属性值。在创建使用 JSF-EL 绑定的组件属性时,需要创建值绑定表达式。在调用绑定属性的 getter 方法时,除非 setter 方法已经设置了值,否则 getter 方法必须用值绑定获得值。组件可以作为 ValueHolder EditableValueHolderValueHolder 与一个或多个 Validator Converter 相关联;所以 JSF UI 组件也与 Validator Converter 关联

 

像表单字段组件这样的组件拥有一个 ValueBinding,它必须绑定到 JavaBean 的读写属性。组件可以调用 getParent 方法访问它们的双亲,也可以调用 getChildren 方法访问它们的子女。组件也可以有 facet 组件facet 组件是当前组件的子组件,可以调用 getFacets 方法访问它,这个方法返回一个映射。Facets 是著名的子组件。

这里描述的许多组件的概念将会是接下来展示的示例的一部分,所以请记住它们!

 

 

提示!

在许多情况下,可以在保持组件本身不变的情况下,通过改变渲染而简化开发过

程。在这些情况下,可以编写定制渲染器而不是定制组件。

 

JSF 组件的LoginComponent

 

我们用一个又好又容易的示例来开始 JSF 组件的开发:

下面是我要采取的步骤:

 

1. 扩展 UIComponent

创建一个类,扩展 UIComponent

保存组件状态

faces-config.xml 登记组件

2. 创建定制标记,继承 UIComponentTag

覆盖 encode

覆盖 decode

faces-config.xml 登记渲染器

3. 定义渲染器或者内联地实现它

返回渲染器类型

返回组件类型

设置可能使用 JSF 表达式的属性

 

 

详细结构:由component model renderer taglib utils(可选) 组成。

 

Step 1 扩展Components

具体代码请参考Sample , 这里只叙述一些重要的方法

第一个类是LoginComponent.java LoginComponent是整个组件开发的核心类。

它必须继承一个UIComponent ,这里它继承的是UIInput

每个组件都关联着一个组件类型,组件类型用于在 face 的上下文配置文件(faces-config.xml)中登记组件

 

 

   < component >

    
< component-type > sonic.Loginr </ component-type >

    
< component-class > mycomponents.component.LoginComponent </ component-class >

  
</ component >

 

JSF提供了数值绑定机制,使得在Faces标记中访问bean property 很容易,其原理是用 JSF-EL (表达式语言)把 JSF 组件绑定到受管理的 bean 属性。可以把表达式关联到组件上的任何属性,

这样就允许用 JSF-EL 设置组件的属性值,比如:

<hinputText id=”user” value=”#{ userBean .username}” />

Valuebinding 在解析userBean.username这个keypath的时候,会使用反射(reflection)访问UserBean中的username property、如果没有就为null.

 

 

在创建使用 JSF-EL 绑定的组件属性时,需要创建值绑定表达式。在调用绑定属性的 getter 方法时,除非 setter 方法已经设置了值,否则 getter 方法必须用值绑定获得值。

 

 
// get The UserName value

    
public  String getUserText()  {

        
if(this.userText!=null)

             
return this.userText;

        
else{

           ValueBinding vb 
= this.getValueBinding("userText");

           
if(vb==null)

                
return "";

            
else

                
return (String)vb.getValue(getFacesContext());

        }


    }


    

 

  
// Get the Password value

    
public  String getPswText()  {

        
if(this.pswText!=null)

             
return this.pswText;

        
else{

          ValueBinding vb 
= this.getValueBinding("pswText");

           
if(vb == null)

                
return "";

            
else

                
return (String)vb.getValue(getFacesContext());

        }


    }


 

所有 JSF 组件的基类是 UIComponent。在开发自己的组件时,需要继承 UIComponentBase,它扩展了 UIComponent 并提供了 UIComponent 中所有抽象方法的默认实现。

 

它的主要作用就是保存和恢复组件的状态。由saveStore(),restoreState() 两个方法来执行。

getFamily()方法主要是返回在faces-config.xml中定义了相关联的渲染器

 

 

< render-kit >

       
< renderer >

          
< component-family > sonic.Loginr </ component-family >

          
< renderer-type > sonic.Loginr </ renderer-type >

          
< renderer-class > mycomponents.renderer.LoginRenderer </ renderer-class >

       
</ renderer >

</ render-kit >

 

UIComponent 类的一个职责是为其绑定的model property 提供UI表示(UIComponentRender其实对应MVC中的View)。 UIComponent定义有value property , 用了缓冲在请求相应中inbound(接受请求值) , outbound(处理后的请求值)这个property 还在

Apply Request Values 阶段暂时保存了对提交的值转换之后的结果,最终,组件value字段的内容会在

Update Model Value 阶段 被转换到对应的bean property .

 

由于,我的组件是一个复合组件,有2textbox 他不能在updateModel()这个方法中自动绑定与bean相关联的属性,所以在这个里面就要重载updateModel(),手动绑定属性到bean中。

        public   void  updateModel(FacesContext context)  {

              
// TODO Auto-generated method stub

              ValueBinding vb 
= this.getValueBinding("userText");

              
if (vb != null){

                     vb.setValue(context,
this.getUsername());

                     
this.setUsername(null);

              }


              
              ValueBinding vb2 
= this.getValueBinding("pswText");

              
if (vb2 != null){

                     vb2.setValue(context,
this.getPassword());

                     
this.setPassword(null);

              }


              
super.updateModel(context);

       }


 

 

 

Step 2 创建一个LoginTag LoginComponent创建定制标记

 

l  LoginTag 必须 extends UIComponentTag

l  web.xml中的 <jsp-config></jsp-config>中添加标记定位

  < jsp-config >

            
< taglib >

              
< taglib-uri > http://www.ygsoft.com </ taglib-uri >

               
< taglib-location > /WEB-INF/login.tld </ taglib-location >

            
</ taglib >

</ jsp-config >

 

 

l  添加login.tld

 

setProperty()这个方法允许这个代码允许 LoginComponent value 属性绑定到后台 bean

     protected   void  setProperties(UIComponent component)  {

        
/* have to call the super class */

        
super.setProperties(component);

        CompUtils.setAttribute(component,
"userLabel",userLabel);

        CompUtils.setAttribute(component,
"pswLabel",pswLabel);

        CompUtils.setAttribute(component,
"userText",userText);

        CompUtils.setAttribute(component,
"pswText",pswText);

        CompUtils.setAttribute(component,
"loginSuccess",loginSuccess);

        CompUtils.setAttribute(component,
"loginFailure",loginFailure);

        CompUtils.setAttribute(component,
"errorStyleClass",errorStyleClass);

        ((LoginComponent)component).setRequired(
true);

}


 

       
public   final   static   void  setAttribute(UIComponent component,

                     String attrName, String value) 
{

              
if (value != null{

                     
if (Util.isVBExpression(value)) {

                            ValueBinding vb 
= Util.getValueBinding(value);

                            component.setValueBinding(attrName, vb);

                     }
 else {

                            component.getAttributes().put(attrName, value);

                     }


              }


       }


 

errorStyleClass属性不是setValueBinding的,它们被添加到 JSF 组件的属性映射 中。Renderer类会使用属性映射去渲染类和样式属性。这个设置允许特定于 HTML 的代码从组件脱离所以是用getAttributes().put(attrName, value);来设定的

 

最后是把渲染器映射到组件

     public  String getComponentType() {

        
return   " sonic.Loginr " ;

}    

    
< component >

         
< component - type > sonic.Loginr </ component - type >

         
< component - class > mycomponents.component.LoginComponent </ component - class >

</ component >

============================================================================

    
public  String getRendererType() {

        
return   " sonic.Loginr " ;

}    

  
< render - kit >

    
< renderer >

        
< component - family > sonic.Loginr </ component - family >

        
< renderer - type > sonic.Loginr </ renderer - type >

        
< renderer - class > mycomponents.renderer.LoginRenderer </ renderer - class >

    
</ renderer >

</ render - kit >

 

 

 

Step 3 定义一个独立渲染器LoginRender

 

其实渲染器可以在UIComponent里面的,这里使用的是使用委托实现方式,组件委托渲染器进行编码和解码。

渲染器的作用其实主要是html的输出,其中的重点是encodeBegin decode

 

LoginComponent组件是一个 UIInput 组件,这意味着它接受输入(UIOutPut组件不用decode),所以 必须 进行解码。decode方法可以从会话、cookie、头、请求等处读取值。在大多数请问下,decode方法只是像上面那样从请求参数读取值。Decode 方法负责从请求取得输入,并解码成适合组件的数据

 

Encode 编码,编成HTML,Rander类里面就是encodeBegin , encodeEnd这里就是写在encodeBeanin里面,其实对应自定义标签里面的doStartTag() , doEndTag();

encodeBegin()里面主要是用 ResponseWriter

响应写入器(javax.faces.context.ResponseWriter)可以容易地处理 HTML 这样的标记语言

 

 

ResponseWriter writer  =  context.getResponseWriter(); 

比如:

       writer.startElement(
" table " , component);

              writer.writeAttribute(
" width " " 60% " null );

              writer.writeAttribute(
" align " " center " null );

              writer.writeAttribute(
" border " " 0 " null );

              writer.writeAttribute(
" cellpadding " " 0 " null );

              writer.endElement(
" table " );

 

生成的HTML代码就是

< table  width =”60%”  align =”center”  border =”0”  cellspacing =”1” ></ table >

<script type="text/javascript"> </script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值