一、注解的继承
注解继承分为两种情况: 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 有四个作用:
- 注释中的显式别名:
public @interface ContextConfiguration {
// 在@ContextConfiguration中, value和locations是彼此的显式别名。
@AliasFor("locations")
String[] value() default {};
@AliasFor("value")
String[] locations() default {};
}
- 元注释中属性的显式别名:
// 在@XmlTestConfig中, xmlFiles是@ContextConfiguration中locations的显式别名。换句话说, xmlFiles覆盖了@ContextConfiguration中的locations属性。
@ContextConfiguration
public @interface XmlTestConfig {
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xmlFiles();
}
- 注释中的隐式别名:
// 在@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 {};
}
- 注释中的传递隐式别名:
// 在@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. 注解合并的应用
- 合并
@RestController
+RequestMapping
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface RestMapping {
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
}
- 通过
@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 {};
}
(完)