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。
以下是我编写的一个简单的组件,包含了其中所有的典型的生成字节码,并注明了是由谁生成的(因为考虑到复用,有些属性由第一个生成该属性的生成类完成):
/** *//** InjectMessagesWorker */
private org.apache.tapestry.services.ComponentMessagesSource _$componentMessagesSource;
private org.apache.hivemind.Messages _$messages;
/** *//** InjectSpecificationWorker */
private org.apache.tapestry.spec.IComponentSpecification _$specification;
/** *//** SpecifiedPropertyWorker */
private java.lang.String _$sessionProperty;
private java.lang.String _$sessionProperty$default;
private java.lang.String _$specifiedProperty;
private org.apache.tapestry.enhance.InitialValueBindingCreator _$specifiedProperty$initialValueBindingCreator;
private org.apache.tapestry.IBinding _$specifiedProperty$initialValueBinding;
/** *//** ParameterPropertyWorker */
private java.lang.String _$parameter;
private java.lang.String _$parameter$Default;
private boolean _$parameter$Cached;
private java.lang.Class _class$java$lang$String;
/** *//** DispatchToInjectWorker & InjectMetaWorker */
private org.apache.tapestry.services.ComponentPropertySource _$componentPropertySource;
private org.apache.tapestry.coerce.ValueConverter _$valueConverter;
/** *//** DispatchToInjectWorker & InjectObjectWorker */
private org.apache.tapestry.engine.IEngineService _$injectObject;
/** *//** DispatchToInjectWorker & InjectScriptWorker */
private org.apache.tapestry.enhance.DeferredScript _$script;
/** *//** DispatchToInjectWorker & InjectStateWorker */
private java.lang.Object _$injectState;
/** *//** DispatchToInjectWorker & (InjectStateWorker or InjectStateFlagWorker) */
private org.apache.tapestry.engine.state.ApplicationStateManager _$applicationStateManager;
/** *//** InjectComponentWorker */
private java.lang.Class _class$org$apache$tapestry$components$Insert;
private org.apache.hivemind.Location _$injectComponent$location;
private org.apache.tapestry.components.Insert _$injectComponent;
/** *//** AbstractPropertyWorker */
private java.lang.String _$abstractProperty;
private java.lang.String _$abstractProperty$defaultValue;
/** *//** 构造函数,信息由多个相关EnhancementWorker提供 */
public $Enhance_21(org.apache.tapestry.services.ComponentMessagesSource $1,
org.apache.tapestry.spec.IComponentSpecification $2,
org.apache.tapestry.enhance.InitialValueBindingCreator $3, java.lang.Class $4,
org.apache.tapestry.services.ComponentPropertySource $5,
org.apache.tapestry.coerce.ValueConverter $6,
org.apache.tapestry.engine.IEngineService $7,
org.apache.tapestry.enhance.DeferredScript $8,
org.apache.tapestry.engine.state.ApplicationStateManager $9, java.lang.Class $10,
org.apache.hivemind.Location $11) ...{
// InjectMessagesWorker
_$componentMessagesSource = $1;
// InjectSpecificationWorker
_$specification = $2;
// ParameterPropertyWorker for properties have initial-value
_$specifiedProperty$initialValueBindingCreator = $3;
// ParameterPropertyWorker or other(公用属性只需生成一次,由最先生成的完成)
_class$java$lang$String = $4;
// DispatchToInjectWorker & InjectMetaWorker
_$componentPropertySource = $5;
_$valueConverter = $6;
// DispatchToInjectWorker & InjectObjectWorker
_$injectObject = $7;
// DispatchToInjectWorker & InjectScriptWorker
_$script = $8;
// DispatchToInjectWorker & (InjectStateWorker or InjectStateFlagWorker)
_$applicationStateManager = $9;
// InjectComponentWorker
_class$org$apache$tapestry$components$Insert = $10;
_$injectComponent$location = $11;
}
/** *//** ParameterPropertyWorker */
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;
}
}
/** *//** InjectComponentWorker */
public org.apache.tapestry.components.Insert getInjectComponent() ...{
return _$injectComponent;
}
/** *//** InjectSpecificationWorker */
public void setSpecifiedProperty(java.lang.String $1) ...{
_$specifiedProperty = $1;
}
/** *//** DispatchToInjectWorker & InjectStateFlagWorker */
public boolean isInjectStateFlag() ...{
return _$applicationStateManager.exists("visit");
}
/** *//** DispatchToInjectWorker & InjectPageWorker */
public org.apache.tapestry.IPage getInjectPage() ...{
return getPage().getRequestCycle().getPage("Home");
}
/** *//** InjectAssetWorker */
public org.apache.tapestry.IAsset getAsset() ...{
return getAsset("$template");
}
/** *//** InjectSpecificationWorker */
public org.apache.tapestry.spec.IComponentSpecification getSpecification() ...{
return _$specification;
}
/** *//** DispatchToInjectWorker & InjectMetaWorker */
public java.lang.String getInjectMeta() ...{
java.lang.String meta = _$componentPropertySource.getComponentProperty(this, "meta");
return (java.lang.String) _$valueConverter.coerceValue(meta, _class$java$lang$String);
}
/** *//** DispatchToInjectWorker & InjectObjectWorker */
public org.apache.tapestry.engine.IEngineService getInjectObject() ...{
return _$injectObject;
}
/** *//** InjectMessagesWorker */
public org.apache.hivemind.Messages getMessages() ...{
if (_$messages == null)
_$messages = _$componentMessagesSource.getMessages(this);
return _$messages;
}
/** *//** AbstractPropertyWorker */
public void setAbstractProperty(java.lang.String $1) ...{
_$abstractProperty = $1;
}
/** *//** InjectBeanWorker */
public pojo.Bean getBean() ...{
return (pojo.Bean) getBeans().getBean("bean");
}
/** *//** SpecifiedPropertyWorker */
public java.lang.String getSpecifiedProperty() ...{
return _$specifiedProperty;
}
/** *//** ParameterPropertyWorker */
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;
}
/** *//** DispatchToInjectWorker & InjectStateWorker */
public java.lang.Object getInjectState() ...{
if (_$injectState == null)
_$injectState = (java.lang.Object) _$applicationStateManager.get("visit");
return _$injectState;
}
/** *//** AbstractPropertyWorker */
public java.lang.String getAbstractProperty() ...{
return _$abstractProperty;
}
/** *//** 页面读入初始化函数,信息由多个相关EnhancementWorker提供 */
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$initialValueBinding = _$specifiedProperty$initialValueBindingCreator
.createBinding(this);
_$specifiedProperty = (java.lang.String) _$specifiedProperty$initialValueBinding
.getObject(_class$java$lang$String);
// InjectComponentWorker
_$injectComponent = (org.apache.tapestry.components.Insert) org.apache.tapestry.TapestryUtils
.getComponent(this, "injectComponent",
_class$org$apache$tapestry$components$Insert, _$injectComponent$location);
// AbstractPropertyWorker
_$abstractProperty$defaultValue = _$abstractProperty;
// InjectPageDetachListenerWorker
getPage().addPageDetachListener(this);
// InjectPageAttachListenerWorker
getPage().addPageAttachListener(this);
// InjectPageValidateListenerWorker
getPage().addPageValidateListener(this);
// InjectPageBeginRenderListenerWorker
getPage().addPageBeginRenderListener(this);
// InjectPageEndRenderListenerWorker
getPage().addPageEndRenderListener(this);
}
/** *//** DispatchToInjectWorker & InjectStateWorker */
public void setInjectState(java.lang.Object $1) ...{
_$applicationStateManager.store("visit", $1);
_$injectState = $1;
}
/** *//** SpecifiedPropertyWorker */
public java.lang.String getSessionProperty() ...{
return _$sessionProperty;
}
public void setSessionProperty(java.lang.String $1) ...{
org.apache.tapestry.Tapestry.fireObservedChange(this, "sessionProperty", $1);
_$sessionProperty = $1;
}
/** *//** ParameterPropertyWorker */
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;
}
}
/** *//** 清空使用过的属性,信息由多个相关EnhancementWorker提供 */
public void pageDetached(org.apache.tapestry.event.PageEvent $1) ...{
super.pageDetached($1);
// SpecifiedPropertyWorker
_$sessionProperty = _$sessionProperty$default;
_$specifiedProperty = (java.lang.String) _$specifiedProperty$initialValueBinding
.getObject(_class$java$lang$String);
// DispatchToInjectWorker & InjectStateWorker
_$injectState = null;
// AbstractPropertyWorker
_$abstractProperty = _$abstractProperty$defaultValue;
}
/** *//** DispatchToInjectWorker & InjectScriptWorker */
public org.apache.tapestry.IScript getInjectScript() ...{
return _$script.getScript();
}
}
下面是这个组件的java类:
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) ...{
}
}
下面是该组件的说明文件:
<! 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" >
< description >
add a description
</ description >
< bean name ="bean" class ="pojo.Bean" property ="bean" />
< property name ="specifiedProperty" initial-value ="'MindHawk'" />
< 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 >
模版文件很简单,不会对生成的代码有影响,这里就不列出来了。