¿Cómo extender la anotación de Java?

3 minutos de lectura

avatar de usuario
bvitaliyg

En mi proyecto utilizo una anotación predefinida @With:

@With(Secure.class)
public class Test { //....

El código fuente de @With:

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

    Class<?>[] value() default {};
}

Quiero escribir una anotación personalizada @Secureque tendrá el mismo efecto que @With(Secure.class). ¿Como hacer eso?


¿Qué pasa si me gusta esto? ¿Funcionará?

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

}

Como señaló piotrek, no puede extender Anotaciones en el sentido de herencia. Aún así, puede crear Anotaciones que agreguen otras:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SuperAnnotation {
    String value();
}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface SubAnnotation {
    SuperAnnotation superAnnotation();
    String subValue();
}

Uso:

@SubAnnotation(subValue = "...", superAnnotation = @SuperAnnotation(value = "superValue"))
class someClass { ... }

  • Así que en lugar de escribir @With(Secure.class)OP ahora tiene que escribir @Secure(superAnnotation = @With(Secure.class))? ¿Cómo ayuda eso?

    – Mateo Lee

    19 de marzo de 2018 a las 16:27

avatar de usuario
piotrek

De la especificación del lenguaje Java, Capítulo 9.6 Tipos de anotaciones:

No se permite la cláusula de extensión. (Los tipos de anotación extienden implícitamente annotation.Annotation.)

Por lo tanto, no puede extender una Anotación. necesita usar algún otro mecanismo o crear un código que reconozca y procese su propia anotación. Spring le permite agrupar otras anotaciones de Spring en sus propias anotaciones personalizadas. pero aún así, no se extiende.

  • Probablemente ‘extender’ no sea la mejor expresión para esto. Me refiero a la implementación de la colocación automática para el argumento.

    – bvitaliyg

    2 de marzo de 2014 a las 11:25

  • @bvitaliyg: puede crear su propia anotación con cualquier campo predeterminado, pero no hay un mecanismo listo para usar en jvm que lo reconozca automáticamente. debe escribir ese código usted mismo o verificar si las bibliotecas existentes (como Spring) son suficientes para su caso

    – piotrek

    2 de marzo de 2014 a las 11:27

  • Para cualquiera que busque hacer esto con Spring, vale la pena saber que el término que Spring tiene para esto es “meta-anotación”. Una “anotación personalizada” es simplemente cualquier anotación que usted mismo definió, no significa necesariamente que la metaanotó con una anotación de terceros.

    – Andrés Spencer

    4 oct 2018 a las 11:46


Para ampliar la respuesta de Muhammad Abdurrahman:

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

}

Esto hace no funciona de forma predeterminada, pero usted pueden úsalo junto con Spring’s Utilidades de anotación.

Vea esta respuesta SO para un ejemplo.

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

}

Esto funcionará.

Puede usar la anotación para anotaciones como esta:

@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};
}

Y definir el estado exacto en su “hijo”

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@WithCustomUser(username = "admin@admin.com",
                password = "admin",
                authorities = {Authority.USER, Authority.ADMINISTRATOR})
public @interface WithAdminUser {
}

En este caso, tiene algún tipo de “estado” y acceso a los campos de anotación principales a través de reflejo/aspecto.

  • Esto es exactamente lo que estaba buscando. Me preguntaba si es necesario cambiar el ElementType si quiero una anotación “niño”. Aparentemente, no tengo que cambiarlo si se aplica al mismo tipo que el principal.

    – Igor

    9 de agosto de 2021 a las 13:11

avatar de usuario
Georgi Peev

Entonces, la respuesta proporcionada por Eric Jiang funciona al 100% en mi situación y ella es: Necesito JMSListener, pero quiero ocultar el nombre de destino:

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

Así que esto está funcionando perfectamente, y es lo mismo que:

@JmsListener(destination = "example")
    @GetPlayerDataByUUIDListener
    public void getPlayerDataByUUID(Object message) {
        System.out.println("Im Here");
    }

  • Esto es exactamente lo que estaba buscando. Me preguntaba si es necesario cambiar el ElementType si quiero una anotación “niño”. Aparentemente, no tengo que cambiarlo si se aplica al mismo tipo que el principal.

    – Igor

    9 de agosto de 2021 a las 13:11

¿Ha sido útil esta solución?