I'm risking this question knowing quite a few other questions have been asked that somehow touch the same issue. But they are either quite old, or very specific, or hard to follow, and the answers are also very specific and seem not applicable to my use case.
What I want to achieve is quite simple to describe, it is about injecting code before and after a method call in Java. Here is a simple example. I want something like this line of code:
method(p1, @ANNOTATION(type=String) p2, p3);
to desugar to
Wrapper<String> w = new Wrapper<>(p2);
method(p1, w, p3);
p2 = w.get();
in the context of a larger block of statements, with a pre-defined Wrapper class. I have limited control over the build environment that is used to compile the code, so any solution should be realizable with Gradle plugins/dependencies.
What I've looked at so far (without having a deep understanding of any of them):
So, what could be a solution for this?
You could use aspectj to achieve what you want.
Lets say we have a compiled jar with a method similar to what you have described:
You want to intercept calls to logStuff method made from certain location(-s) in your application and change the logic to wrap the argument in DummyContainer object. Here is how your code might look like.
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public static @interface DummyAnnotation {
Class type();
}
//that is your target method
public static void logStuff(@DummyAnnotation(type = String.class) Object data) {
if(data instanceof String) {
System.out.println("String: " + data);
} else {
System.out.println("Not a string " + data);
}
}
Dummy container class:
public static class DummyContainer {
private String string;
private Class clazz;
public DummyContainer(String string, Class clazz) {
this.clazz = clazz;
this.string = string;
}
@Override
public String toString() {
return "DummyContainer=[clazz: " + clazz + ", string: " + string + "]";
}
}
Aspect:
@Aspect
public class WrapperAspect {
@Pointcut("call(* "
/**method to intercept*/
+ "com.yourpackage.YourType.logStuff(Object)) && args(param) && "
/**only calls made from within that location
* will be intercepted. Remove it to intercept
* calls from everywhere*/
+ "within(test.Runner)")
public void logStuffPointcut(Object param) {}
@Around("logStuffPointcut(param)")
public void simpleWrap(Object param, ProceedingJoinPoint jp) throws Throwable {
String calledMethodName = jp.getSignature().getName();
Class type = jp.getSignature().getDeclaringType();
Method method = type.getDeclaredMethod(calledMethodName, Object.class);
DummyAnnotation annotation = method.getParameters()[0].getAnnotation(DummyAnnotation.class);
DummyContainer wrapped = new DummyContainer((String) param, (Class)annotation.type());
//code before method call
jp.proceed(new Object[] {wrapped});
//code after method call
}
}
Method within test.Runner class that calls logStuff
public static void main(String[] args) {
Object instance = "thats a string";
System.out.println("Obj type : " + instance.getClass());
YourType.logStuff(instance);
System.out.println("Obj type : " + instance.getClass());
}
Output:
Obj type : class java.lang.String Not a string DummyContainer=[clazz: class java.lang.String, string: thats a string] Obj type : class java.lang.String
To build the project I use Maven. Here is how the build section of my pom.xml looks like:
...
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.11</version>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.13</version>
</dependency>
</dependencies>
<configuration>
<Xajruntimetarget>1.8</Xajruntimetarget>
<complianceLevel>1.8</complianceLevel>
<weaveDependencies>
<weaveDependency>
<groupId>your.jar.group.id</groupId>
<artifactId>artifactid</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With