veclocity宏的问题探索

最近使用velocity的宏进行类目树的展示,但是却发现了一些离奇的问题,发现velocity的宏没那么好使,表面现象是:宏里面没有独立的变量作用域,宏的参数传递是引用传递。关于这个问题,我专门做了个小测试。测试如下。

经过测试后发现基本和一开是的猜测一致。本来怀疑就是velocity宏的功能有限了,但是查了资料后发现其实并非如此。

1.宏的传参:

宏的参数其实是按名称传递的(pass by name),这种方式和我们java的函数值传参不同,velocimacro的参数传递有点类似引用传递。这种方式就是说只有当宏开始执行时候才去解析参数,加入参数是一个函数调用,并且在宏里面会有多次出现该参数,那么每次解析都调用一次。要想限制这种动态的效果,可以在调用宏之前用#set($tmp=$xx)来把xx参数先解析一遍。

#macro( callme $a )
  $a $a $a
#end

##这里效果是:bar()被调用了3此,加入$foo是有状态的而bar()会改变其状态,那么将会改变3次。
#callme( $foo.bar() )

##这里就不会出现上述效果,只会执行一次bar()
#set($tmp = $foo.bar())
#callme( $tmp )


2.宏的变量作用域:

其实velocimacro的作用域是可以设置的。默认宏是能接受到调用者的环境的。就是外部变量在宏里面是可见的,并能被修改。要想让宏的作用域只在自己范围里面,那么可以将velocity的属性velocimacro.context.localscope设置为false即可。见上面代码的测试效果。

参考资料显示——“context.localscope属性用于定义#set指令影响在Velocimacro 中使用Velocity上下文的方式。当属性值为true时,Velocimacro将有效接收它自己本地的上下文。调用者的对象上下文关键字对Velocimacro来说不可见,转而变成使用来自Velocimacro内部的上下文,而且不复制回转给调用者。与此相反,当属性值为false时,将把调用者的上下文放到一个Velocimacro可达到的、可更改的地方。也就是说Velocimacro将使用调用者的上下文。默认值为false。”下面是具体测试过程:在eclipse中单独起了个java工程,java代码如下:其中包含了3个测试用例testVarAssign()用来测试velocity的变量赋值和宏的作用域的testMacroRec()用来测试velocity的递归用的


下面是具体测试过程:

在eclipse中单独起了个java工程,java代码如下:
其中包含了3个测试用例
testVarAssign()用来测试velocity的变量赋值和宏的作用域的
testMacroRec()用来测试velocity的递归用的

package org.nx.velocity;

import java.io.StringWriter;
import java.util.Properties;

import junit.framework.TestCase;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.junit.Test;

public class VariableTest extends TestCase {

    private VelocityEngine ve         = new VelocityEngine();
    private Properties     properties = new Properties();
    private String         VM_PATH;

    {
        VM_PATH = this.getClass().getResource("/vm").getPath();
        properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, VM_PATH);
        properties.setProperty(Velocity.VM_CONTEXT_LOCALSCOPE, "true");
        try {
            ve.init(properties);
        } catch (Exception e) {
            assertTrue(false);
        }
    }

    @Test
    public void testVarAssign() {

        try {
            VelocityContext context = new VelocityContext();
            Template template = ve.getTemplate("testAssign.vm", "gbk");
            StringWriter sw = new StringWriter();
            template.merge(context, sw);
            System.out.println(sw.toString());
            // assertEquals("2", sw.toString());
        } catch (Exception e) {
            e.printStackTrace();
            assertTrue(false);
        }
    }

    @Test
    public void testMacroRec() {

        try {
            VelocityContext context = new VelocityContext();
            Template template = ve.getTemplate("testMacroRec.vm", "gbk");
            StringWriter sw = new StringWriter();
            template.merge(context, sw);
            System.out.println(sw.toString());
        } catch (Exception e) {
            e.printStackTrace();
            // assertTrue(false);
        }
    }
}


testAssign.vm代码如下:

#macro (change $var)


    #set($var = 1)set var = 1 [var=$var]
    #set($someVarOutside = 100)set someVarOutside = 100 [someVarOutside=$someVarOutside]
#end




case1:常规赋值
#set( $a = 1 )set a=1 [ a=$a ]
#set( $a = $a + 1)set a=a+1 [ a=$a ]
结论:正常


case2:多变量重复赋值
 #set( $a1 = 1000 )set a1=1000 [a1=$a1]
 #set( $a2 = $a1 )set a2=a1 [a2=$a2]
 #set( $a3 = 2222 )set a3=2222 [a3=$a3] 
 #set( $a1 = $a3 )set a1=a3 [a1=$a1,a2=$a2]
结论:2变量没相互影响


case3:已初始化b=2,经过macro后,观察macro是否影响外部b的值
#set($b=2) set b=2 [b=$b]
 begin-macro#change($b)
 after-macro[ b=$b ]
结论:影响,估计是引用传递


case4:已初始化外部var=0,经过macro设置内部var=1后,观察macro外部var的值
 #set($var = 0)set var=0 [var=$var]
 begin-macro#change($b)
 after-macro [var=$var]
结论:只要不是指向同一个变量,宏内外部变量相互不影响


case5:已初始化外部someVarOutside=0,经过macro设置内部someVarOutside=100后,观察macro外部someVarOutside的值
 #set($someVarOutside = 1)set someVarOutside=1 [someVarOutside=$someVarOutside]
 begin-macro#change($b)
 after-macro [someVarOutside=$someVarOutside]
结论:宏里面的变量都是全局的(velocimacro.context.localscope=true下恰好相反)

testMacroRec.vm代码如下:

#macro(change $var)
 #if ($var < 10)
 #set($var = $var + 1)
 $var
 #set($tmp = 0)
 $tmp=$var
 #set($tmp = $var)
 #change($tmp)
 $var
 #end
#end

#change(0)


参考文章:

某人根据《Mastering Apache Velocity》翻译的文章http://www.blogjava.net/kingwee/articles/235339.html

官网的说明文档http://people.apache.org/~henning/velocity/html/ch07.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值