Create custom Annotation as alias for Framework-Annotation? - java

is it possible to create a custom Alias-Annotation to be used instead of
#SuppressWarnings("unused") // EventBus
public void onEvent(SomeMessage msg) { ... }
like
#EventBusListener
public void onEvent(SomeMessage msg) { ... }
This would be more self-documenting and should include the SuppressWarnings of course...
Sorry if this trivial but my googling failed me so far.

One approach is to write an annotation processor that transforms the AST (the compiler's internal representation of the source code). At each occurrence of #EventBusListener, your annotation processor would insert an occurrence of #SuppressWarnings("unused"). Later phases of the compiler would see the annotation.
Annotation processors do not ordinarily change the source code, so this takes a bit of work. The AST is supplied to the annotation processor as an interface type, so your annotation processor would need to cast this to a concrete class and perform side effects on the concrete class. Project Lombok is an example of annotation processing that modifies the AST during compilation.
You might just want to write the #SuppressWarnings("unused") annotation, though.

You can implement it following way :
#Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
#Retention(RetentionPolicy.SOURCE)
#SuppressWarnings("unused")
public #interface EventBusListener{
#AliasFor(annotation = SuppressWarnings.class, attribute = "value") String[] value() default {"unused"};
}

Related

Is a type-level annotation always inherited by public methods of that type?

Is it always the case that the following
public interface Foo {
#MyCustomAnnotation
void bar();
#MyCustomAnnotation
void bar2();
}
Is equivalent to
#MyCustomAnnotation
public interface Foo {
void bar();
void bar2();
}
Or does it depend on how the annotation is defined? In other words, are class/interface-level annotations always inherited by methods of that type (as long as the method isn't also marked with this annotation)?
Specifically, if I define the annotation above as
#Target({ ElementType.METHOD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnnotation {
}
Will methods inherit this annotation from the surrounding class/interface?
It is never the case that they are equivalent unless the library interpreting the annotation specifically decides to treat the type-level annotation as a default (e.g., Spring's #Transactional). Even then, it is entirely up to the code written in the library whether a combination of class-level and method-level annotations result in combination (#RequestMapping) or replacement (#Transactional) of definitions.

How to decide constraint validator class at runtime?

I have defined an annotation for validation like this:
#Documented
#Constraint(validatedBy = MyValidator.class)
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomValid {
//required methods
}
Now, I want to decide the "validatedBy" class at runtime. Like I have a field in my class:
public class MyClass {
#MyCustomValid
MyObject myObject;
}
How do I pass the ConstraintValidator class at runtime. I have different implementations for different cases.
Annotations are compiled into the code at compile time and they can't change, so you need a hack.
Create a validation class which delegates to another validator. The delegate needs to be created at runtime, using whatever algorithm you design. Note that the code might be used concurrently, so you need a thread-safe initialization.

How to get custom annotation from source code in java?

I'm trying to use a custom annotation to get some statistics for unit test. Another guy has defined a custom annotation as follows:
#Retention(RetentionPolicy.SOURCE)
#Target( { ElementType.METHOD, ElementType.TYPE})
public #interface TimsID {
String id();
String description() default "";
}
What I need to do is extracting this annotation from all unit tests in our project.
Here comes the problem:
the RetentionPolicy is defined as SOURCE, I don't know how to get it in the unit test.
I know that if it's a RUNTIME, it may be read reflectively like this:
Class<TestExample> obj = TestExample.class;
// Process #TimsID
if (obj.isAnnotationPresent(TimsID.class)) {
Annotation annotation = obj.getAnnotation(TimsID.class);
TimsID TimsID = (TimsID) annotation;
}
But now it's 'SOURCE', annotations will not be recorded in the class file by the compiler or retained by the VM at run time, so they can't be read reflectively.
The guy who defined the custom annotation said the reason he chooses "SOURCE" is that we just need to statistic this annotation in source code, we don't need to write these custom annotations in class file or even runtime, so we need annotation analysis only in source code.
I've accomplished this work, and here is the step and code.
SOURCE retention is aimed to be used only during compilation process. You may look into APT (Annotation Processing Tool) for more information on how to perform such kind of compile-time annotation processing logic. (However I wonder if it can do what you want)
You'll have to change the RetentionPolicy in the source code, unfortunately. There's no other way to make the annotation available for reflection at runtime, even in tests.

Passing annotation properties to meta-annotations

