Groovy引擎脚本合并问题:预编译静态脚本与动态脚本如何结合?
Absolutely! You can merge your static, pre-compilable Groovy code with dynamic, parameter-dependent logic into a single executable flow—while retaining the performance boost of pre-compiling the static portion. Here’s a practical, working approach to make this happen:
Step 1: Pre-Compile Your Static Script in @PostConstruct
First, compile your static logic once during bean initialization (via @PostConstruct) so you don’t recompile it on every execution. This turns your static code into a reusable Script class that you can instantiate whenever you need to run the full logic.
import groovy.lang.GroovyClassLoader; import groovy.lang.Script; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.Map; @Component public class CombinedScriptExecutor { // Store the pre-compiled static script class private Class<? extends Script> staticScriptClass; @PostConstruct public void precompileStaticLogic() throws Exception { // Your static, non-parameter-dependent script code String staticScriptSource = """ // Static utility methods, fixed calculations, or shared context def computeBaseFactor() { // Example fixed logic: could be complex static rules return 1.5; } def formatOutput(String rawText) { return "Processed: " + rawText.trim().toUpperCase(); } """; // Compile once and store the class GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader()); staticScriptClass = classLoader.parseClass(staticScriptSource); }
Step 2: Merge with Dynamic Logic at Runtime
When you need to execute the full flow, instantiate the pre-compiled static script, inject user parameters into its binding, then evaluate your dynamic script within that context. The dynamic code will have full access to the static methods and variables you pre-defined.
public Object executeFullFlow(Map<String, Object> userParameters) throws Exception { // Create an instance of the pre-compiled static script Script staticScriptInstance = staticScriptClass.getDeclaredConstructor().newInstance(); // Inject user parameters into the script's binding (makes them available to dynamic code) staticScriptInstance.getBinding().setVariables(userParameters); // Your dynamic, parameter-dependent script code String dynamicScriptSource = """ // Use static methods + user parameters to compute the result def calculatedValue = computeBaseFactor() * userInputNumber; def formattedResult = formatOutput(userInputText); // Return a combined result (can be any object) return [calculated: calculatedValue, formatted: formattedResult]; """; // Execute the dynamic script within the static script's context return staticScriptInstance.evaluate(dynamicScriptSource); } }
Why This Works
- Performance: The static portion is compiled once at startup, so you avoid the overhead of re-parsing and compiling that code on every execution. The dynamic portion is small and evaluated quickly against the pre-built context.
- Seamless Integration: The dynamic script runs in the same binding as the static script, so it can directly call static methods, access static variables, and use the user-provided parameters without any extra setup.
Optional Optimizations
If your dynamic script’s structure is fixed (only the values change, not the logic), you can pre-compile it into a Closure instead of evaluating a string each time. For example:
// Pre-compile dynamic closure in @PostConstruct (if structure is fixed) private Closure dynamicClosure; @PostConstruct public void precompileAll() throws Exception { // ... (precompile static script as before) // Pre-compile dynamic closure Script tempScript = staticScriptClass.getDeclaredConstructor().newInstance(); dynamicClosure = (Closure) tempScript.evaluate(""" return { Map params -> def calculatedValue = computeBaseFactor() * params.userInputNumber; def formattedResult = formatOutput(params.userInputText); return [calculated: calculatedValue, formatted: formattedResult]; } """); } // Then execute like this: public Object executeOptimized(Map<String, Object> userParams) { return dynamicClosure.call(userParams); }
Key Notes
- Security: Be cautious with user-provided dynamic script content (if it’s user-editable) to prevent code injection attacks. If the dynamic logic is controlled by your app (not end-users), this isn’t a concern.
- Class Loader Management: If you’re compiling many scripts, consider reusing the
GroovyClassLoaderor cleaning up classes to avoid memory leaks. - Error Handling: Add try-catch blocks around script evaluation to handle runtime errors gracefully.
内容的提问来源于stack exchange,提问作者Mehdi Bouzidi

