wicket,tapestry5,sopo 模板实现比较
1 Wicket实现
Wicket模板是html格式,示例:index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Index.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
</head>
<body>
<a wicket:id="edit" href="UserEdit.html?uid=1" target="_blank" >edit form</a>
</body>
</html>
还要在后台page类Index 中手动添加进组件列表中。
public class Index extends WebPage
{
/**
* Constructor
*/
public Index()
{
PageLink link = new PageLink("edit",new IPageLink());
add(link);
}
}
add方法是继承自父类org.apache.wicket.MarkupContainer的方法,会将组件添加到其字段children中去.
模板由org.apache.wicket.markup.MarkupParser解析,遇到wicket:id属性的会转换为org.apache.wicket.markup.MarkupElement,
最终将html模板解析成MarkupElement列表,添加到org.apache.wicket.markup.Markup的List<MarkupElement> markupElements集合中,在组件绘制的时候,将其
包装成org.apache.wicket.markup.MarkupStream,会遍历MarkupElement列表,依据组件id获得组件,让每个组件进行绘制--调用方法
void org.apache.wicket.Component.render(MarkupStream markupStream).
具体的主要的解析实现是由org.apache.wicket.markup.parser.XmlPullParser完成的,通过过字符串操作来解析,详见其next方法,并没有使用第三方类库.
2 tapestry实现
tapestry5模板时xml格式,示例Index.tml
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> <head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<h1>Address Book</h1>
<t:span c="d" >${fi}</t:span>
<ul>
<li>
<a t:type="pagelink" t:page="index" >Create new address</a>
</li>
</ul>
</body> </html>
因为已经在模板中指明了这个节点是什么类型的组件,所以,tapestry5就不需要再add组件了,它本身也不提供add方法。
public class Index
{
@Inject
private Logger logger;
private GridDataSource dss;
private int fi;
public Index()
{
super();
System.out.println("call ctor.");
}
public int getFi()
{
return fi;
}
public void setFi(int fi)
{
this.fi = fi;
}
}
tapastry5使用stAx实现的,这篇文章介绍了stax的用法,tapestry5通过注入的方式将org.apache.tapestry5.internal.services.TemplateParserImpl注入到
org.apache.tapestry5.internal.services.ComponentTemplateSourceImpl中的private final TemplateParser parser;字段中,TemplateParserImpl
实际使用的是org.apache.tapestry5.internal.services.StaxTemplateParser,在这里面会使用stax对模板进行解析。它通过节点是否有t:id,t:type属性来判断
是否是一个服务端组件。解析完模板会组建成一个节点列表,再在绘制页面的时候依据这个列表的顺序来调用相应的组件绘制。
逻辑主要在以下的类中
org.apache.tapestry5.internal.parser.ComponentTemplateImpl
org.apache.tapestry5.internal.structure.PageImpl
org.apache.tapestry5.internal.services.PageRenderRequestHandlerImpl
org.apache.tapestry5.internal.services.PageResponseRendererImpl
org.apache.tapestry5.internal.services.PageMarkupRendererImpl
org.apache.tapestry5.internal.services.PageRenderQueueImpl
Page org.apache.tapestry5.internal.pageload.PageLoaderImpl.loadPage(String logicalPageName, Locale locale)
3 sopo的实现方法
Sopo是可由每个page类来指明模板内容,只要是html格式就可以,示例:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>test.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<link rel="stylesheet" type="text/css" href="./styles.css">
<script type="text/javascript" src="js/hello.js" ></script>
<script type="text/javascript">
function $(id){
return document.getElementById(id);
}
function test()
{
$('rst').innerHTML=reg.test($('d').value);
}
var reg = new RegExp("^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$");
//var reg = /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/i;
</script>
</head>
<body>
h--------
<!-- hahah -->
<![CDATA[jkjkjk]]>
<form action="">
<label stype="s:Label" id="lab1" >Hello World1<label stype="s:Label" id="lab2" >Hello World3</label></label>
<input type="password" />
<label stype="s:Label" id="lab3" >Hello World2</label>
<input stype="s:Checkbox" id="chk" name="chk" >sss</input>
<select stype="s:Select" id="sel" name="sel" width="20px" >
<option stype="s:Option" text="1" ></option>
<option stype="s:Option" text="2" selected="selected" ></option>
<option stype="s:Option" text="3" ></option>
</select>
<input id="d" type="text" /><button onclick="test();">Test</button>
<span id="rst">true</span>
<input type="radio" name="1" >1</input>
<input type="radio" name="1" >2</input>
<input type="radio" name="1" onclick="alert(this.value);" >3</input>3
</form>
</body>
</html>
它和tapestry有些像,因为模板里面指明了组件类型,所以就不需要再添加了.
public class Index extends Page
{
@Override
public String getTemplate()
{
try
{
return FileUtils.readFileToString(new File(getSession().getServletContext().getRealPath("Index.html")));
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public void onLoad()
{
Label lab = (Label)getRoot().findComponent("lab3");
lab.addComponent(new Literal("<a href=\"#\">hi i am dynamic!!</a>"));
Integer count = (Integer)getViewSate().get("count");
if (null == count)
{
count = 0;
}
count ++;
getViewSate().put("count", count);
Button btn = new Button("Ok" + count);
lab.addComponent(btn);
}
}
Sopo则是由neko解析模板,生成页面的组件树,web.sopo.template这个包下面包含了所有的模板解析类。每个page都有一个根组件
ComponentRoot web.sopo.page.Page.getRoot(),当开始绘制的时候则会从跟组件开始绘制。它的特点是可以在程序逻辑阶段动态的改变组件树的构造,上面的例子可以看到动态的加了个链接和按钮。使用了它的viewstate特性—存贮页面级变量,这和asp.net很像。
比较
这三种都支持模板(包括页面模板和组件模板),共同特点是模板是html格式,美工可以直接编辑模板,没有讨厌的jsp标签.wicket取经于tapestry,tapestry取经于asp.net,而sopo也是学asp.net并且和它最像。Wicket需要后台add对应模板的组件,通过匹配,这样做虽然可以动态的决定绘制组件的类型但是也比较繁琐,它类似于swing的方式,但是Mode这个概念入侵很大,通过session来保持状态。tapestry通过模板和组件的绘制来展示页面,不允许你new 一个组件,并且和prototype, scriptaculous集成了,它的performance是这三个中最快的,虽然page和组件都是pojo,但是有注入依赖,到底这个特性有没有用那是见仁见智了。Sopo则是比较灵活,可以动态的修改组件树,和asp.net非常接近,写组件和tapestry一样非常清晰,概念和实现都很单,缺点是它的性能只适用于中小型应用。