Tapestry4改進運行效率的實現方法

來源:互聯網
上載者:User

    在Tapestry4之前的版本,Tapestry使用了大量的動態調用(大部分是使用OGNL調用的),這樣勢必會造成大量運行效率的損失。好在大多數WEB程式的瓶頸是在訪問資料庫而不是在頁面上,所以並沒有對Tapestry的推廣構成毀滅性的影響。但是隨著Tapestry社區的發展,使用人群的增加,Howard Lewis Ship(Tapestry的作者)和一些支援Tapestry項目的開發人員,意識到了這個問題。並作了大量的工作對效能進行改進。
    在Tapestry引入HiveMind之後,Tapestry的活力明顯的增強了,第三方支援包也越來越豐富。這種豐富不僅僅是組件的豐富,而是包括架構在內。其它人可以很容易的為Tapestry提供高效的服務。比方說在《使用tapestry-prop提高Tapestry運行效率》中就提到了使用代碼產生的方式來改進OGNL綁定效率低的問題。tapestry-prop是通過對綁定規則的擴充提升系統效率的。
    除了第三方支援外,Howard Lewis Ship本人對整個架構的低層實現也作了很大的改進,大大減少了動態調用的使用。對架構的改進主要有兩個地方,一個是對頁面和組件定義的屬性的改進,一個對參數綁定的改進。
頁面和組件屬性的改進
    在Tapestry3的時候,屬性在初始化和丟棄(頁面從頁面池中取出或還回之前)都需要採用動態調用的方式設定預設值或使用者定義的預設值。可想而只當頁面使用屬性越多效能損失肯定也就會越多。
    在Tapestry4裡,對屬性的這種初始化工作有了很大的變化。首先,是代碼產生的結構做了調整,採用了EnhancementWorker(多用於屬性的產生)和InjectEnhancementWorker(用於各類注入的產生)兩個介面定義的方法對頁面進行代碼產生的工作。這樣通過HiveMind的定義就可以很容易的擴充代碼產生的方式。其次,產生的屬性不是只包含定義的屬性,而是而外還會產生一個與它對應的預設值。這樣在初始化資料的時候就可以直接通過預設值賦值而不用通過動態調用。不過要注意的是如果定義的屬性有預設值的話情況就不同了,架構就不得不使就得使用綁定規則來產生值了,如果採用的是OGNL的規則初始化資料動態調用也就不可避免了。在後面會介紹一種不需要動態調用設定複雜初始值的方法。
    下面是我寫了一個簡單的頁面代碼產生後的形式(所有代碼全是產生的)。
public class $Hawk_2_116 extends org.apache.tapestry.workbench.hawk.Hawk_2 implements
  org.apache.tapestry.event.PageDetachListener {
//下面三個屬性每個頁面都會產生,與頁面屬性的定義無關
 private org.apache.tapestry.services.ComponentMessagesSource _$componentMessagesSource;

 private org.apache.hivemind.Messages _$messages;

 private org.apache.tapestry.spec.IComponentSpecification _$specification;
     //頁面中我注入了一個Spring的bean
 private org.springframework.beans.factory.BeanFactory _$beanFactory;
     //與_$beanFactory配合使用,主要用於出錯提示
 private org.apache.hivemind.Location _$location;
     //頁面中定義了一個value屬性,代碼產生後的形式
 private java.lang.String _$value;
     //用於儲存value屬性預設值的屬性
 private java.lang.String _$value$defaultValue;

    //建構函式。所有頁面必須的屬性或注入的屬性都在初始化時確定
 public $Hawk_2_116(org.apache.tapestry.services.ComponentMessagesSource $1,
   org.apache.tapestry.spec.IComponentSpecification $2,
   org.springframework.beans.factory.BeanFactory $3, org.apache.hivemind.Location $4) {
  _$componentMessagesSource = $1;
  _$specification = $2;
  _$beanFactory = $3;
  _$location = $4;
 }
     //響應頁面分離事件,用於初始化屬性
 public void pageDetached(org.apache.tapestry.event.PageEvent $1) {
  _$value = _$value$defaultValue;
 }

 public org.apache.tapestry.spec.IComponentSpecification getSpecification() {
  return _$specification;
 }
     //擷取注入的bean對象
 public com.kft.util.Decoder getDecoder() {
  try {
   return (com.kft.util.Decoder) _$beanFactory.getBean("Decoder");
  } catch (Exception ex) {
   throw new org.apache.hivemind.ApplicationRuntimeException(ex.getMessage(), _$location,ex);
  }
 }

 public org.apache.hivemind.Messages getMessages() {
  if (_$messages == null)
   _$messages = _$componentMessagesSource.getMessages(this);
  return _$messages;
 }
    //擷取定義的value屬性
 public java.lang.String getValue() {
  return _$value;
 }
    //頁面剛產生時調用,只調一次
 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);
         //設定預設值,因為父類方法先調用,所以在父類方法中可以設定value的值
  _$value$defaultValue = _$value;
  getPage().addPageDetachListener(this);
  getPage().addPageBeginRenderListener(this);
 }
     //設定value屬性
 public void setValue(java.lang.String $1) {
  _$value = $1;
 }
}
    因為我沒有定義含有初始值的屬性,所以產生之後的代碼很沒有任何動態調用的痕迹。從上例中可以看出,注入的屬性在初使化時就已經賦值了,不會再改動(在也為什麼注入Spring的bean時需要使用BeanFactory擷取的原因)。而對於屬性就特別一點了。首先,在頁面剛產生的時候會調用一次finishLoad()方法,用於設定各屬性的初始值(該方法會首先調用父類的方法,因此可以把屬性初始值放在這個函數裡面定義,這樣就不會存在動態調用)。之後,在頁面還回池中之前會調用pageDetached()方法,將所有屬性設定為初始值。通過以上兩點的改進就消除了屬性初始化對動態調用的依賴,效能得到大大的改進。
