问题
- 注解是干什么的?
- 一个注解可以使用多次吗?如何使用?
- @Inherited是做什么的?
- @Target中的
TYPE_PARAMETER和TYPE_USER
用在什么地方? - 泛型中如何使用注解?
- 注解定义可以实现继承吗?
- spring对注解有哪些增强?@Aliasfor注解是干什么的?
什么是注解
代码中注释大家都熟悉吧,注释是给开发者看的,可以提升代码的可读性和可维护性,但是对于java编译器和虚拟机来说是没有意义的,编译之后的字节码文件中是没有注释信息的;而注解和注释有点类似,唯一的区别就是注释是给人看的,而注解是给编译器和虚拟机看的,编译器和虚拟机在运行的过程中可以获取注解信息,然后可以根据这些注解的信息做各种想做的事情。比如:大家对@Override应该比较熟悉,就是一个注解,加在方法上,标注当前方法重写了父类的方法,当编译器编译代码的时候,会对@Override标注的方法进行验证,验证其父类中是否也有同样签名的方法,否则报错,通过这个注解是不是增强了代码的安全性。
总的来说:注解是对代码的一种增强,可以在代码编译或者程序运行期间获取注解的信息,然后根据这些信息做各种牛逼的事情。
注解语法
public @interface MyAnnotation {
}
注解中定义的参数
public @interface 注解名称{
[public] 参数类型 参数名称1() [default 参数默认值];
[public] 参数类型 参数名称2() [default 参数默认值];
[public] 参数类型 参数名称n() [default 参数默认值];
}
注解中可以定义多个参数,参数的定义有以下特点:
- 访问修饰符必须为public,不写默认为public
- 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组
- 该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作)
- 参数名称后面的()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法
- default代表默认值,值必须和第2点定义的类型一致
- 如果没有默认值,代表后续使用注解时必须给该类型元素赋值
@Target 指定注解的使用范围
看一下@Target源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
继续看一下ElementType源码:
public enum ElementType {
/*类、接口、枚举、注解上面*/
TYPE,
/*字段上*/
FIELD,
/*方法上*/
METHOD,
/*方法的参数上*/
PARAMETER,
/*构造函数上*/
CONSTRUCTOR,
/*本地变量上*/
LOCAL_VARIABLE,
/*注解上*/
ANNOTATION_TYPE,
/*包上*/
PACKAGE,
/*类型参数上*/
TYPE_PARAMETER,
/*类型名称上*/
TYPE_USE
}
指定注解的保留策略:@Retention
java程序的3个过程
- 源码阶段
- 源码被编译为字节码之后变成class文件
- 字节码被虚拟机加载然后运行
那么自定义注解会保留在上面的哪个阶段,则是有@Retention注解来指定。
看下@Retention源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
RetentionPolicy源码:
public enum RetentionPolicy {
/*注解只保留在源码中,编译为字节码之后就丢失了,也就是class文件中就不存在了*/
SOURCE,
/*注解只保留在源码和字节码中,运行阶段会丢失*/
CLASS,
/*源码、字节码、运行期间都存在*/
RUNTIME
}
综合案例
@Target(value = {
ElementType.TYPE,
ElementType.METHOD,
ElementType.FIELD,
ElementType.PARAMETER,
ElementType.CONSTRUCTOR,
ElementType.LOCAL_VARIABLE
})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann6 {
String value();
ElementType elementType();
}
@Ann6(value = "我用在类上", elementType = ElementType.TYPE)
public class UseAnnotation6 {
@Ann6(value = "我用在字段上", elementType = ElementType.FIELD)
private String a;
@Ann6(value = "我用在构造方法上", elementType = ElementType.CONSTRUCTOR)
public UseAnnotation6(@Ann6(value = "我用在方法参数上", elementType = ElementType.PARAMETER) String a) {
this.a = a;
}
@Ann6(value = "我用在了普通方法上面", elementType = ElementType.METHOD)
public void m1() {
@Ann6(value = "我用在了本地变量上", elementType = ElementType.LOCAL_VARIABLE) String a;
}
}
@Target(ElementType.TYPE_PARAMETER)
@Target(value = {
ElementType.TYPE_PARAMETER
})
@Retention(RetentionPolicy.RUNTIME)
@interface Ann7 {
String value();
}
public class UseAnnotation7<@Ann7("T0是在类上声明的一个泛型类型变量") T0, @Ann7("T1是在类上声明的一个泛型类型变量") T1> {
public <@Ann7("T2是在方法上声明的泛型类型变量") T2> void m1() {
}
public static void main(String[] args) throws NoSuchMethodException {
for (TypeVariable typeVariable : UseAnnotation7.class.getTypeParameters()) {
print(typeVariable);
}
for (TypeVariable typeVariable : UseAnnotation7.class.getDeclaredMethod("m1").getTypeParameters()) {
print(typeVariable);
}
}
private static void print(TypeVariable typeVariable) {
System.out.println("类型变量名称:" + typeVariable.getName());
Arrays.stream(typeVariable.getAnnotations()).forEach(System.out::println);
}
}
运行效果:
类型变量名称:T0
@com.javacode2018.lesson001.demo18.Ann7(value=T0是在类上声明的一个泛型类型变量)
类型变量名称:T1
@com.javacode2018.lesson001.demo18.Ann7(value=T1是在类上声明的一个泛型类型变量)
类型变量名称:T2
@com.javacode2018.lesson001.demo18.Ann7(value=T2是在方法上声明的泛型类型变量)
AnnotatedElement常用方法
// 该元素如果存在指定类型的注解,则返回这些注解
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
// 返回此元素上存在的所有注解,包含从父类继承的
Annotation[] getAnnotations();
// 判断此元素上是否存在指定类型的注解
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
// 返回直接存在于此元素上的所有注解,不包括父类的注解
Annotation[] getDeclaredAnnotations();
案例
两个自定义注解:
@Target({ElementType.PACKAGE,
ElementType.TYPE,
ElementType.FIELD,
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ann1 {
public String value();
}
@Target({ElementType.PACKAGE,
ElementType.TYPE,
ElementType.FIELD,
ElementType.CONSTRUCTOR,
ElementType.METHOD,
ElementType.PARAMETER,
ElementType.TYPE_PARAMETER,
ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Ann2 {
public int value();
}
注解标注的类:
@Ann1("用在类上")
@Ann2(0)
public class UseAnnotation1<@Ann1("用在变量类型") T0, @Ann2(12) T1> {
@Ann1("用在字段上")
@Ann2(2)
private String name;
private Map<@Ann1("用在泛型上,String") @Ann2(3) String, @Ann1("用在泛型上,Integer") @Ann2(4) Integer> map;
@Ann1("用在构造器上")
@Ann2(5)
public UseAnnotation1() {
}
@Ann1("用在了返回值上")
@Ann2(6)
public String m1(@Ann1("用在了参数上") @Ann2(7) String name) {
return null;
}
}
测试方法:
@Test
public void testAnnotation() throws NoSuchFieldException, NoSuchMethodException {
Annotation[] annotations = UseAnnotation1.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println("------------------");
TypeVariable<Class<UseAnnotation1>>[] typeParameters = UseAnnotation1.class.getTypeParameters();
for (TypeVariable<Class<UseAnnotation1>> typeParameter : typeParameters) {
Annotation[] annotations1 = typeParameter.getAnnotations();
for (Annotation annotation : annotations1) {
System.out.println(annotation);
}
}
System.out.println("------------------");
Field name = UseAnnotation1.class.getDeclaredField("name");
Annotation[] annotations1 = name.getAnnotations();
for (Annotation annotation : annotations1) {
System.out.println(annotation);
}
System.out.println("-----------------");
Field field = UseAnnotation1.class.getDeclaredField("map");
Type genericType = field.getGenericType();
Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
AnnotatedType annotatedType = field.getAnnotatedType();
AnnotatedType[] annotatedActualTypeArguments = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
int i = 0;
for (AnnotatedType actualTypeArgument : annotatedActualTypeArguments) {
Type actualTypeArgument1 = actualTypeArguments[i++];
String typeName = actualTypeArgument1.getTypeName();
System.out.println(typeName);
for (Annotation annotation : actualTypeArgument.getAnnotations()) {
System.out.println(annotation);
}
}
System.out.println("-----------------");
Constructor<?> constructor = UseAnnotation1.class.getConstructors()[0];
for (Annotation annotation : constructor.getAnnotations()) {
System.out.println(annotation);
}
System.out.println("-----------------");
Method method = UseAnnotation1.class.getMethod("m1", String.class);
for (Annotation annotation : method.getAnnotations()) {
System.out.println(annotation);
}
System.out.println("-----------------");
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
Annotation[] annotations2 = parameter.getAnnotations();
for (Annotation annotation : annotations2) {
System.out.println(annotation);
}
}
}
运行结果:
@com.spring.annotation.Ann1(value=用在类上)
@com.spring.annotation.Ann2(value=0)
------------------
@com.spring.annotation.Ann1(value=用在变量类型)
@com.spring.annotation.Ann2(value=12)
------------------
@com.spring.annotation.Ann1(value=用在字段上)
@com.spring.annotation.Ann2(value=2)
-----------------
java.lang.String
@com.spring.annotation.Ann1(value=用在泛型上,String)
@com.spring.annotation.Ann2(value=3)
java.lang.Integer
@com.spring.annotation.Ann1(value=用在泛型上,Integer)
@com.spring.annotation.Ann2(value=4)
-----------------
@com.spring.annotation.Ann1(value=用在构造器上)
@com.spring.annotation.Ann2(value=5)
-----------------
@com.spring.annotation.Ann1(value=用在了返回值上)
@com.spring.annotation.Ann2(value=6)
-----------------
@com.spring.annotation.Ann1(value=用在了参数上)
@com.spring.annotation.Ann2(value=7)
@Inherited : 实现类之间的注解继承
看一下这个注解的源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
我们通过@Target元注解的属性值可以看出,这个@Inherited 是专门修饰注解的。
作用:让子类可以继承父类中被@Inherited修饰的注解,注意是继承父类中的,如果接口中的注解也使用@Inherited修饰了,那么接口的实现类是无法继承这个注解的
案例
public class InheritAnnotationTest {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A1{}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@interface A2{}
@A1
interface I1{}
@A2
class C1{}
class M extends C1 implements I1{}
public static void main(String[] args) {
Annotation[] annotations = M.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
运行结果:
@com.spring.annotation.InheritAnnotationTest$A2()
从输出中可以看出类可以继承父类上被@Inherited修饰的注解,而不能继承接口上被@Inherited修饰的注解,这个一定要注意
@Repeatable重复使用注解
如果我们想重复使用注解的时候,需要用到@Repeatable注解
使用步骤
- 先定义容器注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Repeatable(A1s.class)
@interface A1 {
String name();
}
- 为注解指定容器
//容器注解中必须有个value类型的参数,参数类型为子注解类型的数组。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@interface A1s {
A1[] value();
}
- 使用注解
@A1(name = "我是类上的第一个A1注解")
@A1(name = "我是类上的第二个A1注解")
public class RepeatableTest {
@A1s(
{@A1(name = "我是变量上的第一个A1S注解"),
@A1(name = "我是变量上的第一个A1S注解")
})
private String V1;
}
测试:
public static void main(String[] args) throws NoSuchFieldException {
Annotation[] annotations = RepeatableTest.class.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
System.out.println("------------------");
Field name = RepeatableTest.class.getDeclaredField("V1");
Annotation[] annotations1 = name.getAnnotations();
for (Annotation annotation : annotations1) {
System.out.println(annotation);
}
}
运行结果:
@com.spring.annotation.RepeatableAnnTest$A1s(value=[@com.spring.annotation.RepeatableAnnTest$A1(name=我是类上的第一个A1注解), @com.spring.annotation.RepeatableAnnTest$A1(name=我是类上的第二个A1注解)])
------------------
@com.spring.annotation.RepeatableAnnTest$A1s(value=[@com.spring.annotation.RepeatableAnnTest$A1(name=我是变量上的第一个A1S注解), @com.spring.annotation.RepeatableAnnTest$A1(name=我是变量上的第一个A1S注解)])
Disconnected from the target VM, address: '127.0.0.1:59466', transport: 'socket'
我们先看一个案例
public class AliasForAnnTest {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface A1 {
String value() default "a";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@A1
@interface B1 {
String value() default "b";
}
@B1("b1")
public class Ann {
}
public static void main(String[] args) {
A1 mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(Ann.class, A1.class);
System.out.println(mergedAnnotation);
System.out.println("------------");
B1 mergedAnnotation1 = AnnotatedElementUtils.getMergedAnnotation(Ann.class, B1.class);
System.out.println(mergedAnnotation1);
}
}
运行结果:
@com.spring.annotation.AliasForAnnTest$A1(value=a)
------------
@com.spring.annotation.AliasForAnnTest$B1(value=b1)
代码很简单,没有什么问题。如果我想在Ann类上给注解A1设置值怎么办呢?@Aliasfor可以实现
@Aliasfor 案例
在上面的案例上直接修改B1:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@A1
@interface B1 {
String value() default "b";
//添加一个@AliasFor 注解,指定注解类和注解属性名
@AliasFor(annotation = A1.class, value = "value")
String a1Value();
}
Ann类修改:
@B1(value = "b1" , a1Value = "a1")
public class Ann {
}
其他不变,运行结果:
@com.spring.annotation.AliasForAnnTest$A1(value=a1)
------------
@com.spring.annotation.AliasForAnnTest$B1(a1Value=a1, value=b1)
在来一个案例
public class AliasForAnn {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@interface A1 {
@AliasFor(value = "v2")
String v1() default "";
@AliasFor(value = "v1")
String v2() default "";
}
@A1(v1 = "类 v1")
public class Test {
@A1(v2 = "属性 v2")
private String name;
}
public static void main(String[] args) throws NoSuchFieldException {
A1 mergedAnnotation = AnnotatedElementUtils.getMergedAnnotation(Test.class, A1.class);
System.out.println(mergedAnnotation);
System.out.println("--------");
A1 name = AnnotatedElementUtils.getMergedAnnotation(Test.class.getDeclaredField("name"), A1.class);
System.out.println(name);
}
}
运行结果:
@com.spring.annotation.AliasForAnn$A1(v1=类 v1, v2=类 v1)
--------
@com.spring.annotation.AliasForAnn$A1(v1=属性 v2, v2=属性 v2)
我们看下@AliasFor的源码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
@AliasFor("attribute")
String value() default "";
@AliasFor("value")
String attribute() default "";
Class<? extends Annotation> annotation() default Annotation.class;
}
AliasFor注解中value和attribute互为别名,随便设置一个,同时会给另外一个设置相同的值。