Say I have an annotation with a property:
#Named(name = "Steve")
private Person person
and I want to create a compound annotation with several meta-annotations, including the one that takes a property
#Named
#AnotherAnnotation
#YetAnotherAnnotation
public #interface CompoundAnnotation {
...
}
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
Eg, something like this:
#CompoundAnnotation(name = "Bob")
private Person person;
that is equivalent to, but much more convenient than
#Named(name = "Bob")
#AnotherAnnotation
#YetAnotherAnnotation
private Person person;
Thanks!
PS apologies for my poor choice of an example annotation - I didn't have the javax.inject.#Named annotation in mind, just some arbitrary annotation that has properties.
Thank you everyone for your answers/comments.
It definitely seems to be the case that this is not possible. However, it just happens that there is a simple work-around for my case-in-point, which I will share in case it helps anyone:
I am working with Spring and want to create my own Annotations that have #Component as a meta-annotation, thus being autodetected by component scanning. However, I also wanted to be able to set the BeanName property (corresponding to the value property in #Component) so I could have custom bean names.
Well it turns out that the thoughtful guys at Spring made it possible to do just that - the AnnotationBeanNameGenerator will take the 'value' property of whatever annotation it is passed and use that as the bean name (and of course, by default, it will only get passed annotations that are #Component or have #Component as a meta-annotation). In retrospect this should have been obvious to me from the start - this is how existing annotations with #Component as a meta-annotation, such as #Service and #Registry, can provide bean names.
Hope that is useful to someone. I still think it's a shame that this is not possible more generally though!
It is a few years later now, and since you are using Spring, what you are asking for is sort of possible now using the #AliasFor annotation.
For example:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#SpringApplicationConfiguration
#ActiveProfiles("test")
public #interface SpringContextTest {
#AliasFor(annotation = SpringApplicationConfiguration.class, attribute = "classes")
Class<?>[] value() default {};
#AliasFor("value")
Class<?>[] classes() default {};
}
Now you can annotate your test with #SpringContextTest(MyConfig.class), and the amazing thing is that it actually works the way you would expect.
N.B. When you need to programmatically get the attribute values, the Spring automagical aliasing works only when you use AnnotatedElementUtils instead of AnnotationUtils, as the documentation says:
AnnotatedElementUtils defines the public API for Spring's meta-annotation programming model with support for annotation attribute overrides. If you do not need support for annotation attribute overrides, consider using AnnotationUtils instead.
Example:
final Named namedAnnotation = AnnotatedElementUtils.findMergedAnnotation(Person.class, Named.class);
final String name = namedAnnotation.name();
assertEquals("Steve", name);
Is there a way that I can pass properties to the compound annotation to one of the meta annotations?
I think the simple answer is "no". There is no way to ask Person what annotations it has on it and get #Named for example.
The more complex answer is that you can chain annotations but you would have to investigate these annotations via reflection. For example, the following works:
#Bar
public class Foo {
public static void main(String[] args) {
Annotation[] fooAnnotations = Foo.class.getAnnotations();
assertEquals(1, fooAnnotations.length);
for (Annotation annotation : fooAnnotations) {
Annotation[] annotations =
annotation.annotationType().getAnnotations();
assertEquals(2, annotations.length);
assertEquals(Baz.class, annotations[0].annotationType());
}
}
#Baz
#Retention(RetentionPolicy.RUNTIME)
public #interface Bar {
}
#Retention(RetentionPolicy.RUNTIME)
public #interface Baz {
}
}
However the following statement will return null:
// this always returns null
Baz baz = Foo.class.getAnnotation(Baz.class)
This means that any 3rd party class that is looking for the #Baz annotation won't see it.

If an annotation is associated with a method while declaring it in an interface, can we force the presence of annotation in the implementation class?

This is regarding use of annotations in Java. I associated an annotation with a method while declaring it in the interface. While implementing, how can I ensure that the annotation is carried along with #Override annotation and if not, it should throw a compilation error?
Thanks.
You can't enforce this in the compiler, no. It is the job of the tools which use those annotations to check all superclasses and interfaces when looking for annotations on a given class.
For example, Spring's AnnotationsUtils takes a class to examine for annotations, and crawls all over its inheritence tree looking for them, because the compiler and JVM does not do this for you.
You can't.
You need to write some code to do this (either on your applciation load time, or using apt)
I had the same scenario, and created an annotation of my own:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.ANNOTATION_TYPE)
public #interface DependsOn {
Class<? extends Annotation>[] value();
/**
* Specifies whether all dependencies are required (default),
* or any one of them suffices
*/
boolean all() default true;
}
and applied it to other annotations, like:
#Retention(value = RetentionPolicy.RUNTIME)
#Target(value = ElementType.TYPE)
#DependsOn(value={Override.class})
public #interface CustomAnnotation {
}
Imporant: have in mind that #Override has a compile-time (SOURCE) retention policy, i.e. it isn't available at run-time.

Categories

Resources