问题
I've got a couple Velocity macros like this:
#macro(Alpha)
#set($p = 1)
#@Beta()
$p // 1
$bodyContent
#end
#end
#macro(Beta $params)
#set($p = 2)
$p // 2
$bodyContent
#end
And I'm using them like so:
#set($p = 0)
#@Alpha(...)
$p // 3
#end
I believe this renders like so (ignore formatting): 2, 2, 2
But I'd like to have proper closure behavior, including more locally scoped names hiding parent scope names. In particular, the usage of $p labelled '3' should refer to the value 0, '2' to the value 2, and '1' to the value 1.
Given proper closure semantics, it would print: 2, 1, 0
Is there any way to get this, or a way to implement a custom directive / modify #macro directive behavior to achieve this?
回答1:
Velocity is a templating engine, not a proper programming language, so it's a bit harder to grasp how it works.
Macros are not functions like in Java or C, meaning that invoking a macro isn't going to create a new segment on the stack for local variables; velocity works with a context, and most of the time there's just one global context.
Still, there are two ways of dealing with local variables:
- There's the velocimacro.context.localscope configuration parameter that prevents changing global variables from within macros; note that this setting is deprecated and will be removed in Velocity 2.0
- You can use the
$macro
variable as a local container for private variables if you enable themacro.provide.scope.control
configuration parameter
Still, there's another problem that would prevent your code from running correctly: Velocity macros work mostly as call-by-macro expansion, which means that the body passed to a macro isn't evaluated first, then passed to the macro; it will be evaluated dynamically when the nested macro is executed. The code behaves as:
#macro(Alpha)
#set($macro.p = 1) -> $macro refers to Alpha
$p -> $p will always be 0, since we're changing a local variable
$macro.p -> 1 here
$bodyContent -> Here $p is used, and 0 is printed
#@Beta()
$macro.p -> it is 1 now, but when this line will be actually evaluated, $macro will refer to Beta, so 2 will be printed
$bodyContent -> It's the content of the Beta macro, which triggers an infinite recursion; it's not $p as above
#end
#end
#macro(Beta)
#set($macro.p = 2) -> $macro refers to Beta
$macro.p -> 2 here
$bodyContent -> the $bodyContent that was passed into the $bodyContent is evaluated now, so it causes a recursion
#end
#set($p = 0)
#@Alpha()
$p -> Global variable
#end
来源:https://stackoverflow.com/questions/12519289/closure-in-velocity-template-macros