译者注:
在[“Facelets 非常适合 JSF”(Richard Hightower,developerWorks,2006 年 2 月):开始第一个能真正 解决您的 JSF-and-JSP 烦恼的解决方案。 ] 一文中我们看到了如何使用Facelets来创建复合组件. 这是facelets的强大功能之一. 但是还是有点缺陷(如何创建复合控制). 下面我们就来看看,如何解决这个问题:
原文: http://andrewfacelets.blogspot.com/2006/06/creating-composite-controls-with-jsf.html
翻译: icess http://blog.matrix.org.cn/page/icess
JSF是一个强大的web框架,但是没有很好的工具用来支持开发复合控制(composite controls). 如果创建一个具有很多子control的control 或者组合了100个子组件的control,则要有很多工作要做,这没有一个简单的解决方案.JSF规范上只不过写道: 创建一个组件并且有她自己来呈现她.
而Facelets 是JSF的一个非常强大的扩展. 使用一个 user tag (a.k.a. source tag), 可以很容易的创建一个 composite control .问题是传递一个方法绑定到子组件不是 立即可以使用的( out-of-the-box).
例如:
Snippet from taglib.xml:
<tag>
<tag-name>test</tag-name>
<source>tags/testTag.xhtml</source>
</tag>
Usage in an XHTML file:
<my:test actionListener="#{myBean.doSomething}" />
User Tag file:
<ui:composition>
<h:commandButton value="Click Me" actionListener="#{actionListener}" />
</ui:composition>
上面的问题是: facelets 的user tag handler总是为每一个source tag的属性 创建 ValueExpression 对象 ,这对于简单属性是很好的,但是在上面的情况下 ,应该创建 一个 MethodExpression
我想解决该问题,使用者不需要创建新的tag handler 或者 component handler 就可以使用. 我还想使用一个 re-usable complete solution. 在我潜入到facelets的代码中后发现我只需要扩展faceltets的api就可以了, 解决方案有两部分:
- Create a tag handler with component support
- Create a new value expression that returns method expressions
Creating a value expression that is a method expression
首先讨论上面的第二步是比较容易的. 思想是使用一个返回method expression值的 value expression函数. 这将把"#{myBean.doSomething}" 解释为一个方法调用 "getDoSomething"
下面的代码可能还不是十分完善,但是她工作的很好:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import javax.el.ELContext;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
public class MethodValueExpression
extends ValueExpression
implements Externalizable
{
private ValueExpression orig;
private MethodExpression methodExpression;
public MethodValueExpression() {}
MethodValueExpression(ValueExpression orig, MethodExpression methodExpression)
{
this.orig = orig;
this.methodExpression = methodExpression;
}
@Override
public Class getExpectedType()
{
return orig.getExpectedType();
}
@Override
public Class getType(ELContext ctx)
{
return MethodExpression.class;
}
@Override
public Object getValue(ELContext ctx)
{
return methodExpression;
}
@Override
public boolean isReadOnly(ELContext ctx)
{
return orig.isReadOnly(ctx);
}
@Override
public void setValue(ELContext ctx, Object val) {}
@Override
public boolean equals(Object val)
{
return orig.equals(val);
}
@Override
public String getExpressionString()
{
return orig.getExpressionString();
}
@Override
public int hashCode()
{
return orig.hashCode();
}
@Override
public boolean isLiteralText()
{
return orig.isLiteralText();
}
/**
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
orig = (ValueExpression)in.readObject();
methodExpression = (MethodExpression)in.readObject();
}
/**
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out)
throws IOException
{
out.writeObject(orig);
out.writeObject(methodExpression);
}
}
现在我们有一个包装了method expression的value expression. 我们需要在JSF环境中替换他们.当facelets 应用组件到组件树上时, 有一个 EL context 来解释数据. 如果我们知道绑定的方法签名( method signatures) 我们就可以创建 MethodExpression 对象.
我们从构造函数开始. 我们要一个attribute来保存我们的配置 . 不幸的是. 一个 attribute 只能指定一次( given once), 因此我将创建一个 custom format 来代替使用. Starting code:
public class CompositeControlHandler
extends TagHandler
{
private final TagAttribute methodBindings;
private ComponentHandler componentHandler;
/**
* @param config
*/
public CompositeControlHandler(TagConfig config)
{
super(config);
methodBindings = getAttribute("methodBindings");
}
// TODO...
}
现在我们有一个skeleton 用来声明 我们自定义tag的"methodBindings" attribute ,来定义我们自定义tag中的那些属性应该用方法绑定来替代变量. 我使用下面的格式:
attribute-name=java-return-type first-java-parameter-type second-java-parameter-type;
second-attribute-name=java-return-type first-java-parameter-type second-java-parameter-type;
Example from above:
actionListener=void javax.faces.event.ActionEvent;
现在, 我们有一个方法来指定那些属性是 methods, 我们还需要做些工作. Steps:
- 解析attribute
- 对于每一个Attribute,看看她是否 "bound" 到当前的 variable mapper上
- 如果bound了,从配置信息中创建一个新的 method expression
- 使用method expression来隐藏初始变量
The resultant code is as follows:
public class CompositeControlHandler
extends TagHandler
{
private final static Pattern METHOD_PATTERN = Pattern.compile(
"(\\w+)\\s*=\\s*(.+?)\\s*;\\s*");
private final TagAttribute methodBindings;
/**
* @param config
*/
public CompositeControlHandler(TagConfig config)
{
super(config);
methodBindings = getAttribute("methodBindings");
}
/**
* @see com.sun.facelets.FaceletHandler#apply(com.sun.facelets.FaceletContext, javax.faces.component.UIComponent)
*/
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException
{
VariableMapper origVarMap = ctx.getVariableMapper();
try
{
VariableMapperWrapper variableMap = new VariableMapperWrapper(origVarMap);
ctx.setVariableMapper(variableMap);
if (methodBindings != null)
{
String value = (String)methodBindings.getValue(ctx);
Matcher match = METHOD_PATTERN.matcher(value);
while (match.find())
{
String var = match.group(1);
ValueExpression currentExpression = origVarMap.resolveVariable(var);
if (currentExpression != null)
{
try
{
FunctionMethodData methodData = new FunctionMethodData(
var, match.group(2).split("\\s+"));
MethodExpression mexpr = buildMethodExpression(ctx,
currentExpression.getExpressionString(), methodData);
variableMap.setVariable(var, new MethodValueExpression(
currentExpression, mexpr));
}
catch (Exception ex)
{
throw new FacesException(ex);
}
}
}
}
// TODO: will do this next
}
finally
{
ctx.setVariableMapper(origVarMap);
}
}
private MethodExpression buildMethodExpression(FaceletContext ctx, String expression,
FunctionMethodData methodData)
throws NoSuchMethodException, ClassNotFoundException
{
return ctx.getExpressionFactory().createMethodExpression(ctx, expression,
methodData.getReturnType(), methodData.getArguments());
}
private class FunctionMethodData
{
private String variable;
private Class returnType;
private Class[] arguments;
FunctionMethodData(String variable, String[] types)
throws ClassNotFoundException
{
this.variable = variable;
if ("null".equals(types[0]) || "void".equals(types[0]))
returnType = null;
else
returnType = ReflectionUtil.forName(types[0]);
arguments = new Class[types.length - 1];
for (int i = 0; i < arguments.length; i++)
arguments[i] = ReflectionUtil.forName(types[i + 1]);
}
public Class[] getArguments()
{
return this.arguments;
}
public void setArguments(Class[] arguments)
{
this.arguments = arguments;
}
public Class getReturnType()
{
return this.returnType;
}
public void setReturnType(Class returnType)
{
this.returnType = returnType;
}
public String getVariable()
{
return this.variable;
}
public void setVariable(String variable)
{
this.variable = variable;
}
}
}
现在,比较有意思了,为什么不使用一个 tag handler来代替她呢,但是一个component handler也是一样的. 下一个步骤将可以不需要任何XML配置文件就可以使用该 user tag了 . 目标是让用户指定作为我们user tag的组件的 component type and renderer type (If none is given, the ComponentRef from facelets will be used).
代码并不是很难,下面是完整的代码:
public class CompositeControlHandler
extends TagHandler
{
private final static Pattern METHOD_PATTERN = Pattern.compile(
"(\\w+)\\s*=\\s*(.+?)\\s*;\\s*");
private final TagAttribute rendererType;
private final TagAttribute componentType;
private final TagAttribute methodBindings;
private ComponentHandler componentHandler;
/**
* @param config
*/
public CompositeControlHandler(TagConfig config)
{
super(config);
rendererType = getAttribute("rendererType");
componentType = getAttribute("componentType");
methodBindings = getAttribute("methodBindings");
componentHandler = new ComponentRefHandler(new ComponentConfig() {
/**
* @see com.sun.facelets.tag.TagConfig#getNextHandler()
*/
public FaceletHandler getNextHandler()
{
return CompositeControlHandler.this.nextHandler;
}
public Tag getTag()
{
return CompositeControlHandler.this.tag;
}
public String getTagId()
{
return CompositeControlHandler.this.tagId;
}
/**
* @see com.sun.facelets.tag.jsf.ComponentConfig#getComponentType()
*/
public String getComponentType()
{
return (componentType == null) ?
ComponentRef.COMPONENT_TYPE :
componentType.getValue();
}
/**
* @see com.sun.facelets.tag.jsf.ComponentConfig#getRendererType()
*/
public String getRendererType()
{
return (rendererType == null) ?
null : rendererType.getValue();
}
});
}
/**
* @see com.sun.facelets.FaceletHandler#apply(com.sun.facelets.FaceletContext, javax.faces.component.UIComponent)
*/
public void apply(FaceletContext ctx, UIComponent parent)
throws IOException, FacesException, FaceletException, ELException
{
VariableMapper origVarMap = ctx.getVariableMapper();
try
{
VariableMapperWrapper variableMap = new VariableMapperWrapper(origVarMap);
ctx.setVariableMapper(variableMap);
if (methodBindings != null)
{
String value = (String)methodBindings.getValue(ctx);
Matcher match = METHOD_PATTERN.matcher(value);
while (match.find())
{
String var = match.group(1);
ValueExpression currentExpression = origVarMap.resolveVariable(var);
if (currentExpression != null)
{
try
{
FunctionMethodData methodData = new FunctionMethodData(
var, match.group(2).split("\\s+"));
MethodExpression mexpr = buildMethodExpression(ctx,
currentExpression.getExpressionString(), methodData);
variableMap.setVariable(var, new MethodValueExpression(
currentExpression, mexpr));
}
catch (Exception ex)
{
throw new FacesException(ex);
}
}
}
}
componentHandler.apply(ctx, parent);
}
finally
{
ctx.setVariableMapper(origVarMap);
}
}
private MethodExpression buildMethodExpression(FaceletContext ctx, String expression,
FunctionMethodData methodData)
throws NoSuchMethodException, ClassNotFoundException
{
return ctx.getExpressionFactory().createMethodExpression(ctx, expression,
methodData.getReturnType(), methodData.getArguments());
}
private class FunctionMethodData
{
private String variable;
private Class returnType;
private Class[] arguments;
FunctionMethodData(String variable, String[] types)
throws ClassNotFoundException
{
this.variable = variable;
if ("null".equals(types[0]) || "void".equals(types[0]))
returnType = null;
else
returnType = ReflectionUtil.forName(types[0]);
arguments = new Class[types.length - 1];
for (int i = 0; i < arguments.length; i++)
arguments[i] = ReflectionUtil.forName(types[i + 1]);
}
public Class[] getArguments()
{
return this.arguments;
}
public void setArguments(Class[] arguments)
{
this.arguments = arguments;
}
public Class getReturnType()
{
return this.returnType;
}
public void setReturnType(Class returnType)
{
this.returnType = returnType;
}
public String getVariable()
{
return this.variable;
}
public void setVariable(String variable)
{
this.variable = variable;
}
}
}
现在我们要在一个 taglib.xml 中注册她, 然后就可以使用了:
<tag>
<tag-name>compositeControl</tag-name>
<handler-class>mypackage.CompositeControlHandler</handler-class>
</tag>
注册后让我们开始使用她吧. 使用该tag的XHTML file:
<my:test actionListener="#{myBean.doSomething}" />
The user tag does look different, but not that much:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:my="http://mynamespace">
<body>
<ui:composition>
<my:compositeControl
id="#{id}"
methodBindings="action=java.lang.String;
actionListener=void javax.faces.event.ActionEvent;">
<ui:debug />
<h:commandButton value="Click me"
actionListener="#{actionListener}"
action="#{action}" />
</my:compositeControl>
</ui:composition>
</body>
</html>
That should be enough to get you going.
© Copyright 2006 - Andrew Robinson.
Please feel free to use in your applications under the LGPL license (http://www.gnu.org/licenses/lgpl.html).
分享到:
相关推荐
Facelets使用XML语法,允许开发者更直观地创建和组织UI组件。它支持模板、导入、组合和重用组件,使得视图更加清晰和易于维护。Facelets还具有编译时的错误检查和性能优化。 3. **Ajax4jsf**:Ajax4jsf是一个开源...
综上所述,JSF与Facelets的结合提供了强大的Web应用开发工具集,它简化了视图的创建和维护,同时保持了与后端业务逻辑的紧密集成。通过使用这个框架,开发者可以专注于业务逻辑的实现,而不需要过多关注底层的HTTP...
jsf_facelets 1.0.10版本
jsf-facelets.jar 下载 希望对您有用
jsf-facelets.jar 1.1.15.B1
1. **模板语言**:Facelets使用XML语法,提供了一种声明式的方式来创建和组织用户界面组件。这种模板驱动的方法使得代码更加清晰和易于维护。 2. **直接组件绑定**:Facelets允许直接在页面上声明和引用JSF组件,...
Facelets 非常适合 专为 JSF 设计的视图技术
jsf-facelets-1.1.15.B1.jar
4. **Managed Bean的创建和使用**: 编写Managed Bean,处理页面请求和数据。 5. **调试和测试**: 运行应用,通过浏览器进行测试,根据需求进行调整。 ### 学习资源 1. **官方文档**: Oracle提供了详细的JSF规范...
[TipTec Development] JSF & Facelets & JBoss Seam 核心技术 (英文版) [TipTec Development] Essential JSF, Facelets & JBoss Seam (E-Book) ☆ 出版信息:☆ [作者信息] Kent Ka Iok Tong [出版机构] TipTec ...
Facelets是JSF的默认视图层技术,它用于创建和管理用户界面。相比于JSP,Facelets更加强调组件化和模板化,提供了更加清晰和可维护的代码结构。Facelets使用XML语法,可以更好地与Java代码集成,并支持声明式编程。 ...
Facelets 是一种用于构建 JavaServer Faces (JSF) 应用程序视图的框架,它提供了更高效和灵活的方式来创建用户界面。以下是 Facelets 的详细知识点: 1. **下载与依赖**:首先,你需要下载 Facelets 的库文件,并...
JSF(JavaServer Faces)是Java平台上用于构建Web应用程序的一种技术框架,它提供了一种声明式的方法来创建用户界面,并且处理与后端数据模型的交互。在这个"jsf实现登录功能"的例子中,我们将探讨如何利用JSF来构建...
它提供了声明式的方式去定义页面结构和逻辑,使得开发者能够更高效地创建和维护Web应用程序的前端。在这个"facelets_demo"项目中,我们可以深入学习Facelets如何与MyEclipse集成,以及如何在实际开发环境中应用。 1...
1. **Facelet**:Facelets是JSF的默认视图技术,用于创建和组织用户界面组件。 2. **Managed Bean**:Managed Bean是JSF中的核心组件,用来存储和处理数据。 3. **Navigation Handler**:负责处理页面间的跳转。 4. ...
5. **JSF项目的创建**:这部分教程会指导你从零开始创建一个完整的JSF项目,包括设置项目结构、编写Managed Beans、配置Facelets视图和部署描述符等。通过实践,你可以加深对JSF工作流程的理解,并能独立完成JSF应用...
Facelets 是JSF 1.2及更高版本的默认视图技术,它使用XML语法来定义用户界面组件和布局。Facelets 提供了模板、组合和重用功能,使得开发更加模块化和易于维护。与JSP相比,Facelets 更加轻量级,有更好的性能和更少...
在 Facelets 中可以添加 JSF 验证器和转换器。Facelets 全面支持表达式语言 (EL) 和 JavaServer Pages Standard Tag Library (JSTL)。在 Web 应用程序中使用 Facelets 的部分好处包括模板化、重用和易于开发。