【小家Spring】Spring贡献的多个注解相关的工具类:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...(上)

简介: 【小家Spring】Spring贡献的多个注解相关的工具类:AnnotationUtils、AnnotatedElementUtils、AnnotationConfigUtils...(上)

前言


本文主要聊聊Spring提供的多个关于注解相关的工具类:AnnotationUtils和AnnotatedElementUtils等等

因为很多逻辑都封装在了工具类里面,因此要理解Spring的深层意思,有时候不了解工具类也是比较麻烦的


虽然说都是Spring内部去使用,但是有些工具类是public的(下面会有介绍),所以我们平时若有需要,也是可以使用的


image.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.pngimage.png

image.png


本文要说明的工具类如上图,一共会讲述9个(排名不分先后)。(ParserStrategyUtils忽略,没什么好说的,只有一个方法处理Aware的注入。AnnotationReadingVisitorUtils也忽略,它和ASM字节码相关,后面再讨论)


AnnotationUtils(最重要)


总之,从类名就能看出来。这是Spring提供的获取、处理注解的工具类。

可能有小伙伴就非常好奇了:JDK已经提供给我们获取注解的方法了,Spring为何要多此一举呢?如下:

@MyAnno
interface Eat {
}
class Parent implements Eat {
}
class Child extends Parent {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping // 特意注解上放一个注解,方面测试看结果
@Inherited
@interface MyAnno {
}
  // 现在我们来获取类上面的注解如下
    public static void main(String[] args) {
        MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);
        MyAnno anno2 = Parent.class.getAnnotation(MyAnno.class);
        MyAnno anno3 = Child.class.getAnnotation(MyAnno.class);
        System.out.println(anno1); //@com.fsx.maintest.MyAnno()
        System.out.println(anno2); //null
        System.out.println(anno3); //null
    }

此处必须对结果说明一点:我们的注解标注了@Inherited表示该注解可以被继承,但是anno2和anno3还是null。需要注意:@Inherited继承只能发生在类上,而不能发生在接口上(也就是说标注在接口上仍然是不能被继承的)


介绍Class提供的获取注解相关方法:


  • <A extends Annotation>A getAnnotation(Class<A>annotationClass):获取该class对象对应类上指定类型的Annotation,如果该类型注解不存在,则返回null

Annotation[] getAnnotations():返回修饰该class对象对应类上存在的所有Annotation

  • <A extends Annotation>A getDeclaredAnnotation(Class<A>annotationClass):这是Java 8中新增的,该方法获取直接修饰该class对象对应类的指定类型的Annotation,如果不存在,则返回null(也就说只找自己的,继承过来的注解这个方法就不管了)
  • Annotation[] getDeclaredAnnotations():返回修饰该Class对象对应类上存在的所有Annotation(同上,继承的不管)


  • <A extends Annotation>A[] getAnnotationByType(Class<A>annotationClass):该方法的功能与前面介绍的getAnnotation()方法基本相似,但由于Java8增加了重复注解功能,因此需要使用该方法获取修饰该类的指定类型的多个Annotation(会考虑继承的注解)
  • <A extends Annotation>A[] getDeclaredAnnotationByType(Class<A>annotationClass):相信不解释你也懂


更多Class中的方法使用,请参阅:Java反射获取类和对象信息全解析


既然JDK提供给了我们这么多的注解相关方法,乍一看是够用了呢?为何Spring还自己写个工具类呢?我觉得这也是Spring的强大之处,往往写出来的东西比JDK的还强大。比如试想一下下面两个场景,你就没觉得疑惑?


1.@AliasFor(Spring4.2之后才有的)为何能生效呢?大大的方便了我们注解的使用


2.@RequestMapping注解明明不能继承(即使有@Inherited也不能写在接口上嘛),但为何我们把它写在接口上时,用Controller去实现的时候却像是被继承了一样呢?


注解支持的数据类型:


  • 所有基本类型(int,float,boolean,byte,double,char,long,short) 注意:包装类型不支持
  • String
  • Class
  • enum
  • Annotation
  • 上述类型的数组


注解是不支持继承的,因此不能使用关键字extends来继承某个@interface,但注解在编译后,编译器会自动继承java.lang.annotation.Annotation接口(从反编译代码里可以看出,类似于Enum)


*如果你不能解释至少上面两个疑问,那么你就很有必要来看看这个工具类了~~~~*

