<script type="text/javascript"> document.location.href="http://blog.csdn.net/mindhawk/archive/2006/12/16/1445012.aspx" target="_blank" rel="external nofollow" ; </script>
Tapestry最大的的一個特點就是通過位元組碼生成在運作時動态的建立頁面的執行個體。這也是整個架構能有如此活力的基礎。通過運作時的代碼生成不僅使說明(specification),模闆(template)和類結合起來形成一個統一的整體,更主要的是使程式員得到了極大的解放。所有重複性的,易出錯的煩瑣工作都有架構負責,動态的将必須的資訊賦給頁面。程式員需要的僅僅是關心一些最為基本的設定。
Tapestry4對這個生成的過程作了一個較大的調整。和其它改進類似,頁面生成也是通過HiveMind來組織的。将各種不同的生成政策使用接口分離開,然後再通過HiveMind的服務和配置組織在一起。
與代碼生成有關的主要對象大緻可以分為三類。第一個是工廠方法,負責共的生成排程緩存的工作。另一類是基本生成工作類(enhance),負責各種不同的類型的代碼生成工作。最後一個是對基本生成工作類的一個擴充,主要用于生成注入屬性的位元組碼。至于如何實際生成位元組碼可以參見javassist。
工廠方法(ComponentConstructorFactory):整個項目隻有一個執行個體,主要是提供一個統一的服務接口。
基本生成類(EnhancementWorker):通過HiveMind組成成了一個責任鍊的形式(四人幫的 chain 模式),生成時會逐個調用各個實作類生成具體位元組碼。
注入擴充類(InjectEnhancementWorker):這個擴充由一個基本生成類調用。由key/value的形式儲存組織,使用者可以根據自己的需要添加實作。key是該擴充的名稱,value是具體的實作類。
上圖沒有列出所有的實作類。EnhancementWorker接口的主要實作有十多類,分别負責資源資訊(InjectMessagesWorker)、說明(InjectSpecificationWorker)、定義屬性(SpecifiedPropertyWorker)、參數(ParameterPropertyWorker)、注入配置設定(DispatchToInjectWorker)、元件(InjectComponentWorker)、Bean(InjectBeanWorker)、Asset(InjectAssetWorker)、抽象屬性(AbstractPropertyWorker)、頁面的接口(pageBeginRender等五類接口,InjectPageDetachListenerWorker,InjectPageAttachListenerWorker,InjectPageValidateListenerWorker,InjectPageBeginRenderListenerWorker,InjectPageEndRenderListenerWorker)等的生成工作。這十多個類的組成一個有序的 chain,抽象屬性是這個序列的分水嶺。大體順序與我上面提到的一緻,在抽象屬性前的為一個集合,每一個都必須在抽象屬性之前,但是他們之間沒有順序的限定。抽象屬性之後的是一個集合。AnnotationEnhancementWorker 比較特殊,隻有在java5以上才有效,它實際上是其它EnhancementWorker的一個排程。如果存在它會排在這個 chain 的最前面。
注入配置設定(DispatchToInjectWorker)有它自己另外一套的配置,主要使用者便于使用者擴充注入的屬性。架構提供的主要有六類。InjectMetaWorker,InjectObjectWorker,InjectPageWorker,InjectScriptWorker,InjectStateFlagWorker,InjectStateWorker。
以下是我編寫的一個簡單的元件,包含了其中所有的典型的生成位元組碼,并注明了是由誰生成的(因為考慮到複用,有些屬性由第一個生成該屬性的生成類完成):
public class $Enhance_21 extends com.kft.tapestry.components.Enhance ... {
private org.apache.tapestry.services.ComponentMessagesSource _$componentMessagesSource;
private org.apache.hivemind.Messages _$messages;
private org.apache.tapestry.spec.IComponentSpecification _$specification;
private java.lang.String _$sessionProperty;
private java.lang.String _$sessionProperty$default;
private java.lang.String _$specifiedProperty;
private java.lang.String _$specifiedProperty$default;
private java.lang.String _$parameter;
private java.lang.String _$parameter$Default;
private boolean _$parameter$Cached;
private java.lang.Class _class$java$lang$String;
private org.apache.tapestry.services.ComponentPropertySource _$componentPropertySource;
private org.apache.tapestry.coerce.ValueConverter _$valueConverter;
private org.apache.tapestry.engine.IEngineService _$injectObject;
private org.apache.tapestry.enhance.DeferredScript _$script;
private java.lang.Object _$injectState;
private org.apache.tapestry.engine.state.ApplicationStateManager _$applicationStateManager;
private java.lang.Class _class$org$apache$tapestry$components$Insert;
private org.apache.hivemind.Location _$injectComponent$location;
private org.apache.tapestry.components.Insert _$injectComponent;
private java.lang.String _$abstractProperty;
private java.lang.String _$abstractProperty$defaultValue;
public $Enhance_21(org.apache.tapestry.services.ComponentMessagesSource $1,
org.apache.tapestry.spec.IComponentSpecification $2, java.lang.Class $3,
org.apache.tapestry.services.ComponentPropertySource $4,
org.apache.tapestry.coerce.ValueConverter $5,
org.apache.tapestry.engine.IEngineService $6,
org.apache.tapestry.enhance.DeferredScript $7,
org.apache.tapestry.engine.state.ApplicationStateManager $8, java.lang.Class $9,
org.apache.hivemind.Location $10) ...{
// InjectMessagesWorker
_$componentMessagesSource = $1;
// InjectSpecificationWorker
_$specification = $2;
// ParameterPropertyWorker or other(公用屬性隻需生成一次,由最先生成的完成)
_class$java$lang$String = $3;
// DispatchToInjectWorker & InjectMetaWorker
_$componentPropertySource = $4;
_$valueConverter = $5;
// DispatchToInjectWorker & InjectObjectWorker
_$injectObject = $6;
// DispatchToInjectWorker & InjectScriptWorker
_$script = $7;
// DispatchToInjectWorker & (InjectStateWorker or InjectStateFlagWorker)
_$applicationStateManager = $8;
// InjectComponentWorker
_class$org$apache$tapestry$components$Insert = $9;
_$injectComponent$location = $10;
}
public void cleanupAfterRender(org.apache.tapestry.IRequestCycle $1) ...{
super.cleanupAfterRender($1);
org.apache.tapestry.IBinding parameterBinding = getBinding("parameter");
if (_$parameter$Cached && !parameterBinding.isInvariant()) ...{
_$parameter$Cached = false;
_$parameter = _$parameter$Default;
}
}
public org.apache.tapestry.components.Insert getInjectComponent() ...{
return _$injectComponent;
}
public void setSpecifiedProperty(java.lang.String $1) ...{
_$specifiedProperty = $1;
}
public boolean isInjectStateFlag() ...{
return _$applicationStateManager.exists("visit");
}
public org.apache.tapestry.IPage getInjectPage() ...{
return getPage().getRequestCycle().getPage("Home");
}
public org.apache.tapestry.IAsset getAsset() ...{
return getAsset("$template");
}
public org.apache.tapestry.spec.IComponentSpecification getSpecification() ...{
return _$specification;
}
public java.lang.String getInjectMeta() ...{
java.lang.String meta = _$componentPropertySource.getComponentProperty(this, "meta");
return (java.lang.String) _$valueConverter.coerceValue(meta, _class$java$lang$String);
}
public org.apache.tapestry.engine.IEngineService getInjectObject() ...{
return _$injectObject;
}
public org.apache.hivemind.Messages getMessages() ...{
if (_$messages == null)
_$messages = _$componentMessagesSource.getMessages(this);
return _$messages;
}
public void setAbstractProperty(java.lang.String $1) ...{
_$abstractProperty = $1;
}
public pojo.Bean getBean() ...{
return (pojo.Bean) getBeans().getBean("bean");
}
public java.lang.String getSpecifiedProperty() ...{
return _$specifiedProperty;
}
public java.lang.String getParameter() ...{
if (_$parameter$Cached)
return _$parameter;
org.apache.tapestry.IBinding binding = getBinding("parameter");
if (binding == null)
return _$parameter$Default;
java.lang.String result = (java.lang.String) binding.getObject(_class$java$lang$String);
if (isRendering() || binding.isInvariant()) ...{
_$parameter = result;
_$parameter$Cached = true;
}
return result;
}
public java.lang.Object getInjectState() ...{
if (_$injectState == null)
_$injectState = (java.lang.Object) _$applicationStateManager.get("visit");
return _$injectState;
}
public java.lang.String getAbstractProperty() ...{
return _$abstractProperty;
}
public void finishLoad(org.apache.tapestry.IRequestCycle $1,
org.apache.tapestry.engine.IPageLoader $2,
org.apache.tapestry.spec.IComponentSpecification $3) ...{
super.finishLoad($1, $2, $3);
// SpecifiedPropertyWorker
_$sessionProperty$default = _$sessionProperty;
_$specifiedProperty$default = _$specifiedProperty;
// InjectComponentWorker
_$injectComponent = (org.apache.tapestry.components.Insert) org.apache.tapestry.TapestryUtils
.getComponent(this, "injectComponent",
_class$org$apache$tapestry$components$Insert, _$injectComponent$location);
_$abstractProperty$defaultValue = _$abstractProperty;
// InjectPageDetachListenerWorker
getPage().addPageDetachListener(this);
// InjectPageAttachListenerWorker
getPage().addPageAttachListener(this);
// InjectPageValidateListenerWorker
getPage().addPageValidateListener(this);
// InjectPageBeginRenderListenerWorker
getPage().addPageBeginRenderListener(this);
// InjectPageEndRenderListenerWorker
getPage().addPageEndRenderListener(this);
}
public void setInjectState(java.lang.Object $1) ...{
_$applicationStateManager.store("visit", $1);
_$injectState = $1;
}
public java.lang.String getSessionProperty() ...{
return _$sessionProperty;
}
public void setSessionProperty(java.lang.String $1) ...{
org.apache.tapestry.Tapestry.fireObservedChange(this, "sessionProperty", $1);
_$sessionProperty = $1;
}
public void setParameter(java.lang.String $1) ...{
if (!isInActiveState()) ...{
_$parameter$Default = $1;
return;
}
org.apache.tapestry.IBinding binding = getBinding("parameter");
if (binding == null)
throw new org.apache.hivemind.ApplicationRuntimeException(
"Parameter 'parameter' is not bound and can not be updated.");
binding.setObject($1);
if (isRendering()) ...{
_$parameter = $1;
_$parameter$Cached = true;
}
}
public void pageDetached(org.apache.tapestry.event.PageEvent $1) ...{
super.pageDetached($1);
// SpecifiedPropertyWorker
_$sessionProperty = _$sessionProperty$default;
_$specifiedProperty = _$specifiedProperty$default;
// DispatchToInjectWorker & InjectStateWorker
_$injectState = null;
// AbstractPropertyWorker
_$abstractProperty = _$abstractProperty$defaultValue;
}
public org.apache.tapestry.IScript getInjectScript() ...{
return _$script.getScript();
}
}
下面是這個元件的java類:
package com.kft.tapestry.components;
import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.IPage;
import org.apache.tapestry.IScript;
import org.apache.tapestry.components.Insert;
import org.apache.tapestry.engine.IEngineService;
import org.apache.tapestry.engine.PageService;
import org.apache.tapestry.event.PageAttachListener;
import org.apache.tapestry.event.PageBeginRenderListener;
import org.apache.tapestry.event.PageDetachListener;
import org.apache.tapestry.event.PageEndRenderListener;
import org.apache.tapestry.event.PageEvent;
import org.apache.tapestry.event.PageValidateListener;
import pojo.Bean;
public abstract class Enhance extends BaseComponent implements PageBeginRenderListener,
PageEndRenderListener, PageAttachListener, PageDetachListener, PageValidateListener ... {
public abstract String getAbstractProperty();
public abstract String getSpecifiedProperty();
public abstract String getSessionProperty();
public abstract String getParameter();
public abstract Insert getInjectComponent();
public abstract Bean getBean();
public abstract IAsset getAsset();
public abstract IPage getInjectPage();
public abstract String getInjectMeta();
public abstract IEngineService getInjectObject();
public abstract IScript getInjectScript();
public abstract Object getInjectState();
public abstract boolean isInjectStateFlag();
public void pageAttached(PageEvent event) ...{
}
public void pageBeginRender(PageEvent event) ...{
}
public void pageValidate(PageEvent event) ...{
}
public void pageDetached(PageEvent event) ...{
}
}
下面是該元件的說明檔案:
<? xml version="1.0" ?>
<! DOCTYPE component-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 4.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_4_0.dtd" >
< component-specification class ="com.kft.tapestry.components.Enhance" >
< bean name ="bean" class ="pojo.Bean" property ="bean" />
< property name ="specifiedProperty" />
< property name ="sessionProperty" persist ="session" />
< parameter name ="parameter" />
< component id ="injectComponent" type ="Insert" property ="injectComponent" >
< binding name ="value" value ="literal:mindhawk" />
</ component >
< asset name ="$template" path ="context:WEB-INF/component/Enhance.html" property ="asset" />
< inject property ="injectPage" type ="page" object ="Home" />
< meta key ="meta" value ="meta" />
< inject property ="injectMeta" type ="meta" object ="meta" />
< inject property ="injectObject" object ="engine-service:page" />
< inject property ="injectScript" type ="script" object ="Enhance.script" />
< inject property ="injectState" type ="state" object ="visit" />
< inject property ="injectStateFlag" type ="state-flag" object ="visit" />
</ component-specification >
模版檔案很簡單,不會對生成的代碼有影響,這裡就不列出來了。