1 Star 0 Fork 156

祝少云 / Code-Analysis

forked from huifer / Code-Analysis 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
第二十六章-注解元数据.md 10.99 KB
一键复制 编辑 原始数据 按行查看 历史
huifer 提交于 2021-02-18 16:46 . fix: 修改图片地址

第二十六章 注解元数据

本章笔者将围绕 org.springframework.core.type.AnnotationMetadata 接口和各位读者一起探讨它做了什么。

26.1 基础认识

注解元数据的接口是 AnnotationMetadata ,我们先来看这个接口的类图

  • AnnotationMetadata 类图

AnnotationMetadata

从这个类图中我们可以看到注解元数据由三个接口提供,对于 ClassMetadata 中定义了那些方法各位可以翻阅第二十五章,下面我们来看其他两个接口的方法

26.2 AnnotatedTypeMetadata 接口说明

  • AnnotatedTypeMetadata 详细信息
public interface AnnotatedTypeMetadata {

    /**
     * 获取所有注解
     */
    MergedAnnotations getAnnotations();

    /**
     * 是否有注解, 是否被参数注解修饰
     */
    default boolean isAnnotated(String annotationName) {
        return getAnnotations().isPresent(annotationName);
    }

    /**
     * 获取注解的属性
     */
    @Nullable
    default Map<String, Object> getAnnotationAttributes(String annotationName) {
        return getAnnotationAttributes(annotationName, false);
    }

    /**
     * 获取注解属性表
     */
    @Nullable
    default Map<String, Object> getAnnotationAttributes(String annotationName,
            boolean classValuesAsString) {

        MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName,
                null, MergedAnnotationSelectors.firstDirectlyDeclared());
        if (!annotation.isPresent()) {
            return null;
        }
        return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
    }

    /**
     * 获取注解的属性表
     */
    @Nullable
    default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
        return getAllAnnotationAttributes(annotationName, false);
    }

    /**
     * 获取注解属性表
     */
    @Nullable
    default MultiValueMap<String, Object> getAllAnnotationAttributes(
            String annotationName, boolean classValuesAsString) {

        Adapt[] adaptations = Adapt.values(classValuesAsString, true);
        return getAnnotations().stream(annotationName)
                .filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes))
                .map(MergedAnnotation::withNonMergedAttributes)
                .collect(MergedAnnotationCollectors.toMultiValueMap(map ->
                        map.isEmpty() ? null : map, adaptations));
    }

}
方法名称 方法返回值 方法参数 方法作用
getAnnotations MergedAnnotations 提取注解集合
isAnnotated boolean annotationName:注解名称 判断是否存在该注解
getAnnotationAttributes Map<String, Object> annotationName:注解名称 获取指定注解的属性表

26.3 AnnotationMetadata 接口说明

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {

   /**
     * 获取注解名称,全类名
    */
   default Set<String> getAnnotationTypes() {
      return getAnnotations().stream()
            // 判断是否直接使用注解
            .filter(MergedAnnotation::isDirectlyPresent)
            // 获取注解的名字
            .map(annotation -> annotation.getType().getName())
            // 转换成set
            .collect(Collectors.toCollection(LinkedHashSet::new));
   }

   /**
     * 注解全类名
    */
   default Set<String> getMetaAnnotationTypes(String annotationName) {
      // 获取注解合并后的结果
      MergedAnnotation<?> annotation = getAnnotations().get(annotationName, MergedAnnotation::isDirectlyPresent);
      // 判断注解是否使用
      if (!annotation.isPresent()) {
         // 不使用直接返回空
         return Collections.emptySet();
      }
      // 注解使用 继承查找获得所有注解名称(类名)
      return MergedAnnotations.from(annotation.getType(), SearchStrategy.INHERITED_ANNOTATIONS).stream()
            .map(mergedAnnotation -> mergedAnnotation.getType().getName())
            .collect(Collectors.toCollection(LinkedHashSet::new));
   }

   /**
     * 是否包含某个注解
    */
   default boolean hasAnnotation(String annotationName) {
      return getAnnotations().isDirectlyPresent(annotationName);
   }

   /**
     * 是否被某个注解标记过
    */
   default boolean hasMetaAnnotation(String metaAnnotationName) {
      return getAnnotations().get(metaAnnotationName,
            MergedAnnotation::isMetaPresent).isPresent();
   }

   /**
     * 是否有注解,类里面有一个注解就返回true
    */
   default boolean hasAnnotatedMethods(String annotationName) {
      return !getAnnotatedMethods(annotationName).isEmpty();
   }

   /**
     * 获取包含注解的方法
    */
   Set<MethodMetadata> getAnnotatedMethods(String annotationName);


   /**
     * 通过反射创建一个注解的元信息
    */
   static AnnotationMetadata introspect(Class<?> type) {
      return StandardAnnotationMetadata.from(type);
   }

}
方法名称 方法返回值 方法说明
getAnnotationTypes Set<String> 获取注解类名列表
getMetaAnnotationTypes Set<String> 获取一个类型的注解名称列表
hasAnnotation boolean 是否存直接存在某个注解
hasMetaAnnotation boolean 是否存在某个注解
hasAnnotatedMethods boolean 是否存在某个注解
getAnnotatedMethods Set<MethodMetadata> 获取注解属性
introspect AnnotationMetadata 构造器提供AnnotationMetadata初始化方法

