Emulate annotation inheritance for interfaces and methods with AspectJ - java

Often people ask AspectJ questions like this one, so I want to answer it in a place I can easily link to later.
I have this marker annotation:
package de.scrum_master.app;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface Marker {}
Now I annotate an interface and/or methods like this:
package de.scrum_master.app;
#Marker
public interface MyInterface {
void one();
#Marker void two();
}
Here is a little driver application which also implements the interface:
package de.scrum_master.app;
public class Application implements MyInterface {
#Override
public void one() {}
#Override
public void two() {}
public static void main(String[] args) {
Application application = new Application();
application.one();
application.two();
}
}
Now when I define this aspect, I expect that it gets triggered
for each constructor execution of an annotated class and
for each execution of an annotated method.
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
public aspect MarkerAnnotationInterceptor {
after() : execution((#Marker *).new(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
after() : execution(#Marker * *(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
}
Unfortunately the aspect prints nothing, just as if class Application and method two() did not have any #Marker annotation. Why does AspectJ not intercept them?

The problem here is not AspectJ but the JVM. In Java, annotations on
interfaces,
methods or
other annotations
are never inherited by
implementing classes,
overriding methods or
classes using annotated annotations.
Annotation inheritance only works from classes to subclasses, but only if the annotation type used in the superclass bears the meta annotation #Inherited, see JDK JavaDoc.
AspectJ is a JVM language and thus works within the JVM's limitations. There is no general solution for this problem, but for specific interfaces or methods you wish to emulate annotation inheritance for, you can use a workaround like this:
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
import de.scrum_master.app.MyInterface;
/**
* It is a known JVM limitation that annotations are never inherited from interface
* to implementing class or from method to overriding method, see explanation in
* JDK API.
* <p>
* Here is a little AspectJ trick which does it manually.
*
*/
public aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
Please note: With this aspect in place, you can remove the (literal) annotations from the interface and from the annotated method because AspectJ's ITD (inter-type definition) mechanics adds them back to the interface plus to all implementing/overriding classes/methods.
Now the console log when running the Application says:
execution(de.scrum_master.app.Application())
execution(void de.scrum_master.app.Application.two())
By the way, you could also embed the aspect right into the interface so as to have everything in one place. Just be careful to rename MyInterface.java to MyInterface.aj in order to help the AspectJ compiler to recognise that it has to do some work here.
package de.scrum_master.app;
public interface MyInterface {
void one();
void two();
// Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104
public static aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
}
Update 2021-02-11: Someone suggested an edit to the latter solution, saying that the aspect MarkerAnnotationInheritor nested inside interface MyInterface is implicitly public static, so the modifiers in the aspect declaration could be omitted. In principle this is true, because members (methods, nested classes) of interfaces are always public by default and a non-static inner class definition would not make sense inside an interface either (there is no instance to bind it to). I like to be explicit in my sample code, though, because not all Java developers might know these details.
Furthermore, currently the AspectJ compiler in version 1.9.6 throws an error if we omit static. I have just created AspectJ issue #571104 for this problem.

Related

How to set a property value using Java custom annotation and Spring AOP?

I would like to use custom Java annotation to insert a value in a private class property using Spring AOP (and/or AspectJ). Quick example:
MyAnnotation.java:
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {
}
MyController.java:
public class MyControllerImpl implements MyController {
...
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
// imagine this is a REST API that gets called on #GET
// request and returns a string
System.out.println(this.var1); // <-- I'd like this to be "helloworld"
// this is just for illustration
// of course, I will want to do
// something more meaningful with
// the 'var1' variable
return "ok"; <- unimportant for this example
}
...
MyAspect.java:
#Aspect
#Component
public class MyAspect {
#Pointcut("#annotation(com.mypackage.annotation.MyAnnotation)")
public void fieldAnnotatedWithMyAnnotation() {
}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
// problem #1 - the program never enters here
// problem #2 - I need to figure out how to set up the var1 here
// to "helloworld" , how?
return pjp.proceed();
}
...
}
What would I like to happen?
I will call and get into the getVarExample() and after it returns I would like to see "helloworld" in console or log. I would like to somehow set the var1 to a custom value using AOP. Any property variable that will be annotated with #MyAnnotation will be set to "helloworld". I hope the example above is clear.
What have I tried?
I made sure there is no typo in the package names, also fiddled with different AOP advice annotations like #Around and #Before. I also tried different targets in the MyAnnotation and ended up with ElementType.FIELD which should be correct.
Can you help me to get it working?
I know this can be done, but couldn't find any working example online. Again, I would like to see 2 answers:
1. How to get the pointcut to trigger on MyController entrance? I want to catch a breakpoint inside the enrichVar1(..) method of the MyAspect class.
2. How can I modify the annotated var1 value inenrichVar1(..) method of the MyAspect class?
I don't know what I am doing wrong. Any help will be greatly appreciated. Thank you!
The AOP is set up correctly in my project. I know that because I am already using AOP for different things (logging for example).
Update #1:
Please, note there are not getters or setters for the var1 private variable. The variable will be only used within the MyControllerImpl. To illustrate this better I changed the return value of the getVarExample.
Like I said in my comment:
The pointcut designator #annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set(). I.e., the pointcut would also need to be changed if migrating to AspectJ. But I agree that sticking to Spring AOP and annotating getter methods instead of fields is probably enough here.
But because you insist that you want to keep the controller class unchanged, here is the native AspectJ solution. Please read chapter Using AspectJ with Spring Applications for how to configure that with #EnableLoadTimeWeaving and JVM parameter -javaagent:/path/to/aspectjweaver.jar.
In order to demonstrate that this solution really does work independently of Spring, I am using no Spring classes or annotations at all, only POJOs and native AspectJ. You can simply do the same within your Spring application. Please note that native AspectJ aspects do not need #Component annotations, in contrast to Spring AOP aspects.
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {}
package de.scrum_master.app;
public interface MyController {
String getVarExample();
}
package de.scrum_master.app;
public class MyControllerImpl implements MyController {
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
System.out.println(this.var1);
return "ok";
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
MyController myController = new MyControllerImpl();
myController.getVarExample();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("get(#de.scrum_master.app.MyAnnotation * *)")
public void fieldAnnotatedWithMyAnnotation() {}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp);
return "helloworld";
}
}
When running Application, the console log is going to be:
get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld
The AspectJ manual explains the syntax of field get and set join point signatures and field patterns.
Note: I think that your use case might be a hack rather than a valid application design. You ought to refactor rather than hack into an application like this.
As it goes from Spring docs Spring AOP does support Spring beans' method execution join points. To make field access join points work you need to use AspectJ's backend with load time weaving for AOP.
But for your case it's not required to use field join points, you can put your annotation on the getter and this should work.

