bvitaliyg
bvitaliyg

Reputation: 3255

How to extend Java annotation?

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 {

}

Upvotes: 75

Views: 104646

Answers (7)

Borja Bravo
Borja Bravo

Reputation: 51

If you need to set some field you can use @AliasFor. Example.

Instead of using this verbose annotations:

@Test
@Sql(
    config = SqlConfig(
        dataSource = "myDataSource",
        transactionManager = "myTransactionManager",
    ),
    scripts = ["/myScript.sql"],
)
test()

I can create my own annotation that will container those that I want to customize. Note that I had to use the @aliasFor annotation to keep my ability to customize some fields:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.core.annotation.AliasFor;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Sql(config = @SqlConfig(
    dataSource = "mnDataSource",
    transactionManager = "myTransactionManager"
))
public @interface SqlMyDB {

  @AliasFor(annotation = Sql.class, attribute = "scripts")
  String[] scripts() default {};

}

Now I can simply do:

@Test
@SqlMyDB(scripts = ["/myScript.sql"])
test()

Note that @AliasFor is a Spring annotation and will only work if:

  • You use it for Spring Framework Annotations
  • Use Spring Annotation utils like with Eric's answer
  • Code the equivalent logic yourself.

Upvotes: 4

Georgi Peev
Georgi Peev

Reputation: 1109

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");
    }

Upvotes: 1

user2210419
user2210419

Reputation: 41

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 "[email protected]";
  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.

Upvotes: 1

Eric Jiang
Eric Jiang

Reputation: 583

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.

Upvotes: 14

Sidi Muhammad
Sidi Muhammad

Reputation: 164

@With(Secure.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Secure {

}

This will work.

Upvotes: 10

Halmackenreuter
Halmackenreuter

Reputation: 709

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 { ... }

Upvotes: 38

piotrek
piotrek

Reputation: 14530

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.

Upvotes: 30

Related Questions