文章出處

一、前言                          

  Velocity作為歷史悠久的模板引擎不單單可以替代JSP作為Java Web的服務端網頁模板引擎,而且可以作為普通文本的模板引擎來增強服務端程序文本處理能力。而且Velocity被移植到不同的平臺上,如.Net的 NVelocity和js的Velocity.js,雖然各平臺在使用和實現上略有差別,但大部分語法和引擎核心的實現是一致的,因此學習成本降低不少 哦。

  最好的學習資源——官網:http://velocity.apache.org/

  本系列打算采用如下結構對Velocity進行較為全面的學習,若有不妥或欠缺望大家提出,謝謝。

  1. 入門示例

  2. VTL語法詳解

  3. 模板與宿主環境通信

  4. 基礎配置項

  5. 深入模板引擎及調優配置

 

二、模板與宿主環境通信                   

  模板指的是使用VTL編寫的Velocity模板,宿主環境指的是Java代碼調用部分。而兩者通信的紐帶就是引擎上下文對象( VelocityContext實例 ),下面是常用的 VelocityContext實例 方法。

// 構造函數,入參為上下文的鍵值對集
VelocityContext(Map context)
// 添加上下文的鍵值對
Object put(String key, Object value)
// 從上下文獲取指定鍵的值
Object get(String key)
// 檢查上下文中是否存在指定的鍵值對
boolean containsKey(Object key)
// 獲取所有鍵
Object[] getKeys()
// 移除指定鍵
Object remove(Object key)
// 獲取上下文鏈中鄰近的上下文對象
Context getChainedContext()

 

三、宿主環境向模板傳值                    

// 1. 通過構造函數傳值
HashMap<String, String> baseCtx = new HashMap<String, String>();
baseCtx.put("version", "1");
VelocityContext ctx = new VelocityContext(baseCtx);

// 2. 通過put傳值
ctx.put("author", "fsjohnhuang");

注意鍵值對中值的數據類型為

Integer、Long等簡單數據類型的裝箱類型;

String類型

Object子類

Object[] 數組類型,從1.6開始Velocity將數組類型視為 java.util.List 類型看待,因此模板中可調用 size() 、 get(intindex) 和 isEmpty() 的變量方法;

java.util.Collection子類;

java.util.Map子類;

java.util.Iterator對象;

java.util.Enumeration對象。

除此之外,我們還可以將一個靜態類賦予到上下文對象中,如 java.lang.Math靜態類 

ctx.put("Math", java.lang.Math.class);

 

四、模板向宿主環境傳值                      

  1.  通信示例1——通過引擎上下文對象獲取變量

  模板文件frm.vm

#set($action="./submit")
<form action="$action">
  .........
</form>

  Java代碼部分

VelocityContext ctx = new VelocityContext();
VelocityEngine ve = new VelocityEngine();
StringWriter sw = new StringWriter();
ve.mergeTemplate("frm.vm", ctx, sw);
String actionStr = ctx.get("action");
System.out.println(actionStr); // 顯示./submit

 

  2.  通信示例2——通過副作用修改變量、屬性值

  模板文件change.vm

$people.put("john", "john huang")
#set($version = $version + 1)

  Java代碼部分

VelocityContext ctx = new VelocityContext();
ctx.put("version", 1);
HashMap<String, String> people = new HashMap<String,String>();
ctx.put("people", people);
VelocityEngine ve = new VelocityEngine();
StringWriter sw = new StringWriter();
ve.mergeTemplate("change.vm", ctx, sw);
System.out.println(ctx.get("version")); // 顯示2
System.out.println(people.get("john")); //顯示john huang

 上述示例表明在模板中對引用類型實例進行操作時,操作結果將影響到該引用類型實例本身,因此必須謹慎操作。

 

五、引擎上下文鏈                          

  所謂引擎上下文鏈就是將原有的上下文對象賦予給新建的上下文對象,從而達到上下文內的鍵值對復用。具體代碼如下:

VelocityContext ctx1 = new VelocityContext();
ctx1.put("name", "fsjohuang");
ctx1.put("version", 1);
VelocityContext ctx2 = new VelocityContext(ctx1);
ctx2.put("version", 2);

System.out.println(ctx2.get("name")); // 顯示fsjohnhuang
System.out.println(ctx2.get("version")); // 顯示2

    就是當前上下文對象沒有該鍵值對時,則查詢上下文鏈的對象有沒有該鍵值對,有則返回,無則繼續找鏈上的其他上下文對象,直到找到該鍵值對或遍歷完所有鏈上的上下文對象。

    但VelocityContext實例除了put、get方法外,還有remove、getKeys、containsKey方法,它們的行為又是如何的呢?下面我們通過源碼來了解吧!

 

六、從源碼看引擎上下文                        

  從源碼 public class VelocityContext extends AbstractContext implements Cloneable 得知VelocityContext繼承自AbstractContext抽象類,且實現了抽象類的internalGet、internalPut、internalContainsKey、internalGetKeys和internalRemove方法,實際開發中常用的put、get等方法卻是由AbstractContext提供的,而put、get方法內部在調用對應的internalGet、internalPut方法外再進行了其他處理。這是一種由子類提供具體實現,父類提供對外接口的設計方式(但與純面向接口編程又有些區別)。

  VelocityContext類源碼片段:

public class VelocityContext extends AbstractContext implements Cloneable
{
  private Map context = null;
  
  public Object internalGet( String key )
  {
      return context.get( key );
  }

  public Object internalPut( String key, Object value )
  {
      return context.put( key, value );
  }

  public  boolean internalContainsKey(Object key)
  {
      return context.containsKey( key );
  }

  public  Object[] internalGetKeys()
  {
      return context.keySet().toArray();
  }

  public  Object internalRemove(Object key)
  {
      return context.remove( key );
  }
}

  從上面可知VelocityContext內置一個Map實例,而各實例方法均為對該Map實例的操作。

  AbstractContext類源碼片段:

public abstract class AbstractContext extends InternalContextBase
    implements Context
{
    // 上下文對象鏈中鄰近的上下文對象
    private   Context  innerContext = null;

    // 只將鍵值對存放在當前上下文對象的Map對象中
    public Object put(String key, Object value)
    {
        /*
         * don't even continue if key is null
         */
        if (key == null)
        {
            return null;
        }
        
        return internalPut(key.intern(), value);
    }

     // 從當前上下文對象的HashMap對象獲取鍵值
     //  失敗再從上下文鏈中尋找
     public Object get(String key)
    {
        if (key == null)
        {
            return null;
        }


        Object o = internalGet( key );

        if (o == null && innerContext != null)
        {
            o = innerContext.get( key );
        }

        return o;
    }

    // 搜索整條上下文鏈的對象是否包含指定鍵值對
    public boolean containsKey(Object key)
    {
        if (key == null)
        {
            return false;
        }

        boolean exists = internalContainsKey(key);
        if (!exists && innerContext != null)
        {
            exists = innerContext.containsKey(key);
        }
        
        return exists;
    }

    // 僅返回當前上下文對象的Map對象的鍵
    public Object[] getKeys()
    {
        return internalGetKeys();
    }

    // 僅移除當前上下文對象的Map對象指定的鍵值對
    // 上下文鏈上的其他對象不受影響
     public Object remove(Object key)
    {
        if (key == null)
        {
            return null;
        }

        return internalRemove(key);
    }
}

   引擎上下文鏈就是通過AbstractContext實現的,跟JS的原型鏈相同。

 

七、總結                              

   本節簡單介紹了模板與宿主環境通信的兩種方式,并透過源碼了解了一下VelocityContext和上下文鏈的實現,但想繼續深入上下文的實現那請自行閱讀AbstractContext的父類InternalContextBase了。

   尊重原創,轉載請注明來自:http://www.cnblogs.com/fsjohnhuang/p/4114653.html  ^_^肥仔John


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()