Currently I have the standard one:
@DeclareParents(value = "(@moody.MyAttribute *)", defaultImpl = MoodyImpl.class)
This will add my interface+implementation to any class with @MyAttribute
I would like to do this for all classes that have this attribute AND/OR have a method with that attribute.
So this class should also get my interface+implementation:
class MyClass {
@MyAttribute
public void test()
{
}
}
Is that possible?
No, because both @DeclareParents
and the newer @DeclareMixin
need class name specifications in their value
parameter. If I were you I would refactor my annotation so as to only be applicable to classes, not methods, and then my code to move all annotations to classes as well.
One more option if you absolutely want to stay on your path: Since AspectJ 1.8.2 there is a new annotation processing feature. You might want to explore that one and create an annotation processor creating an ITD aspect for each affected class with annotated methods.
Update: I have just remembered a non-standard compiler option -XhasMember
which you can use:
ajc -X
AspectJ Compiler 1.8.2 non-standard options:
(...)
-XhasMember allow hasmethod() and hasfield type patterns in
declare parents and declare @type
(...)
Caveat: It does not seem to work with @AspectJ syntax, i.e. you cannot use annotation-style @DeclareParents
but have to use native AspectJ syntax declare parents
. The default interface implementation is also done differently, i.e. via ITD in the aspect, there is no need for a specific implementation class.
Here is a compileable, fully self-consistent code sample:
Marker annotation:
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.TYPE, ElementType.METHOD })
public @interface MyAttribute {}
Interface to be implemented via ITD:
package de.scrum_master.app;
public interface Moody {
public void sayHelloTo(String name);
}
Sample classes with(out) marker annotation:
Class Foo
has the annotation at class level, Bar
at method level and Zot
has no annotation at all.
package de.scrum_master.app;
@MyAttribute
public class Foo {
public static void foo() {}
}
package de.scrum_master.app;
public class Bar {
@MyAttribute
public static void bar() {}
}
package de.scrum_master.app;
public class Zot {
public static void zot() {}
}
Driver application:
For demonstration purposes, the application checks for the existence of a method sayHelloTo(String)
via reflection.
package de.scrum_master.app;
import java.lang.reflect.Method;
public class Application {
public static void main(String[] args) throws Exception {
Method method;
try {
method = Foo.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Foo: " + method);
try {
method = Bar.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Bar: " + method);
try {
method = Zot.class.getDeclaredMethod("sayHelloTo", String.class);
} catch (NoSuchMethodException nsme) {
method = null;
}
System.out.println("Zot: " + method);
}
}
Console output (without aspect):
Foo: null
Bar: null
Zot: null
Aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Moody;
import de.scrum_master.app.MyAttribute;
public aspect ITDAspect {
declare parents : @MyAttribute * implements Moody;
declare parents : hasmethod(@MyAttribute * *(..)) implements Moody;
public void Moody.sayHelloTo(String name) {
System.out.println("Hello " + name);
}
}
Console output (with aspect):
Foo: public void de.scrum_master.app.Foo.sayHelloTo(java.lang.String)
Bar: public void de.scrum_master.app.Bar.sayHelloTo(java.lang.String)
Zot: null
Voilà! We have successfully added the interface including its default implementation to Bar
which does not have class-level annotation, but a method-level one.
Enjoy!