前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring系列第十八讲 深入理解Java注解及Spring对注解的增强(下)

Spring系列第十八讲 深入理解Java注解及Spring对注解的增强(下)

作者头像
易兮科技
发布2020-11-24 15:41:57
1.1K0
发布2020-11-24 15:41:57
举报
文章被收录于专栏:CSDN博客专栏CSDN博客专栏

Spring系列第十八讲

@Inherit:实现类之间的注解继承

用法

来看一下这个注解的源码

代码语言:javascript
复制
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

我们通过@Target元注解的属性值可以看出,这个@Inherited 是专门修饰注解的。

作用:让子类可以继承父类中被@Inherited修饰的注解,注意是继承父类中的,如果接口中的注解也使用@Inherited修饰了,那么接口的实现类是无法继承这个注解的

案例

代码语言:javascript
复制
package com.javacode2018.lesson001.demo18;

import java.lang.annotation.*;

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);
        }
    }
}

@1:定义了一个注解A1,上面使用了@Inherited,表示这个具有继承功能

@2:定义了一个注解A2,上面使用了@Inherited,表示这个具有继承功能

@3:定义接口I1,上面使用了@A1注解

@4:定义了一个C1类,使用了A2注解

@5:C2继承了C1并且实现了I1接口

@6:获取C2上以及从父类继承过来的所有注解,然后输出

运行输出:

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.InheritAnnotationTest$A2()

从输出中可以看出类可以继承父类上被@Inherited修饰的注解,而不能继承接口上被@Inherited修饰的注解,这个一定要注意

@Repeatable重复使用注解

来看一段代码:

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Ann12{}

@Ann12
@Ann12
public class UseAnnotation12 {
}

上面代码会报错,原因是:UseAnnotation12上面重复使用了@Ann12注解,默认情况下@Ann12注解是不允许重复使用的。

像上面这样,如果我们想重复使用注解的时候,需要用到@Repeatable注解

使用步骤

先定义容器注解

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@interface Ann12s {
    Ann12[] value(); //@1
}

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

为注解指定容器

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

代码语言:javascript
复制
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
@Repeatable(Ann12s.class)//@2
@interface Ann12 {
    String name();
}

使用注解

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

  1. 重复使用注解,如下面的类上重复使用@Ann12注解
  2. 通过容器注解来使用更多个注解,如下面的字段v1上使用@Ann12s容器注解
代码语言:javascript
复制
@Ann12(name = "路人甲Java")
@Ann12(name = "Spring系列")
public class UseAnnotation12 {
    @Ann12s(
            {@Ann12(name = "Java高并发系列,见公众号"),
                    @Ann12(name = "mysql高手系列,见公众号")}
    )
    private String v1;
}

获取注解信息

代码语言:javascript
复制
com.javacode2018.lesson001.demo18.UseAnnotation12

@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);
    }
}

运行输出:

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.Ann12s(value=[@com.javacode2018.lesson001.demo18.Ann12(name=Java), @com.javacode2018.lesson001.demo18.Ann12(name=Spring系列)])
-------------
@com.javacode2018.lesson001.demo18.Ann12s(value=[@com.javacode2018.lesson001.demo18.Ann12(name=Java高并发系列), @com.javacode2018.lesson001.demo18.Ann12(name=mysql高手系列)])

上面就是java中注解的功能,下面我们来介绍spring对于注解方面的支持。

先来看一个问题

代码如下:

代码语言:javascript
复制
package com.javacode2018.lesson001.demo18;

import org.junit.Test;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface A1 {
    String value() default "a";//@0
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@A1
@interface B1 { //@1
    String value() default "b";//@2
}

@B1("Java") //@3
public class UseAnnotation13 {
    @Test
    public void test1() {
        //AnnotatedElementUtils是spring提供的一个查找注解的工具类
        System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, B1.class));
        System.out.println(AnnotatedElementUtils.getMergedAnnotation(UseAnnotation13.class, A1.class));
    }
}

@0:A1注解value参数值默认为a

@1:B1注解上使用到了@A1注解

@2:B1注解value参数值默认为b

@2:UseAnnotation13上面使用了@B1注解,value参数的值为:路人甲java

test1方法中使用到了spring中的一个类AnnotatedElementUtils,通过这个工具类可以很方便的获取注解的各种信息,方法中的2行代码用于获取UseAnnotation13类上B1注解和A1注解的信息。

运行test1方法输出:

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.B1(value=Java)
@com.javacode2018.lesson001.demo18.A1(value=a)

上面用法很简单,没什么问题。

此时有个问题:此时如果想在UseAnnotation13上给B1上的A1注解设置值是没有办法的,注解定义无法继承导致的,如果注解定义上面能够继承,那用起来会爽很多,spring通过@Aliasfor方法解决了这个问题。

Spring @AliasFor:对注解进行增强

直接上案例,然后解释代码。

案例1:通过@AliasFor解决刚才难题

代码语言:javascript
复制
package com.javacode2018.lesson001.demo18;

import org.junit.Test;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@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));
    }
}

运行输出:

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.B14(a14Value=通过B14给A14的value参数赋值, value=Java)
@com.javacode2018.lesson001.demo18.A14(value=通过B14给A14的value参数赋值)

注意上面diam的@3只使用了B14注解,大家认真看一下,上面输出汇总可以看出A14的value值和B14的a14Value参数值一样,说明通过B14给A14设置值成功了。

重点在于代码@5,这个地方使用到了@AliasFor注解:

代码语言:javascript
复制
@AliasFor(annotation = A14.class, value = "value")

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

案例2:同一个注解中使用@AliasFor

代码语言:javascript
复制
package com.javacode2018.lesson001.demo18;

import org.junit.Test;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@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));
    }
}

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

运行输出

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.A15(v1=我是v1, v2=我是v1)
@com.javacode2018.lesson001.demo18.A15(v1=我是v2, v2=我是v2)

从输出中可以看出v1和v2的值始终是相等的,上面如果同时给v1和v2设置值的时候运行代码会报错。

我们回头来看看@AliasFor的源码:

代码语言:javascript
复制
@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互为别名,随便设置一个,同时会给另外一个设置相同的值。

案例3:@AliasFor中不指定value和attribute

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

代码语言:javascript
复制
package com.javacode2018.lesson001.demo18;

import org.junit.Test;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@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));
    }
}

运行输出:

代码语言:javascript
复制
@com.javacode2018.lesson001.demo18.A16(name=我是v1)
@com.javacode2018.lesson001.demo18.B16(name=我是v1)
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-11-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Spring系列第十八讲
  • @Inherit:实现类之间的注解继承
    • 用法
      • 案例
      • @Repeatable重复使用注解
        • 使用步骤
          • 为注解指定容器
            • 使用注解
              • 获取注解信息
              • 先来看一个问题
              • Spring @AliasFor:对注解进行增强
                • 案例1:通过@AliasFor解决刚才难题
                  • 案例2:同一个注解中使用@AliasFor
                    • 案例3:@AliasFor中不指定value和attribute
                    相关产品与服务
                    容器服务
                    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档