Spring对注解的增强以及Java注解的定义和使用 您所在的位置:网站首页 AOP增强处理类型 Spring对注解的增强以及Java注解的定义和使用

Spring对注解的增强以及Java注解的定义和使用

2024-02-14 13:05| 来源: 网络整理| 查看: 265

Java注解的定义和使用 一、什么是注解?二、定义注解2.1 语法2.2 注解中定义参数2.3 指定注解的使用范围:@Target2.4 指定注解的保留策略:@Retention 三、使用注解3.1 无参注解3.2 一个参数的注解3.3 一个参数为value的注解,可以省略参数名称3.4 数组类型参数3.5 @Target(ElementType.TYPE_PARAMETER)3.6 @Target(ElementType.TYPE_USE)3.7 注解信息的获取3.7.1 解析类上的注解3.7.2 解析类上的类型变量3.7.3 解析字段name上的注解3.7.4 解析泛型字段map上的注解3.7.5 解析构造函数上的注解3.7.6 解析m1方法上的注解3.7.7 解析m1方法参数注解 四、实现类之间的注解继承:@Inherited四、@Repeatable重复使用注解五、 @AliasFor:对注解进行增强七、自定义注解支持定义bean名称八、常见面试题

一、什么是注解?

注解是对代码的一种增强,可以在代码编译或代码运行时,获取注解的信息,然后根据这些信息做一些针对性的事情

二、定义注解 2.1 语法

jdk中注解相关的类和接口都定义在java.lang.annotation包中

public @interface 注解名称{ [public] 参数类型 参数名称1() [default 参数默认值]; String value() default ""; ... } 2.2 注解中定义参数

注解中可以定义多个参数,参数的定义有以下特点:

访问修饰符必须为public,不写默认为public元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一维数组元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value参数名称后面的()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法default代表默认值,值必须和第2点定义的类型一致如果没有默认值,代表后续使用注解时必须给该类型元素赋值 2.3 指定注解的使用范围:@Target @Target(value = {ElementType.TYPE,ElementType.METHOD}) public @interface MyAnnotation { }

上面指定了MyAnnotation注解可以用在类、接口、注解类型、枚举类型以及方法上面。 自定义注解上也可以不使用@Target注解,如果不使用,表示自定义注解可以用在任何地方

看一下@Target源码: @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); } 参数value,是ElementType类型的一个数组,再来看一下ElementType package java.lang.annotation; /*注解的使用范围*/ public enum ElementType { /*类、接口、枚举、注解上面*/ TYPE, /*字段上*/ FIELD, /*方法上*/ METHOD, /*方法的参数上*/ PARAMETER, /*构造函数上*/ CONSTRUCTOR, /*本地变量上*/ LOCAL_VARIABLE, /*注解上*/ ANNOTATION_TYPE, /*包上*/ PACKAGE, /*类型参数上*/ TYPE_PARAMETER, /*类型名称上*/ TYPE_USE } 2.4 指定注解的保留策略:@Retention

java程序的三个过程:

源码阶段源码被编译为字节码之后变成class文件字节码被虚拟机加载然后运行 @Retention(RetentionPolicy.SOURCE) public @interface MyAnnotation { }

上面指定了MyAnnotation只存在于源码阶段,后面的2个阶段都会丢失

