开发一个JSF自定义组件一般需要开发三个类:组件类(Component,这代表了组件的模型)、渲染类(Renderer,负责显示视图)和标签类(Tag,负责引用组件类和渲染类),别外还需要两个对应的文件,一个对Tag的时行描述的TLD文件和一个对组件的配置文件。关于如何开发JSF自定义组件已经有很多好文章和图书,这里就不多做介绍了,可以参考《Pro JSF And AJAX》这本书,我觉得讲得很不错。由于JSF中巧妙地将组件模型和视图显示分离开来,这样同一个组件模型可以以不同的样式显示,换句话说,不同的显示视图可以重用同一个模型。这篇文章中,主要是想说明这是如何实现的。我们先从JSF自定义组件的配置文件说起,下面是我的faces-config.xml中的一段:
- <render-kit>
- <renderer>
- <component-family>com.yanhua.YhComponent</component-family>
- <renderer-type>com.yanhua.YhRenderer1</renderer-type>
- <renderer-class>com.yanhua.YhRenderer1</renderer-class>
- </renderer>
- <renderer>
- <component-family>com.yanhua.YhComponent</component-family>
- <renderer-type>com.yanhua.YhRenderer2</renderer-type>
- <renderer-class>com.yanhua.YhRenderer2</renderer-class>
- </renderer>
- </render-kit>
这段配置比较好理解,我定义了两个Renderer,它们具有相同的component-family,把有不同的renderer-type,当然也指向不同的Renderer类,也就是说,一个component-family和renderer-type的组合才能确定一个Renderer类。比如一个Component对象要显示的时候,它首先要得到一个Renderer,然后调用Renderer的方法来显示,请看javax.faces.component.UIComponentBase的renderStart这个方法,在显示时会先调用这个方法:
java 代码
- public void encodeBegin(FacesContext context)
- throws IOException
- {
- if (context == null)
- throw new NullPointerException();
- if (! isRendered())
- return;
- Renderer renderer = getRenderer(context);
- if (renderer != null)
- renderer.encodeBegin(context, this);
- }
- @Override
- protected Renderer getRenderer(FacesContext context)
- {
- RenderKit renderKit = context.getRenderKit();
- if (renderKit != null)
- return renderKit.getRenderer(getFamily(), getRendererType());
- else
- return null;
- }
>>UIComponentBase.java
我们在自己的Component(继承自UIComponentBase)类中只重写getFamily方法:
java 代码
- public String getFamily()
- {
- return "com.yanhua.YhComponent";
- }
而getRendererType方法我们沿用父类提供的就可以了:
java 代码
- public String getRendererType()
- {
- if (_rendererType != null) return _rendererType;
- ValueBinding vb = getValueBinding("rendererType");
- return vb != null ? (String)vb.getValue(getFacesContext()) : null;
- }
那_rendererType的具体值是在什么时候设置进去的呢?这就要看前面提到的Tag类了,我们来看看UIComponentTag这个类的setProperties方法:
UIComponentTag
可以看出这里它给Component的_rendererType赋了值。我们只要在自己的Tag类(继承自UIComponentTag)中重写getRendererType方法就可以了,比如我们写两个Tag类Tag1中的代码是这样的:
java 代码
- protected void setProperties(UIComponent component)
- {
- if (getRendererType() != null)
- {
- _componentInstance.setRendererType(getRendererType());
- }
- if (_rendered != null)
- {
- if (isValueReference(_rendered))
- {
- ValueBinding vb = getFacesContext().getApplication().createValueBinding(_rendered);
- component.setValueBinding("rendered", vb);
- } else
- {
- boolean b = Boolean.valueOf(_rendered).booleanValue();
- component.setRendered(b);
- }
- }
- }
java 代码
- public String getRendererType()
- {
- return "com.yanhua.YhRenderer1";
- }
Tag2中的代码是这样的:
java 代码
- public String getRendererType()
- {
- return "com.yanhua.YhRenderer2";
- }
这样,我们在JSP页面中加入两个标签:
xml 代码
- <yh:tag1>.......</yh:tag1>
- <yh:tag2>.......</yh:tag2>
这样,这们两个标签会以不同的样式显示,但它们重用了同一个Component类。不知道我说清楚了没有,不清楚的话,把文章从下往上倒着看一篇应该就知道整个的调用过程了。*^_^*