2014-05-05 57 views
0

我想知道,是否有可能在freemarker模板模型中實現某種屬性代理/範圍繼承? 考慮一下:代理對象屬性

public class A { 
    String aProperty; 
} 

public class B { 
    String bProperty; 
    A parent; 
} 

所以在我的模板,我可以寫類似${obj.aProperty},它會無論是從obj檢索 aProperty直接,如果它有一個(objA一個實例),或檢索parent.aProperty如果它有none(objB的一個實例)。

我唯一想到的就是使用自定義BeansWrapper,但也許還有其他創新性較小的解決方案?任何幫助將不勝感激。

更新

解決方案與Proxy

public interface TemplateModelObject { 
    String getaProperty(); 
} 

public class BProxy implements InvocationHandler { 
    private B target; 

    public BProxy(B target) { 
     this.target = target; 
    } 

    @Override 
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
     if (method.getDeclaringClass().equals(TemplateModelObject.class)) { 
      return method.invoke(target.getParent(), args); 
     } else { 
      return method.invoke(target, args); 
     } 
    } 
} 

假設A implements TemplateModelObject

public class CustomWrapper extends BeansWrapper { 
    public TemplateModel wrap(Object object) throws TemplateModelException { 
     if (object != null && B.class.isAssignableFrom(object.getClass())) { 
      try { 
       return super.wrap(Proxy.newProxyInstance(TemplateModelObject.class.getClassLoader(), new Class[]{TemplateModelObject.class}, new BProxy((B) object))); 
      } catch (Exception e) { 
       throw new TemplateModelException(e); 
      } 
     } else { 
      return super.wrap(object); 
     } 
    } 
} 

我還需要一個接口聲明的B特定成員,使他們可以通過代理來訪問。但主要的問題是:不會CustomWrapper在Freemarker中打破了什麼?

回答

1

我不認爲你可以做到這一點沒有自定義ObjectWrapper。畢竟,這就是爲什麼整個ObjectWrapper概念存在。這個問題曾經是幾乎每個人都想要BeansWrapper的功能,從而以某種方式擴展,並且通常情況下,這並不容易。像這裏一樣,我的第一個猜測是你應該重寫BeanModel.get(String),以便當它返回null時,取而代之的是父對象,用BeanModel.wrapper包裝它,然後委託給該包裝對象的get(String)。但直接覆蓋BeansWrapper中的get無用,因爲它只是BeansWrapper創建的TemplateModel類的超類。相反,您必須在BeanModel的所有子類中覆蓋get,然後在BeansWrapper(如果內存服務器正常...)中覆蓋getModelFactory,以便BeansWrapper將創建TemplateModel類的實例,而不是標準類的實例。

+0

我沒有想到我需要改變所有'BeanModel'的實現,這確實使事情變得非常複雜。其實我認爲我找到了一個更簡單的解決方案,請參閱我的問題的更新。如果您能指出任何有關Freemarker的潛在問題,那將是非常好的。 – chimmi

+0

我沒有看到你在那裏擴展'BeansWrapper'的方式有什麼問題。請注意,它比我上面描述的要慢,但它肯定是比較爽的。 – ddekany

+0

實際上,我看到一個問題......如果你是'super.wrap'代理,並且稍後將該對象從模板傳回給Java方法,則它將被解包到代理實例,否則返回原始狀態目的。因此,您從模板調用的Java方法將獲取代理實例,而不是原始實例。 – ddekany

0

您可以訪問它像

使用的

${obj.aProperty} 

與B的實例的實例,

${obj.bProperty} 

${obj.parent.aProperty} // if parent is null then you will get error 

//因此首先檢查父對象不爲空,然後訪問它屬性

<#if obj.parent?has_content > 
${obj.parent.aProperty} 
</#if> 
+0

這正是我想要避免的,謝謝。 – chimmi

0

這將適用於您的基本示例。該模板包含一個將對象傳遞給的函數和該屬性的名稱。它會檢查屬性是否存在,如果不存在,它會從父級獲取相同的屬性。您需要擴展它以進一步檢查父項是否存在或者是否需要多個級別。

A.java

package com.cluboramic.web.controllers; 

public class A { 

    private String property; 

    public String getProperty() { 
     return property; 
    } 

    public void setProperty(String property) { 
     this.property = property; 
    } 

} 

B.java

package com.cluboramic.web.controllers; 

public class B { 

    private String property; 

    private A parent; 

    public String getProperty() { 
     return property; 
    } 

    public void setProperty(String property) { 
     this.property = property; 
    } 

    public A getParent() { 
     return parent; 
    } 

    public void setParent(A parent) { 
     this.parent = parent; 
    } 

} 

對象設置

A a = new A(); 
a.setProperty("This is A"); 

B b1 = new B(); 
b1.setParent(a); 

B b2 = new B(); 
b2.setProperty("This is B2"); 
b2.setParent(a); 

模板

a = ${proxyProp(a "property")}, 

b1 = ${proxyProp(b1 "property")}, 

b2 = ${proxyProp(b2 "property")} 

<#-- 
    obj is the object to access 
    property is the name of the property 
--> 
<#function proxyProp obj property> 
    <#if obj[property]??> 
     <#return obj[property]> 
    <#else> 
     <#return obj.parent[property]> 
    </#if> 
</#function> 

輸出

a = This is A, b1 = This is A, b2 = This is B2