来看一下@Retention源码 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); } value参数,类型为RetentionPolicy枚举 public enum RetentionPolicy { /*注解只保留在源码中,编译为字节码之后就丢失了,也就是class文件中就不存在了*/ SOURCE, /*注解只保留在源码和字节码中,运行阶段会丢失*/ CLASS, /*源码、字节码、运行期间都存在*/ RUNTIME } 三、使用注解 3.1 无参注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation1 { //@1 } @MyAnnotation1 //@2 public class UseAnnotation1 {} 3.2 一个参数的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation2 { //@1 String name(); } @MyAnnotation2(name = "zkc") //@2 public class UseAnnotation2 {} 3.3 一个参数为value的注解,可以省略参数名称 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation3 { String value();//@1 } @MyAnnotation3("zkc") //@2 public class UseAnnotation3 {} 3.4 数组类型参数 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface MyAnnotation4 { String[] name();//@1 } @MyAnnotation4(name = {"我是zkc", "学spring"}) //@2 public class UseAnnotation4 { @MyAnnotation4(name = "如果只有一个值,{}可以省略") //@3 public class T1 {} } 3.5 @Target(ElementType.TYPE_PARAMETER)

这个是1.8加上的,用来标注类型参数,类型参数一般在类后面声明或者方法上声明

@Target(value = { ElementType.TYPE_PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @interface Ann7 { String value(); } public class UseAnnotation7 { public 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是在方法上声明的泛型类型变量) 3.6 @Target(ElementType.TYPE_USE)

这个是1.8加上的,能用在任何类型名称上

@Target({ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @interface Ann10 { String value(); } @Ann10("用在了类上") public class UserAnnotation10 { private Map map; public String m1(String name) { return null; } }

类后面的V1、V2都是类型名称,Map后面的尖括号也是类型名称,m1方法前面也定义了一个类型变量,名称为T

3.7 注解信息的获取 @Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @interface Ann11 { String value(); } @Target({ElementType.PACKAGE, ElementType.TYPE, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) @interface Ann11_0 { int value(); } @Ann11("用在了类上") @Ann11_0(0) public class UseAnnotation11 { @Ann11("用在了字段上") @Ann11_0(3) private String name; private Map map; @Ann11("用在了构造方法上") @Ann11_0(6) public UseAnnotation11() { this.name = name; } @Ann11("用在了返回值上") @Ann11_0(7) public String m1(@Ann11("用在了参数上") @Ann11_0(8) String name) { return null; } } 3.7.1 解析类上的注解 @Ann11("用在了类上") @Ann11_0(0) public class UseAnnotation11.... @Test public void m1() { for (Annotation annotation : UserAnnotation10.class.getAnnotations()) { System.out.println(annotation); } } @com.javacode2018.lesson001.demo18.Ann11(value=用在了类上) @com.javacode2018.lesson001.demo18.Ann11_0(value=0) 3.7.2 解析类上的类型变量

解析类名后面的尖括号的部分

UseAnnotation11 @Test public void m2() { TypeVariable[] typeParameters = UserAnnotation10.class.getTypeParameters(); for (TypeVariable typeParameter : typeParameters) { System.out.println(typeParameter.getName() + "变量类型注解信息:"); Annotation[] annotations = typeParameter.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } } V1变量类型注解信息: @com.javacode2018.lesson001.demo18.Ann11(value=用在了类变量类型V1上) @com.javacode2018.lesson001.demo18.Ann11_0(value=1) V2变量类型注解信息: @com.javacode2018.lesson001.demo18.Ann11(value=用在了类变量类型V2上) @com.javacode2018.lesson001.demo18.Ann11_0(value=2) 3.7.3 解析字段name上的注解 @Test public void m3() throws NoSuchFieldException { Field nameField = UserAnnotation10.class.getDeclaredField("name"); for (Annotation annotation : nameField.getAnnotations()) { System.out.println(annotation); } } @com.javacode2018.lesson001.demo18.Ann11(value=用在了字段上) @com.javacode2018.lesson001.demo18.Ann11_0(value=3) 3.7.4 解析泛型字段map上的注解 @Test public void m4() throws NoSuchFieldException, ClassNotFoundException { Field field = UseAnnotation11.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++]; System.out.println(actualTypeArgument1.getTypeName() + "类型上的注解如下:"); for (Annotation annotation : actualTypeArgument.getAnnotations()) { System.out.println(annotation); } } } java.lang.String类型上的注解如下: @com.javacode2018.lesson001.demo18.Ann11(value=用在了泛型类型上,String) @com.javacode2018.lesson001.demo18.Ann11_0(value=4) java.lang.Integer类型上的注解如下: @com.javacode2018.lesson001.demo18.Ann11(value=用在了泛型类型上,Integer) @com.javacode2018.lesson001.demo18.Ann11_0(value=5) 3.7.5 解析构造函数上的注解 @Test public void m5() { Constructor constructor = UseAnnotation11.class.getConstructors()[0]; for (Annotation annotation : constructor.getAnnotations()) { System.out.println(annotation); } } @com.javacode2018.lesson001.demo18.Ann11(value=用在了构造方法上) @com.javacode2018.lesson001.demo18.Ann11_0(value=6) 3.7.6 解析m1方法上的注解 @Test public void m6() throws NoSuchMethodException { Method method = UseAnnotation11.class.getMethod("m1", String.class); for (Annotation annotation : method.getAnnotations()) { System.out.println(annotation); } } @com.javacode2018.lesson001.demo18.Ann11(value=用在了返回值上) @com.javacode2018.lesson001.demo18.Ann11_0(value=7) 3.7.7 解析m1方法参数注解 @Test public void m7() throws NoSuchMethodException { Method method = UseAnnotation11.class.getMethod("m1", String.class); for (Parameter parameter : method.getParameters()) { System.out.println(String.format("参数%s上的注解如下:", parameter.getName())); for (Annotation annotation : parameter.getAnnotations()) { System.out.println(annotation); } } } 参数arg0上的注解如下: @com.javacode2018.lesson001.demo18.Ann11(value=用在了参数上) @com.javacode2018.lesson001.demo18.Ann11_0(value=8)

上面参数名称为arg0,如果想让参数名称和源码中真实名称一致,操作如下:

如果你编译这个class的时候没有添加参数–parameters,运行的时候你会得到这个结果: Parameter: arg0 编译的时候添加了–parameters参数的话,运行结果会不一样: Parameter: args 对于有经验的Maven使用者,–parameters参数可以添加到maven-compiler-plugin的配置部分: org.apache.maven.plugins maven-compiler-plugin 3.1 -parameters 1.8 1.8 四、实现类之间的注解继承:@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{ //@1 } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited @interface A2{ //@2 } @A1 //@3 interface I1{} @A2 //@4 static class C1{} static class C2 extends C1 implements I1{} //@5 public static void main(String[] args) { for (Annotation annotation : C2.class.getAnnotations()) { //@6 System.out.println(annotation); } } } @com.javacode2018.lesson001.demo18.InheritAnnotationTest$A2() 四、@Repeatable重复使用注解

使用步骤:

先定义容器注解

容器注解中必须有个value类型的参数,参数类型为子注解类型的数组。

@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) @interface Ann12s { Ann12[] value(); //@1 } 为注解指定容器

一个注解可以重复使用,需要在注解上加上@Repeatable注解,@Repeatable中value的值为容器注解

@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) @Repeatable(Ann12s.class)//@2 @interface Ann12 { String name(); } 使用注解

重复使用相同的注解有2种方式,如下面代码

重复使用注解,如下面的类上重复使用@Ann12注解通过容器注解来使用更多个注解,如下面的字段v1上使用@Ann12s容器注解 @Ann12(name = "路人甲Java") @Ann12(name = "Spring系列") public class UseAnnotation12 { @Ann12s( {@Ann12(name = "Java高并发系列,见公众号"), @Ann12(name = "mysql高手系列,见公众号")} ) private String v1; } 获取注解信息 @Test public void test1() throws NoSuchFieldException { Annotation[] annotations = UseAnnotation12.class.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } System.out.println("-------------"); Field v1 = UseAnnotation12.class.getDeclaredField("v1"); Annotation[] declaredAnnotations = v1.getDeclaredAnnotations(); for (Annotation declaredAnnotation : declaredAnnotations) { System.out.println(declaredAnnotation); } } 五、 @AliasFor:对注解进行增强 源码 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface AliasFor { @AliasFor("attribute") String value() default ""; @AliasFor("value") String attribute() default ""; Class annotation() default Annotation.class; } 通过@AliasFor解决UseAnnotation13上给B14上的A14注解设置值 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface A14 { String value() default "a";//@0 } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @A14 //@6 @interface B14 { //@1 String value() default "b";//@2 @AliasFor(annotation = A14.class, value = "value") //@5 String a14Value(); } @B14(value = "路人甲Java",a14Value = "通过B14给A14的value参数赋值") //@3 public class UseAnnotation14 { @Test public void test1() { //AnnotatedElementUtils是spring提供的一个查找注解的工具类 System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation14.class, B14.class)); System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation14.class, A14.class)); } } @com.javacode2018.lesson001.demo18.B14(a14Value=通过B14给A14的value参数赋值, value=路人甲Java) @com.javacode2018.lesson001.demo18.A14(value=通过B14给A14的value参数赋值)

这个相当于给某个注解指定别名,即将B1注解中a14Value参数作为A14中value参数的别名,当给B1的a14Value设置值的时候,就相当于给A14的value设置值,有个前提是@AliasFor注解的annotation参数指定的注解需要加载当前注解上面,如:@6

同一个注解中使用@AliasFor @Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @interface A15 { @AliasFor("v2")//@1 String v1() default ""; @AliasFor("v1")//@2 String v2() default ""; } @A15(v1 = "我是v1") //@3 public class UseAnnotation15 { @A15(v2 = "我是v2") //@4 private String name; @Test public void test1() throws NoSuchFieldException { //AnnotatedElementUtils是spring提供的一个查找注解的工具类 System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation15.class, A15.class)); System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation15.class.getDeclaredField("name"), A15.class)); } } @com.javacode2018.lesson001.demo18.A15(v1=我是v1, v2=我是v1) @com.javacode2018.lesson001.demo18.A15(v1=我是v2, v2=我是v2)

A15注解中(@1和@2)的2个参数都设置了@AliasFor,@AliasFor如果不指定annotation参数的值,那么annotation默认值就是当前注解,所以上面2个属性互为别名,当给v1设置值的时候也相当于给v2设置值,当给v2设置值的时候也相当于给v1设置值。

互为别名的属性属性值类型,默认值,都是相同的; 互为别名的注解必须成对出现,比如 value 属性添加了@AliasFor(“path”),那么 path 属性就必须添加@AliasFor(“value”); 互为别名的属性必须定义默认值。

@AliasFor中不指定value和attribute

当@AliasFor中不指定value或者attribute的时候,自动将@AliasFor修饰的参数作为value和attribute的值

@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface A16 { String name() default "a";//@0 } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @A16 @interface B16 { //@1 @AliasFor(annotation = A16.class) //@5 String name() default "b";//@2 } @B16(name="我是v1") //@3 public class UseAnnotation16 { @Test public void test1() throws NoSuchFieldException { //AnnotatedElementUtils是spring提供的一个查找注解的工具类 System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation16.class, A16.class)); System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation16.class, B16.class)); } } @com.javacode2018.lesson001.demo18.A16(name=我是v1) @com.javacode2018.lesson001.demo18.B16(name=我是v1) 七、自定义注解支持定义bean名称 @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Component //@1 public @interface MyBean { @AliasFor(annotation = Component.class) //@2 String value() default ""; //@3 }

重点在于@1和@2这2个地方的代码,通过上面的参数可以间接给@Component注解中的value设置值。

@MyBean("service1Bean") public class Service1 {} service1Bean->com.javacode2018.lesson001.demo22.test3.Service1@222545dc 八、常见面试题 注解是干什么的?

注解是对代码的一种增强,可以在代码编译或代码运行时,获取注解的信息,然后根据这些信息做一些针对性的事情

一个注解可以使用多次么?如何使用?

①定义一个容器注解,容器注解里定义value类型为子注解的数组。 ②在子注解上打上@Repeatable 注解,value 为容器注解的class。该子注解就可以使用多次。

@Inherited是做什么的?

定义了 @Inherited 注解的注解被使用在类上后,子类可以继承该注解。 被用在接口上,不能继承

@Target中的TYPE_PARAMETER和TYPE_USER用在什么地方?

两个注解都是 JDK8 加上的。 TYPE_PARAMETER 表示注解用在类型参数上,主要是泛型。 TYPE_USE 表示注解能用在任何地方

泛型中如何使用注解?

跟其他地方一样用。注解的 target 得有 ElementType.TYPE_PARAMETER 范围,或 TYPE_USE。

注解定义可以实现继承么?

可以,注解如果用 @Inherited 修饰了,定义在父类上是可以被子类继承的

spring中对注解有哪些增强?@Aliasfor注解是干什么的?

@Aliasfor 允许给注解设置别名,通过修改该别名字段的值,起到修改修饰注解值的作用。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有