AnnotationUtils方法介绍

AnnotationUtils内部使用了很多ConcurrentReferenceHashMap(弱、软引用)作为缓存,来各种提高效率


开门见山,先直接上一个最强的方法,也是该工具类的核心:synthesizeAnnotation

  static <A extends Annotation> A synthesizeAnnotation(A annotation) {
    return synthesizeAnnotation(annotation, null);
  }
  public static <A extends Annotation> A synthesizeAnnotation(
      A annotation, @Nullable AnnotatedElement annotatedElement) {
    return synthesizeAnnotation(annotation, (Object) annotatedElement);
  }
  static <A extends Annotation> A synthesizeAnnotation(A annotation, @Nullable Object annotatedElement) {
    if (annotation instanceof SynthesizedAnnotation) {
      return annotation;
    }
    Class<? extends Annotation> annotationType = annotation.annotationType();
    if (!isSynthesizable(annotationType)) {
      return annotation;
    }
    DefaultAnnotationAttributeExtractor attributeExtractor =
        new DefaultAnnotationAttributeExtractor(annotation, annotatedElement);
    InvocationHandler handler = new SynthesizedAnnotationInvocationHandler(attributeExtractor);
    // Can always expose Spring's SynthesizedAnnotation marker since we explicitly check for a
    // synthesizable annotation before (which needs to declare @AliasFor from the same package)
    Class<?>[] exposedInterfaces = new Class<?>[] {annotationType, SynthesizedAnnotation.class};
    return (A) Proxy.newProxyInstance(annotation.getClass().getClassLoader(), exposedInterfaces, handler);
  }


作用:来获取一个动态代理注解(相当于调用者传进来的注解会被代理掉),该方法是别名注解@AliasFor的核心原理。


  • public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType):

    public static void main(String[] args) {
        MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);
        // 注解交给这么一处理  相当于就会被Spring代理了  这就是优势
        MyAnno sAnno1 = AnnotationUtils.getAnnotation(anno1, MyAnno.class);
        System.out.println(sAnno1); //@com.fsx.maintest.MyAnno()
        // 这样前后类型不一致的话,会把这个注解上面的注解给获取出来
        RequestMapping annotation = AnnotationUtils.getAnnotation(anno1, RequestMapping.class);
        System.out.println(annotation); //@org.springframework.web.bind.annotation.RequestMapping
    }
  • public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType):重载方法。上面annotation.annotationType();其实就是annotatedElement了
  • public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType):强大之处在于:它连桥接方法(BridgeMethod)都支持。参考;java中什么是bridge method(桥接方法)
  • public static Annotation[] getAnnotations(AnnotatedElement annotatedElement):获取指定类型上所有注解

    public static void main(String[] args) {
        MyAnno anno1 = Eat.class.getAnnotation(MyAnno.class);
        // 注意这两种写法的区别:
        // 这个相当于是获取Child.class的它上面的所有注解, 所以只有继承过来的一个@MyAnno
        Annotation[] sAnno1 = AnnotationUtils.getAnnotations(Child.class);
        // 而这里传入的为anno1.annotationType,所以相当于获取该注解上面的注解  所以使用的时候需要注意
        Annotation[] sAnno2 = AnnotationUtils.getAnnotations(anno1.annotationType());
        System.out.println(sAnno1);
        System.out.println(sAnno2);
    }


  • public static Annotation[] getAnnotations(Method method):支持桥接
  • public static <A extends Annotation> Set<A> getRepeatableAnnotations(AnnotatedElement annotatedElement, Class<A> annotationType):支持了Java8的重复注解
  • getDeclaredRepeatableAnnotations(重载方法略):不解释
  • public static <A extends Annotation> A findAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType):他的特点就是,会递归去你的父类、接口里把注解找到,找到既返回,返回第一个匹配的注解信息 顺序是:先接口后父类
  • public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType):(其它重载方法略)

@MyAnno
interface Eat {
}
class Parent implements Eat {
}
//本来,接口上的注解我们无论如何都继承不了了,但用了Spring的,你就可以
    public static void main(String[] args) {
        MyAnno annotation = AnnotationUtils.findAnnotation(Child.class, MyAnno.class);
        System.out.println(annotation);
    }
