How to extend Java annotation?
JavaAnnotationsJava Problem Overview
In my project I use pre-defined annotation @With
:
@With(Secure.class)
public class Test { //....
The source code of @With
:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface With {
Class<?>[] value() default {};
}
I want to write custom annotation @Secure
, which will have the same effect as @With(Secure.class)
. How to do that?
What if I do like this? Will it work?
@With(Secure.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secure {
}
Java Solutions
Solution 1 - Java
As piotrek pointed out, you cannot extend Annotations in the sense of inheritance. Still, you can create Annotations that aggregate others:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SuperAnnotation {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SubAnnotation {
SuperAnnotation superAnnotation();
String subValue();
}
Usage:
@SubAnnotation(subValue = "...", superAnnotation = @SuperAnnotation(value = "superValue"))
class someClass { ... }
Solution 2 - Java
From Java language specification, Chapter 9.6 Annotation Types:
> No extends clause is permitted. (Annotation types implicitly extend annotation.Annotation
.)
So, you can not extend an Annotation. you need to use some other mechanism or create a code that recognize and process your own annotation. Spring allows you to group other Spring's annotation in your own custom annotations. but still, no extending.
Solution 3 - Java
To expand on Muhammad Abdurrahman's answer--
@With(Secure.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secure {
}
This does not work by default but you can use it in conjunction with Spring's AnnotationUtils.
See this SO answer for an example.
Solution 4 - Java
@With(Secure.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secure {
}
This will work.
Solution 5 - Java
You can use annotation for annotation like this:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithSecurityContext(factory = WithCustomUserSecurityContextFactory.class)
public @interface WithCustomUser {
String username() default "demo@demo.com";
String password() default "demo";
String[] authorities() default {Authority.USER};
}
And define exact state in its "child"
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithCustomUser(username = "[email protected]",
password = "admin",
authorities = {Authority.USER, Authority.ADMINISTRATOR})
public @interface WithAdminUser {
}
In this case you have a some kind of "state" and access to the parent annotation fields via reflection/aspect.
Solution 6 - Java
So the provided answer from Eric Jiang is 100% working in my situation and she is: I need JMSListener ,but i want to hide the destination name:
@GetPlayerDataByUUIDListener
public void getPlayerDataByUUID(Object message) {
System.out.println("Im Here");
}
`
@JmsListener(destination = PlayerStatisticsJMSConstants.GET_PLAYER_DATA_BY_UUID)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GetPlayerDataByUUIDListener {
}
So this is working perfectly ,and it is the same as:
@JmsListener(destination = "example")
@GetPlayerDataByUUIDListener
public void getPlayerDataByUUID(Object message) {
System.out.println("Im Here");
}