这里我们需要对直接存在某个注解和是否存在某个注解做出解释。下面笔者将使用 Component 注解和 Configuration 来做出演示

我们可以定义下面两个类

@Component
public class A {

}

@Configuration
public class C {

}

之后我们来编写测试代码

@Test
void testAnn() {
   AnnotationMetadata introspect = AnnotationMetadata.introspect(C.class);
   System.out.println(introspect.hasAnnotation(Component.class.getName()));
   System.out.println(introspect.hasMetaAnnotation(Component.class.getName()));
}

这个测试代码会输出

false
true

Component 注解是间接被引用的,它在 Configuration 上被使用了具体代码如下

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {}

通过这样一个例子我们来对 hasAnnotation 方法和 hasMetaAnnotation 方法的差异做出了解。

26.4 StandardAnnotationMetadata 分析

我们在 AnnotationMetadata 中可以找到 introspect 方法,在这个方法中指向了一个类,这个类提供了注解元数据的处理能力。

先来看这个类的成员变量

变量名称 变量类型 变量说明
mergedAnnotations MergedAnnotations 合并的注解属性
nestedAnnotationsAsMap boolean 是否需要将注解属性做成嵌套的map对象
annotationTypes Set<String> 注解类型列表

下面我们来看它的构造方法

@Deprecated
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
   super(introspectedClass);
   this.mergedAnnotations = MergedAnnotations.from(introspectedClass,
         SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none(),
         AnnotationFilter.NONE);
   this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}
  • mergedAnnotations 数据信息

image-20210210123407696

在这个成员变量中得到的 mergedAnnotations 数据将为后续的注解方法使用提供基础数据内容。下面我们来看一个提取注解属性的操作,具体代码如下。

  • 提取注解属性的代码
@Nullable
default Map<String, Object> getAnnotationAttributes(String annotationName,
        boolean classValuesAsString) {

    MergedAnnotation<Annotation> annotation = getAnnotations().get(annotationName,
            null, MergedAnnotationSelectors.firstDirectlyDeclared());
    if (!annotation.isPresent()) {
        return null;
    }
    return annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
}

在这个方法中出现的操作如下

  1. 提取 mergedAnnotations 中的指定注解的注解信息
  2. 将注解信息转换成 Map 对象
  • 测试用的注解
@ComponentScans(
    value =  {
        @ComponentScan()
    }
)
  • 提取到的数据

image-20210210124353277

在这个处理过程中我们的本质是为了得到注解的数据,本文先不讨论 Spring 中的处理方式,我们先来看 Java 中如何得到这样的一个数据

26.5 java 中注解数据获取

下面我们以 Component 为例来制作注解的数据解析方法

第一步创建一个对象

@Component(value = "abcd")
public class A {

}

第二步编写逻辑代码

@Test
void annAttribute() throws InvocationTargetException, IllegalAccessException {
   A a = new A();
   Class<? extends A> aClass = a.getClass();
   Annotation[] annotations = aClass.getAnnotations();

   Map<String, Map<String, Object>> annotationAttributes = new HashMap<>();

   for (Annotation annotation : annotations) {
      Class<? extends Annotation> annClass = annotation.annotationType();

      String name = annClass.getName();
      Method[] declaredMethods = annClass.getDeclaredMethods();

      Map<String, Object> oneAnnotationAttributes = new HashMap<>();

      for (Method declaredMethod : declaredMethods) {
         String annAtrrName = declaredMethod.getName();
         Object invoke = declaredMethod.invoke(annotation);
         oneAnnotationAttributes.put(annAtrrName, invoke);

      }
      annotationAttributes.put(name, oneAnnotationAttributes);
   }

   System.out.println();
}

逻辑代码编写完成后我们来看看 annotationAttributes 变量中存储了什么内容

image-20210210131005983

annotationAttributes 是一个Map结构

  1. 第一层

    key:主界名称

    value:注解数据

  2. 第二层

    key:注解属性名称

    value:注解属性值

Java
1
https://gitee.com/zhushaoyun/code-analysis.git
git@gitee.com:zhushaoyun/code-analysis.git
zhushaoyun
code-analysis
Code-Analysis
master

搜索帮助

53164aa7 5694891 3bd8fe86 5694891