Spring AOP - Point Cut not getting called

I have a SpringBoot Application.
I have defined an Annotation say "Track", and I have annotated few methods in different packages which I want aop to consider.
The annotation has been defined as below :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Track {
}
I have not missed the #EnableAspectJAutoProxy in the #Configuration class of my package.
I have a Pointcut and an Advice defined in the Aspect like below :
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(#Track * *.*(..))")
void annotatedMethod() {
// No Implementation required
}
#Around("annotatedMethod() && #annotation(methodLevelTrack)")
public void adviseAnnotatedMethods(ProceedingJoinPoint proceedingJoinPoint,
Track methodLevelTrack) throws Throwable {
// do some task
proceedingJoinPoint.proceed();
// do some task after the method is executed.
}
}
My intention is: for any method (annotated with #Track) in any package, with any access modifier, and any number of input arguments, and any return type, to follow the aspect's #Around advice.
Now, the interesting situation is as below :
I have a class say "Engine" which calls other classes and downstream systems to perform a long-running operation. Let's define the class as follows :
public class Engine {
// bunch of other autowired objects
public void processTask() {
<autowired_object_A>.someMethod() // this method has been annotated with #Track
<autowired_object_B>.someMethod() // this method has also been annotated with # Track
.... // bunch of other methods in other autowired objects that have been annotated with # Track
someMethodOfEngineClass(); // Now this has been defined in the Engine class as below, but pointcut doesn't recognize this method!
}
#Track
private void someMethodOfEngineClass() {
// do something
}
}
All the "other" autowired objects' methods are getting recognized by pointcut as expected but the method within this Engine class, that has been annotated with #Track, is not recognized. What's the mystery?
I have tried making "someMethodOfEngineClass" method public, return something instead of void and all those combinations and it doesn't work.
What am I missing?
Is it the pointcut definition expression?
I have defined the aspect in one of the sub packages, is aspect supposed to be defined at the top level in the package structure?
Can you folks please suggest something that can work? I am kinda stuck at this.
When you define aop spring creates proxy around the class,
so when the method is called, actually call is delegated to proxy, sth like
your.package.Engine$$FastClassBySpringCGLIB$$c82923b4.someMethodOfEngineClass()
But this works only when a method is called from outside it's class
If you call class method from the same class you are effectively calling it by this.someMethodOfEngineClass()
here -> http://www.nurkiewicz.com/2011/10/spring-pitfalls-proxying.html
you can find more info about proxying
so proxy is bypassed and aop is not working.

How to add a field to a custom-annotated class using AspectJ

To add a field to some specific class with aspectj we do
package com.test;
public class MyClass {
private String myField;
}
public aspect MyAspect
{
private String MyClass.myHiddenField;
}
How do we add a field to a class that is annotated with some custom annotation?
example usage : if class is annotated with #CustomLoggable add a Logger field and some methods.
or
if method has the #ReadLocked annotation then class will have a ReentrantReadWriteLock field and the appropriate logic injected, etc.
Actually you cannot make inter-type declarations (ITD) on annotation types, i.e. you need to know concrete class names in order to declare static or non-static members or methods directly.
The usual workaround is:
Create an interface with all the methods you need.
Provide implementations for each interface method.
Make each annotated type implement the interface via ITD.
Now if you also want to add a static member such as a logger to all annotated types, again if you do not know the exact class names you need to use a workaround:
Create an aspect holding the desired member(s). Let's call it LoggerHolder in this example.
Make sure that one aspect instance per target class is created instead of the default singleton aspect instance. This is done via pertypewithin.
In order to avoid runtime exceptions you must not initialise the members directly via Logger logger = ... but need to do it lazily, waiting until after the target type's static initialisation phase is finished.
You also need to provide an accessor method like LoggerHolder.getLogger() in the aspect and call it whenever necessary.
In order to hide all the ugly aspect stuff from the end user I recommend to add yet another accessor method LoggableAspect.getLogger() (same method name for convenience) to the ITD interface mentioned above and provide a method implementation extracting the member reference from the aspect instance via LoggerHolder.aspectOf(this.getClass()).getLogger().
Attention: I am using two concepts at once here, mixing them in one application because you asked for both static members and non-static methods added to annotated classes:
Helper interface + implementation added to your core code via ITD
Holder aspect declaring member(s) and associated with target classes via pertypewithin in order to emulate static members
Now here is some sample code:
Annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomLoggable {}
Two classes, one bearing the annotation and one not bearing it:
package de.scrum_master.app;
public class OrdinaryClass {
public void doSomething() {
System.out.println("Logging some action directly to console");
}
}
package de.scrum_master.app;
import java.util.logging.Level;
#CustomLoggable
public class AnnotatedClass {
public void doSomething() {
getLogger().log(Level.INFO, "Logging some action via ITD logger");
getLogger().log(Level.INFO, someOtherMethod(11));
}
}
As you can see, the second class uses two methods which have not been declared directly within the class: getLogger() and someOtherMethod(int). Both of them will be declared via ITD further below, the former providing access to the pseudo-static member and the latter being just another method you want declared on each annotated class.
Aspect holding an instance of the pseudo-static member:
package de.scrum_master.aspect;
import java.util.logging.Logger;
import de.scrum_master.app.CustomLoggable;
public aspect LoggerHolder
pertypewithin(#CustomLoggable *)
{
private Logger logger;
after() : staticinitialization(*) {
logger = Logger.getLogger(getWithinTypeName());
}
public Logger getLogger() {
return logger;
}
}
As I said earlier, please note the usage of pertypewithin and staticinitialization. Another convenient thing is to use the aspect's getWithinTypeName() method in order to get the target class name for naming the logger.
Aspect declaring an interface + implementation and applying it to all target types:
package de.scrum_master.aspect;
import java.util.logging.Logger;
import de.scrum_master.app.CustomLoggable;
public aspect LoggableAspect {
public static interface Loggable {
Logger getLogger();
String someOtherMethod(int number);
}
declare parents : (#CustomLoggable *) implements Loggable;
public Logger Loggable.getLogger() {
return LoggerHolder.aspectOf(this.getClass()).getLogger();
}
public String Loggable.someOtherMethod(int number) {
return ((Integer) number).toString();
}
}
For simplicity, I just declared the interface as a static nested type within the aspect. You can also declare the interface separately, but here you see it in its context which for me is preferable.
The key thing here is the declare parents statement making each target class implement the interface. The two method implementations at the end show how to provide "normal" method implementations as well as how to access the logger from the holder aspect via aspectOf.
Driver class with entry point:
Last, but not least, we want to run the code and see if it does what we want.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new OrdinaryClass().doSomething();
new AnnotatedClass().doSomething();
}
}
Console output:
Logging some action directly to console
Mrz 15, 2015 11:46:12 AM de.scrum_master.app.AnnotatedClass doSomething
Information: Logging some action via ITD logger
Mrz 15, 2015 11:46:12 AM de.scrum_master.app.AnnotatedClass doSomething
Information: 11
VoilĂ ! Logging works, the Logger has a nice name de.scrum_master.app.AnnotatedClass and calling the two interface methods works as expected.
Alternative approach:
Since AspectJ 1.8.2 annotation processing is supported, see also this blog post. I.e. you could use APT in order to generate one aspect per annotated type and introduce static members and additional methods directly without any tricks such as per-type instantiation, accessor methods members within holder aspect instances and interfaces. This comes at the cost of an additional build step, but I think it would be a very neat and straightforward way to solve your problem. Let me know if you have any difficulty understanding the examples and need more help.
You can create a pointcut for any type with a particular annotation. See Join Point Matching based on Annotations.

Is it safe to use a Spring #Transactional annotation on a Java 8 default interface method?

The Spring documentation recommends against putting #Transactional annotations on interface methods because interface annotations are not inherited by classes. However, with Java 8 we can provide a concrete default implementation in the interface. If such a default interface method needs to be the transactional boundary, we have no other choice: we have to put the #Transactional annotation on the interface method.
Will this work (i.e. will spring respect the transaction boundary in this case)? If so, are there any hidden pitfalls to this approach?
Spring uses (among others) a BeanFactoryTransactionAttributeSourceAdvisor as an Advisor when generating a proxy bean for classes annotated with or containing methods annotated with #Transactional.
When the time comes to proxy it, it uses the bean's class type (with CGLIB) to generate the proxy. So we want to see if the default method annotated with #Transactional will be visible from the implementing class' point of view.
Here's a Java 8 SSCCE
public static void main(String[] args) throws Exception{
Class<?> randomImplClass = RandomImpl.class;
System.out.println(randomImplClass);
Easy annotation = randomImplClass.getAnnotation(Easy.class);
System.out.println("Class: " + randomImplClass);
System.out.println("Class Annotation: " + annotation);
Method method = randomImplClass.getMethod("doRandom");
annotation = method.getAnnotation(Easy.class);
System.out.println("Method: " + method);
System.out.println("Method Annotation: " + annotation);
}
public static class RandomImpl implements Random{}
#Easy
interface Random {
#Easy
default void doRandom() {System.out.println("testing");};
}
#Target(value = {METHOD, TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Easy {}
which prints
class TestEnhancer$RandomImpl
Class: class TestEnhancer$RandomImpl
Class Annotation: null
Method: public default void TestEnhancer$Random.doRandom()
Method Annotation: #TestEnhancer$Easy()
Indicating that the annotation was inherited for the interface's method. It seems, therefore, that Spring will be able to add #Transactional behavior when the class has not overriden the default method. If it has overriden it, then annotations are not inherited.

Calling Abstract classes #Activate method (apache felix)

I have an abstract class that a child class extends. My abstract class has an #Activate method, so does the child class. When OSGi creates my service, it invokes the child class activate method but never the abstract class's activate. Is there any way to force the abstract class's activate to be called by OSGi rather than having the child class manually call the parent activate method?
Here is some code to help elaborate on what I am asking.
#Component(componentAbstract=true, inherit=true)
#Service(value=ISomeInterface)
public abstract class AbstractHello implements ISomeInterface{
#Activate
public void activate(){
System.out.print("Hello ");
}
}
#Component
#Service(Value=ISomeInterface)
public class World extends AbstractHello{
#Activate
public void activate(){
System.out.println("World!");
}
}
The result of the code above would be "World!", rather than "Hello World!".
Initially I thought maybe the child activate method name was clobbering the abstract activate method of the same name. The result is the same even if the abstract class's activate method is given a unique name. Is there any way to have OSGi call the abstract class's activate method for me?
The DS annotation processors only look at the concrete class decorated with #Component. Super classes are not examined. Since the annotation processing is done at build time, super types may come from imported packages which are not chosen until runtime.
Also, the annotation processor generates component description XML from the annotations. So there can only be one activate="methodName" attribute in the XML. If you need the superclass' method called, then you need to call it from the subclass' method.
This has nothing to do with Apache Felix and OSGi, this is caused by poor understanding of Class Inheritance and Method Overriding in Java.
Your World class extends AbstractHello class and overrides its activate() method. If you want the AbstractHello.activate() method to be called then you must call it in
// Annotations excluded for readability.
public class World extends AbstractHello {
public void activate() {
super.activate();
System.out.println("World!");
}
}
OSGi can't help here.
UPDATE
Since the base class is abstract, and you don't have an instance of it, you can't call its method. Neither can OSGi container.

Categories