如何通过MetaClass拦截Script及其所有子类的方法调用?
Got it, let's walk through how to intercept all method calls on Script and its subclasses using MetaClass—we'll start with a Java implementation (since you preferred that) and then cover a more concise Groovy version if you need it.
Java Implementation
Since MetaClass is a Groovy-specific feature, we'll need to use Groovy's libraries in our Java code. Here's a step-by-step implementation:
Step 1: Add Groovy Dependency
First, make sure you have Groovy's core library in your project. For Maven, add this to your pom.xml:
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>3.0.17</version> <!-- Use the latest stable version --> </dependency>
Step 2: Create Custom MetaClass Interceptor
We'll extend MetaClassImpl and override invokeMethod to intercept calls. We'll specifically handle the evaluate method here, but you can expand this logic to cover other methods too.
import groovy.lang.MetaClassImpl; import groovy.lang.Script; import groovy.lang.Binding; import groovy.lang.GroovyShell; public class ScriptMethodInterceptor extends MetaClassImpl { private final GroovyShell shell; public ScriptMethodInterceptor(Class<?> targetClass, GroovyShell shell) { super(targetClass); this.shell = shell; initialize(); // Critical: Initialize the MetaClass to make it functional } @Override public Object invokeMethod(Object scriptInstance, String methodName, Object[] args) { // Intercept the "evaluate" method if ("evaluate".equals(methodName)) { System.out.println("Intercepted call to 'evaluate' method"); // Run your custom logic here return runCustomEvaluation((String) args[0]); } // Delegate all other method calls to the original implementation return super.invokeMethod(scriptInstance, methodName, args); } private Object runCustomEvaluation(String scriptText) { // Implement your custom evaluation logic here Binding binding = new Binding(); return shell.evaluate(scriptText, binding); } }
Step 3: Apply the Interceptor to Script Class
Now we need to attach our custom MetaClass to the Script class so all its subclasses inherit this behavior:
public class Main { public static void main(String[] args) { GroovyShell shell = new GroovyShell(); // Initialize our custom MetaClass ScriptMethodInterceptor customMetaClass = new ScriptMethodInterceptor(Script.class, shell); // Assign it to Script's metaClass property Script.metaClass = customMetaClass; // Test with a dynamic Script subclass String scriptCode = """ def testMethod() { println 'Test method executed'; return 42; } """; Script script = shell.parse(scriptCode); // This method call won't be intercepted script.invokeMethod("testMethod", new Object[]{}); // This evaluate call will be intercepted script.invokeMethod("evaluate", new Object[]{"println 'Running evaluated script'"}); } }
Groovy Implementation
If you're working in a Groovy environment, the code becomes much more concise thanks to Groovy's syntax sugar and native MetaClass support:
import groovy.lang.Script import groovy.lang.GroovyShell // Define our custom MetaClass def customMetaClass = new MetaClassImpl(Script.class) { private final GroovyShell shell = new GroovyShell() @Override Object invokeMethod(Object scriptInstance, String methodName, Object[] args) { if (methodName == "evaluate") { println "intercepted" // Run your custom logic here return shell.evaluate((String) args[0]) } else { // Pass through other methods to the original implementation return super.invokeMethod(scriptInstance, methodName, args) } } } customMetaClass.initialize() // Attach the MetaClass to Script Script.metaClass = customMetaClass // Test the interceptor def testScript = new GroovyShell().parse(""" def greet() { println 'Hello from script' } """) // Non-intercepted method call testScript.greet() // Intercepted evaluate call testScript.evaluate("println 'Evaluated content'")
Key Notes
- Global Impact: Setting the MetaClass on
Script.classmeans all subclasses (including dynamically parsed scripts) will use this interceptor. If you only want to target specific subclasses, assign the MetaClass to those instead. - Initialize Method: Always call
initialize()on your customMetaClassImpl—this sets up internal state needed for proper method handling. - Flexibility: You can expand the interception logic to check for other method names, add logging, modify arguments, or even override return values as needed.
内容的提问来源于stack exchange,提问作者Scoopta

