【小家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
    }



相关文章
|
13天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
140 73
|
8天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
40 21
|
13天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
13天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
2月前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
49 4
|
2月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
152 2
|
2月前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
63 2
|
2月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
45 2
|
2月前
|
JSON Java 数据格式
springboot常用注解
@RestController :修饰类,该控制器会返回Json数据 @RequestMapping(“/path”) :修饰类,该控制器的请求路径 @Autowired : 修饰属性,按照类型进行依赖注入 @PathVariable : 修饰参数,将路径值映射到参数上 @ResponseBody :修饰方法,该方法会返回Json数据 @RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中 @Controller@Service@Compont: 将类注册到ioc容器
|
2月前
|
前端开发 Java 开发者
Spring MVC中的控制器:@Controller注解全解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序控制层的核心。它不仅简化了控制器的定义,还提供了灵活的请求映射和处理机制。本文将深入探讨`@Controller`注解的用法、特点以及在实际开发中的应用。
113 0