參數綁定的改進
    在Tapestry3的時候定義參數時必須申明參數的方向。這樣的設定既不便於理解,也會造成不必要的浪費。因為對於in為代表參數方向在調用之前需要一個設值的過程,這個過程會涉及到大量的動態調用,而且這些參數是不能在設值之前調用。
    在Tapestry4裡參數的實現成熟了很多,不再有方向的限制,不需要使用前的賦值過程等。它會直接的和綁定資料關聯,而且還會對資料有緩衝。這樣一來,任何時候使用參數都不會出錯,任何時候讀取參數都不會重複調用綁定規則。即使使用變得方便,不需要理解複雜的函數方向問題,又減少了動態調用的次數。特別的如果和tapestry-prop配合使用效果會更好(tapestry-prop是一種代碼產生綁定技術)。
    下面是我編寫的一個簡單組件產生後的代碼(所有代碼全是產生的)。
public class $HawkCom_25 extends org.apache.tapestry.workbench.components.hawk.HawkCom {
     //下面三個屬性每個頁面都會產生,與頁面屬性的定義無關
 private org.apache.tapestry.services.ComponentMessagesSource _$componentMessagesSource;

 private org.apache.hivemind.Messages _$messages;

 private org.apache.tapestry.spec.IComponentSpecification _$specification;
     //定義的一個參數
 private java.lang.Object _$value;
     //value參數的預設值
 private java.lang.Object _$value$Default;
     //value是否已經緩衝
 private boolean _$value$Cached;
     //value的類類型,使用綁定規則時需要。此處的值為java.lang.Object
 private java.lang.Class _class$java$lang$Object;
     //建構函式,所有頁面需要和不變的資料此時賦值
 public $HawkCom_25(org.apache.tapestry.services.ComponentMessagesSource $1,
   org.apache.tapestry.spec.IComponentSpecification $2, java.lang.Class $3) {
  _$componentMessagesSource = $1;
  _$specification = $2;
  _class$java$lang$Object = $3;
 }

 public org.apache.hivemind.Messages getMessages() {
  if (_$messages == null)
   _$messages = _$componentMessagesSource.getMessages(this);
  return _$messages;
 }

 public org.apache.tapestry.spec.IComponentSpecification getSpecification() {
  return _$specification;
 }
     //設定value參數
 public void setValue(java.lang.Object $1) {
    //這個判斷主是要是為了在finishLoad()方法裡能設定參數的初始值
  if (!isInActiveState()) {
   _$value$Default = $1;
   return;
  }
  org.apache.tapestry.IBinding binding = getBinding("value");
  if (binding == null)
   throw new org.apache.hivemind.ApplicationRuntimeException(
     "Parameter 'value' is not bound and can not be updated.");
  binding.setObject((java.lang.Object) $1);
  if (isRendering()) {
   _$value = $1;
   _$value$Cached = true;
  }
 }
     //擷取value參數
 public java.lang.Object getValue() {
  if (_$value$Cached)
   return _$value;
  org.apache.tapestry.IBinding binding = getBinding("value");
  if (binding == null)
   return _$value$Default;
  java.lang.Object result = (java.lang.Object) binding.getObject(_class$java$lang$Object);
  if (isRendering() || binding.isInvariant()) {
   _$value = result;
   _$value$Cached = true;
  }
  return result;
 }

     //清除緩衝的資料,修改緩衝標誌
 public void cleanupAfterRender(org.apache.tapestry.IRequestCycle $1) {
  super.cleanupAfterRender($1);
  org.apache.tapestry.IBinding valueBinding = getBinding("value");
  if (_$value$Cached && !valueBinding.isInvariant()) {
   _$value$Cached = false;
   _$value = _$value$Default;
  }
 }
}
    從例子中可以看出,沒有了動態調用,沒有了參數使用前的賦值,所有的參數都直接通過綁定規則與實際資料關聯,並且在頁面被啟用並且在render的時候組件還會緩衝參數的資料。使用過後緩衝將會清空,不影響下一次的使用。另外,還有一點從上面代碼中體現不出來。那就是組件參數的預設值和屬性一樣也是可以在finishLoad函數裡面初始化的。這樣就使動態調用的次數減到了最小。
進一步提高效率的小技巧
1。對於大多數的幫定可以使用tapestry-prop的"prop:"幫定代替"ognl:"的幫定,並可以通過設定使整個應用預設使用"prop:"幫定(當然如果是真箇應用預設使用"prop:"可能會有一定的危險)。
2。對於必須初始化的屬性或參數可以考慮寫在finishLoad方法裡面,而不是用OGNL的運算式定義。
3。使用Form系列的組件(Form和必須在Form中使用的組件)最好給id這個參數賦值,特別是在頁面資訊比較多的時候(使用null值是一個比較好的方法,因為一般情況下name和id的作用是相似的)。因為目前所有的Form系列組件大多有這個參數,並且使用的是用OGNL初始化的方式。
    通過以上改進,剩下的動態調用就很少了。如果監聽函數(listener)的調用也能得到改進的話,估計就沒有什麼需要動態調用的了! 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.