@annotation 注解的继承

  • Post author:
  • Post category:其他

一、注解的继承

注解继承分为两种情况: a:类级别 Type (Class, Interface), b:属性和方法级别 (Property, Method)

  • 类级别 (Type): 注解 类 Class 上且注解上含有 元注解 @Inherited 时, 才会被继承;(在 jdk 8 中, 接口Interface 无法继承任何Type类型注解)
  • 属性和方法级别 (Property, Method): 注解 无论何时都会 被子类或子接口继承, 除非子类或子接口重写.

注意以上说的是继承(extends), 不属于注解合并 (叠加)。

  • 测试
public class IterInheritedTest {

    @Inherited
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InheritedAnnotationType {}

    @Inherited
    @Target({ElementType.FIELD,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ABC {
        String value() default "";
    }

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

    @UnInheritedAnnotationType
    static
    class A {}

    @InheritedAnnotationType
    static
    class B extends A {}

    static class C extends B {}

    @UnInheritedAnnotationType
    interface Z {
        @ABC()
        void he();
    }

    @InheritedAnnotationType
    interface Y extends Z {
        @ABC("hhhh")
        void he();
    }

    interface X extends Y {}

    public static void main(String[] args) throws NoSuchMethodException, NoSuchFieldException {
        System.out.println(X.class.getAnnotation(InheritedAnnotationType.class));
        System.out.println(Y.class.getAnnotation(InheritedAnnotationType.class));
        System.out.println(Z.class.getAnnotation(InheritedAnnotationType.class));
        System.out.println("_________________________________");
        System.out.println(X.class.getAnnotation(UnInheritedAnnotationType.class));
        System.out.println(Y.class.getAnnotation(UnInheritedAnnotationType.class));
        System.out.println(Z.class.getAnnotation(UnInheritedAnnotationType.class));
        System.out.println("_________________________________");
        System.out.println(Arrays.toString(Z.class.getMethod("he").getAnnotations()));
        System.out.println(Arrays.toString(Y.class.getMethod("he").getAnnotations()));
        System.out.println(Arrays.toString(X.class.getMethod("he").getAnnotations()));
    }
}
  • 输出
null
@org.pzone.crypto.IterInheritedTest$InheritedAnnotationType()
null
_________________________________
null
null
@org.pzone.crypto.IterInheritedTest$UnInheritedAnnotationType()
_________________________________
[@org.pzone.crypto.IterInheritedTest$ABC(value=)]
[@org.pzone.crypto.IterInheritedTest$ABC(value=hhhh)]
[@org.pzone.crypto.IterInheritedTest$ABC(value=hhhh)]

二、注解的合并

java 注解原本只是一种能被获取信息的 注释 。本身对代码逻辑没有任何影响(作用: 判断是否存在 + 读取内容信息),使用效果完全由使用者决定。

注解被很多规范使用,作为标记或者约定,如 jsr303 参数校验

1. 注解合并的含义

在 springboot 中注解发挥了很大的作用,而这些作用仅在springboot中有用,就好比@AliasFor。

注解本身并不能被注解继承,而 springboot 中却看到大量的合并注解就好比

@RestController = @Controller + @ResponseBody

这就是 @AliasFor 的功劳。需要注意的是,离开 Spring 就无法使用了。

spring 的注解都是由下面这个类读取的,所以 spring 的注解可以进行注解合并(仅限 Spring注解 Only Spring Annotations)

public interface MergedAnnotations

2. @AliasFor 的作用

@AliasFor 有四个作用:

  1. 注释中的显式别名:
public @interface ContextConfiguration {
  // 在@ContextConfiguration中, value和locations是彼此的显式别名。
  @AliasFor("locations")
  String[] value() default {};
  
  @AliasFor("value")
  String[] locations() default {};
}
  1. 元注释中属性的显式别名:
// 在@XmlTestConfig中, xmlFiles是@ContextConfiguration中locations的显式别名。换句话说, xmlFiles覆盖了@ContextConfiguration中的locations属性。
@ContextConfiguration
public @interface XmlTestConfig {

   @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
   String[] xmlFiles();
}
  1. 注释中的隐式别名:
// 在@MyTestConfig中, value 、 groovyScripts和xmlFiles都是@ContextConfiguration中locations属性的显式元注释属性覆盖。因此,这三个属性也是彼此的隐式别名。
@ContextConfiguration
public @interface MyTestConfig {
  @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
  String[] value() default {};
  
  @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
  String[] groovyScripts() default {};
  
  @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
  String[] xmlFiles() default {};
}
  1. 注释中的传递隐式别名:
// 在@GroovyOrXmlTestConfig中, groovy是对 @MyTestConfig 中 groovyScripts 属性的显式覆盖;而xml是对 @ContextConfiguration 中的 locations 属性的显式覆盖。此外, groovy 和 xml 是彼此的可传递隐式别名,因为它们都有效地覆盖了 @ContextConfiguration 中的 locations 属性。
@MyTestConfig
public @interface GroovyOrXmlTestConfig {
  @AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
  String[] groovy() default {};
  
  @AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
  String[] xml() default {};
}

3. 注解合并的应用

  1. 合并 @RestController + RequestMapping
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface RestMapping {
  @AliasFor("path")
  String[] value() default {};
  @AliasFor("value")
  String[] path() default {};
}
  1. 通过 @EnableConfigurationProperties 注解, 合并 @Import 注解, 使得被 @EnableConfigurationProperties 注解上的类, 自动注入 EnableConfigurationPropertiesRegistrar.class (@Import 的功能)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {

	/**
	 * The bean name of the configuration properties validator.
	 * @since 2.2.0
	 */
	String VALIDATOR_BEAN_NAME = "configurationPropertiesValidator";

	/**
	 * Convenient way to quickly register
	 * {@link ConfigurationProperties @ConfigurationProperties} annotated beans with
	 * Spring. Standard Spring Beans will also be scanned regardless of this value.
	 * @return {@code @ConfigurationProperties} annotated beans to register
	 */
	Class<?>[] value() default {};

}

(完)


版权声明:本文为howeres原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。