Java注解合并,注解继承
注解的继承性回顾根据注解继承的特性,我们再做AOP切面拦截的时候会遇到拦截不到的问题,今天我们就讲解下对这些特殊情况如何解决,对源码不做过渡深入的讲解。
注解的继承性回顾
- 被@Inherited元注解标注的注解标注在类上的时候,子类可以继承父类上的注解。
- 注解未被@Inherited元注解标注的,该注解标注在类上时,子类不会继承父类上标注的注解。
- 注解标注在接口上,其子类及子接口都不会继承该注解
- 注解标注在类或接口方法上,其子类重写该方法不会继承父类或接口中方法上标记的注解
根据注解继承的特性,我们再做AOP切面拦截的时候会遇到拦截不到的问题,今天我们就讲解下对这些特殊情况如何解决,对源码不做过渡深入的讲解。
一、注解的继承
注解继承分为两种情况:
- 类级别 Type (Class, Interface),
- 属性和方法级别 (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)
2. @AliasFor 的作用
@AliasFor 有四个作用:
2.1 注释中的显式别名:
public @interface ContextConfiguration {
@AliasFor("locations")
String[] value() default {};
@AliasFor("value")
String[] locations() default {};
}
在@ContextConfiguration中, value和locations是彼此的显式别名。
2.2 元注释中属性的显式别名:
@ContextConfiguration
public @interface XmlTestConfig {
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xmlFiles();
}
在@XmlTestConfig中, xmlFiles是@ContextConfiguration中locations的显式别名。换句话说, xmlFiles覆盖了@ContextConfiguration中的locations属性。
2.3 注释中的隐式别名:
@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 {};
}
在@MyTestConfig中, value 、 groovyScripts和xmlFiles都是@ContextConfiguration中locations属性的显式元注释属性覆盖。因此,这三个属性也是彼此的隐式别名。
2.4 注释中的传递隐式别名:
@MyTestConfig
public @interface GroovyOrXmlTestConfig {
@AliasFor(annotation = MyTestConfig.class, attribute = "groovyScripts")
String[] groovy() default {};
@AliasFor(annotation = ContextConfiguration.class, attribute = "locations")
String[] xml() default {};
}
在@GroovyOrXmlTestConfig中, groovy是对 @MyTestConfig 中 groovyScripts 属性的显式覆盖;而xml是对 @ContextConfiguration 中的 locations 属性的显式覆盖。此外, groovy 和 xml 是彼此的可传递隐式别名,因为它们都有效地覆盖了 @ContextConfiguration 中的 locations 属性。
三、注解合并的应用
spring中有时候一个类上面标记很多注解。
实际上Java注解可以进行继承(也就是把多个注解合并成1个)
比如说SpringMVC的注解
@RestController
@RequestMapping("/person")
可以合并为一个
@PathRestController("/user")
实现是:
import java.lang.annotation.Documented;
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.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RestController
@RequestMapping
public @interface PathRestController {
@AliasFor("path")
String[] value() default {};
@AliasFor("value")
String[] path() default {};
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)