//备注:哪怕@MyAnno上没有标注@Inherited,也是能找出来的(这是后面讲解@RequestMapping为何能被子类继承的重要原因)


  • public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, @Nullable Class<?> clazz):找到第一个(自己有就自己了,否则去父类继续找)有这个注解的。Class~~(有可能是自己,有可能是父类) 备注:接口不找


    public static void main(String[] args) {
        Class<?> annotationDeclaringClass = AnnotationUtils.findAnnotationDeclaringClass(MyAnno.class, Child.class);
        System.out.println(annotationDeclaringClass);
    }


  • public static Class<?> findAnnotationDeclaringClassForTypes(List<Class<? extends Annotation>> annotationTypes, @Nullable Class<?> clazz):annotationTypes和上面相比,只有类里面有一个这个注解,就return了
  • public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz):简单的说就是自己本身Class是否含有指定的这个注解
  • public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz):判断该Class上指定的注解是否是继承过来的。

    public static void main(String[] args) {
        System.out.println(AnnotationUtils.isAnnotationInherited(MyAnno.class, Parent.class)); //false
        // 说明一下:clazz.isAnnotationPresent(annotationType) JDK的。表示注解存在就行,不管你是自己的还是继承来的
        System.out.println(AnnotationUtils.isAnnotationInherited(MyAnno.class, Child.class)); //true 很显然,Child的这个注解是继承来的
    // Child的MyAnno注解是父类的,但这里还是会返回true
        System.out.println(Child.class.isAnnotationPresent(MyAnno.class)); //true
        System.out.println(Child.class.getAnnotation(MyAnno.class)); // @com.fsx.maintest.MyAnno(c...
    }


  • public static boolean isAnnotationMetaPresent(Class<? extends Annotation> annotationType, @Nullable Class<? extends Annotation> metaAnnotationType):简单的说:就是annotationType这个注解类型上面,是否标注有metaAnnotationType这个类型的注解

    public static void main(String[] args) {
        System.out.println(AnnotationUtils.isAnnotationMetaPresent(MyAnno.class, RequestMapping.class)); //true
        System.out.println(AnnotationUtils.isAnnotationMetaPresent(MyAnno.class, Component.class)); //false
    }



相关文章
|
2月前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
125 1
|
2月前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
2月前
|
缓存 Java 数据库连接
Spring Boot奇迹时刻:@PostConstruct注解如何成为应用初始化的关键先生?
【8月更文挑战第29天】作为一名Java开发工程师,我一直对Spring Boot的便捷性和灵活性着迷。本文将深入探讨@PostConstruct注解在Spring Boot中的应用场景,展示其在资源加载、数据初始化及第三方库初始化等方面的作用。
53 0
|
7天前
|
Java Spring 容器
Spring使用异步注解@Async正确姿势
Spring使用异步注解@Async正确姿势,异步任务,spring boot
|
7天前
|
XML Java 数据格式
spring复习03,注解配置管理bean
Spring框架中使用注解配置管理bean的方法,包括常用注解的标识组件、扫描组件、基于注解的自动装配以及使用注解后的注意事项,并提供了一个基于注解自动装配的完整示例。
spring复习03,注解配置管理bean
|
7天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
20天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
2月前
|
Java 数据安全/隐私保护 Spring
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
揭秘Spring Boot自定义注解的魔法:三个实用场景让你的代码更加优雅高效
|
2月前
|
XML Java 数据库
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
这篇文章是Spring5框架的实战教程,详细介绍了事务的概念、ACID特性、事务操作的场景,并通过实际的银行转账示例,演示了Spring框架中声明式事务管理的实现,包括使用注解和XML配置两种方式,以及如何配置事务参数来控制事务的行为。
Spring5入门到实战------15、事务操作---概念--场景---声明式事务管理---事务参数--注解方式---xml方式
|
2月前
|
监控 安全 Java
【开发者必备】Spring Boot中自定义注解与处理器的神奇魔力:一键解锁代码新高度!
【8月更文挑战第29天】本文介绍如何在Spring Boot中利用自定义注解与处理器增强应用功能。通过定义如`@CustomProcessor`注解并结合`BeanPostProcessor`实现特定逻辑处理,如业务逻辑封装、配置管理及元数据分析等,从而提升代码整洁度与可维护性。文章详细展示了从注解定义、处理器编写到实际应用的具体步骤,并提供了实战案例,帮助开发者更好地理解和运用这一强大特性,以实现代码的高效组织与优化。
55 0
下一篇
无影云桌面