Groovy: execute code transparent before and after calling any method

Say we have a groovy class with some methods (static or non-static). What I want to do is execute the code before and after each method of this class is called without touching the class at all and without dynamically manipulating the code inside each method.

What I tried to use groovy metaClass; getting all metaClass methods, and then dynamically replacing each method with a packaging method containing some code and in the middle referring to the old method. The problem is that I don’t know the parameters of each original method, so I can’t replace the old methods with new methods (closure), because I can’t create a closure of the package with different numbers and types of parameters dynamically and even if yi could, I don’t They knew how to access them inside the closure of the package. I need the closure of the package to have the same signature of the old method, so that the closure is called when someone tries to call the old method after transparently changing the class.

In Javascript, for example. I can use the args [] array to access all the arguments in the function body, even if I don't know the argument names at the time of writing the code.

How can I do this in groovy? Or could it be another way to achieve what I'm trying to do?

+4
source share
1 answer

Will something like below do? Using invokeMethod to intercept calls for each method. The test is self-evident.

Explanation:

Below the metaClass implementation overrides invokeMethod from GroovyObject . Since all groovy objects inherit from GroovyObject, we get the flexibility to manipulate / intercept method calls or even to define our own implementation of methodMissing . We will need one override for static and one for non-static methods. Basically, invokeMethod intercepts calls for each method in the class on which it is defined. As a result, we get some before and after functions for each method. Using reflection, the implementation below can recognize a method by its name and argument and call it at run time.

Note: -

  • Make sure that the return value from the method call also returns from closing
  • Maybe an expensive class has several methods with a lot of implementation
  • If selective execution is required, then check the method name and then intercept the call

Implementation:

 class Dummy { def method1() { System.out.println "In method 1" } def method2(String str) { System.out.println "In method 2" } static def method3(int a, int b) { System.out.println "In static method 3" } } Dummy.metaClass.invokeMethod = {String name, args -> def result System.out.println( "Do something before $name is called with args $args") try { result = delegate.metaClass.getMetaMethod(name, args) .invoke(delegate, args) } catch(Exception e) { System.out.println "Handling exception for method $name" } System.out.println( "Do something after $name was called with args $args \n") result } Dummy.metaClass.'static'.invokeMethod = {String name, args -> def result System.out.println( "Do something before static method $name is called with args $args") try { result = delegate.metaClass.getMetaMethod(name, args) .invoke(delegate, args) } catch(Exception e) { System.out.println "Handling exception for method $name" } System.out.println( "Do something after static method $name was called with args $args \n") result } def dummy = new Dummy() dummy.method1() dummy.method2('Test') Dummy.method3(1, 2) 
+6
source

Source: https://habr.com/ru/post/1204840/


All Articles