类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)

Posted 恒奇恒毅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)相关的知识,希望对你有一定的参考价值。

参考:
https://blog.csdn.net/weixin_42189048/article/details/108288213

Spring元数据Metadata的使用,注解编程之AnnotationMetadata,ClassMetadata、MetadataReaderFactory

前言
AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata可以理解为对 Class元数据 和 Annotation元数据 和方法的抽象

他们之间的关系为

Class

Class 有如下形式:

  • Top Level Class:顶层类,即普通类
  • Inner Class:非静态内部类
  • Nested Class:嵌套类(静态内部类)
  • Local Class:方法内声明的局部类
  • Anonymous Class:匿名类

以下 demo 演示这几种类的关系

// TopLevelClass
public class TopLevelClass 

    // InnerClass
    class InnerClass 
    
    
    
    // NestedClass
    static class NestedClass 
    
    
    
    public void a() 
    
    	// LocalClass
    	class LocalClass 
    
    	
    
    	// Anonymous classes
    	new Thread(new Runnable() 
    		@Override
    		public void run() 
    
    		
    	);
    



ClassMetadata

public interface ClassMetadata 

    String getClassName();
    
    boolean isInterface();
    
    boolean isAnnotation();
    
    boolean isAbstract();
    
    // 是否一个具体的类,即不是接口或者抽象类,换句话说,可 new
    boolean isConcrete();
    
    boolean isFinal();
    
    // 是否“独立”,TopLevelClass 或者 NestedClass
    boolean isIndependent();
    
    // 是否含有 InnerClass | NestedClass | LocalClass
    boolean hasEnclosingClass();
    
    @Nullable
    String getEnclosingClassName();
    
    boolean hasSuperClass();
    
    @Nullable
    String getSuperClassName();
    
    String[] getInterfaceNames();
    
    // 返回所有(继承、实现)该类的 成员类(内部类、接口除外)
    String[] getMemberClassNames();



Class 元数据的抽象,方法都很眼熟

StandardClassMetadata

public class StandardClassMetadata implements ClassMetadata 

    private final Class<?> introspectedClass;
    
    public StandardClassMetadata(Class<?> introspectedClass) 
    	Assert.notNull(introspectedClass, "Class must not be null");
    	this.introspectedClass = introspectedClass;
    
    
    public final Class<?> getIntrospectedClass() 
    	return this.introspectedClass;
    
    
    @Override
    public String getClassName() 
    	return this.introspectedClass.getName();
    
    
    @Override
    public boolean isInterface() 
    	return this.introspectedClass.isInterface();
    
    
    @Override
    public boolean isAnnotation() 
    	return this.introspectedClass.isAnnotation();
    
    
    // 略



ClassMetadata 的标准实现,方法其实都直接委托给了 Class类,基于 反射 实现

直接注解 & 元注解

方便行文,做一个约定:

  • 直接注解:就是元素被指定注解直接标注,比如 @Service 直接标注在对应类上
  • 元注解:元注解可以标注在注解上,比如 @Component 标注在 @Service 上,理解为 @Component 就是对应类的 元注解

AnnotatedTypeMetadata

public interface AnnotatedTypeMetadata 

    // 根据“全类名”判断是否被指定 直接注解或元注解 标注
    boolean isAnnotated(String annotationName);
    
    // 根据”全类名“获取所有注解属性(包括元注解)
    @Nullable
    Map<String, Object> getAnnotationAttributes(String annotationName);
    
    @Nullable
    // 同上,但是第二个参数传 true 时会把属性中对应值为 Class 的值
    // 转为 字符串,避免需要预先加载对应 Class
    Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString);
    
    @Nullable
    // 同上,MultiValueMap 是一个 key 可以对应多个 value 的变种 map
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName);
    @Nullable
    MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);



顶层接口,可被注解标注类型(类、方法)元数据的抽象,提供了两个核心方法:

  • 根据 全类名 判断是否被指定注解标注
  • 根据 全类名 返回指定注解的属性集合(包括元注解)

AnnotationMetadata

public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata 

    // 返回目标所有 直接注解 全限定名的集合
    Set<String> getAnnotationTypes();
    
    // 返回目标指定注解上 元注解 的全限定名集合
    Set<String> getMetaAnnotationTypes(String annotationName);
    
    // 是否被指定 直接注解 标注
    boolean hasAnnotation(String annotationName);
    
    // 是否被 指定元注解 标注
    boolean hasMetaAnnotation(String metaAnnotationName);
    
    // 是否存在被指定 直接注解或元注解 标注的方法
    boolean hasAnnotatedMethods(String annotationName);
    
    // 返回上述方法的 MethodMetadata 集合
    Set<MethodMetadata> getAnnotatedMethods(String annotationName);



注解元数据,同时继承了 ClassMetadataAnnotatedTypeMetadata,还拓展了以下方法:

  • 返回目标上所有 直接注解 的全类名集合
  • 返回目标指定注解上所有 元注解 的全类名集合
  • 目标是否被指定 直接注解 标注
  • 目标是否被指定 元注解 标注
  • 目标是否含有被 直接注解或元注解 标注的方法
  • 返回上述方法的 MethodMetadata 集合
StandardAnnotationMetadata

public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata 

    private final Annotation[] annotations;
    
    private final boolean nestedAnnotationsAsMap;
    
    public StandardAnnotationMetadata(Class<?> introspectedClass) 
    	this(introspectedClass, false);
    
    
    public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) 
    	super(introspectedClass);
    	this.annotations = introspectedClass.getAnnotations();
    	this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
    
    
    @Override
    public Set<String> getAnnotationTypes() 
    	Set<String> types = new LinkedHashSet<>();
    	for (Annotation ann : this.annotations) 
    		types.add(ann.annotationType().getName());
    	
    	return types;
    
    
    @Override
    public Set<String> getMetaAnnotationTypes(String annotationName) 
    	return (this.annotations.length > 0 ?
    			AnnotatedElementUtils.getMetaAnnotationTypes(getIntrospectedClass(), annotationName) :
    			Collections.emptySet());
    
    
    @Override
    public boolean hasAnnotation(String annotationName) 
    	for (Annotation ann : this.annotations) 
    		if (ann.annotationType().getName().equals(annotationName)) 
    			return true;
    		
    	
    	return false;
    
    
    @Override
    public boolean hasMetaAnnotation(String annotationName) 
    	return (this.annotations.length > 0 &&
    			AnnotatedElementUtils.hasMetaAnnotationTypes(getIntrospectedClass(), annotationName));
    
    
    @Override
    public boolean isAnnotated(String annotationName) 
    	return (this.annotations.length > 0 &&
    			AnnotatedElementUtils.isAnnotated(getIntrospectedClass(), annotationName));
    
    
    // 略



AnnotationMetadata 的标准实现,同时也继承了 StandardClassMetadata,所以针对 ClassMetadata 方法的实现则由 StandardClassMetadata 来完成,同样 AnnotationMetadata 相关方法委托 AnnotatedElementUtils 实现。

这种接口和类的设计模式,在 Spring 很常用,值得学习

MethodMetadata

public interface MethodMetadata extends AnnotatedTypeMetadata 

    String getMethodName();
    
    String getDeclaringClassName();
    
    String getReturnTypeName();
    
    boolean isAbstract();
    
    boolean isStatic();
    
    boolean isFinal();
    
    boolean isOverridable();



方法元数据的抽象,提供 Method 的相关方法继承 AnnotatedTypeMetadata,因为方法上也能加注解,通过该接口拓展对方法上注解的元数据信息进行访问

StandardMethodMetadata

public class StandardMethodMetadata implements MethodMetadata 

    private final Method introspectedMethod;
    
    private final boolean nestedAnnotationsAsMap;
    
    public StandardMethodMetadata(Method introspectedMethod) 
    	this(introspectedMethod, false);
    
    
    public StandardMethodMetadata(Method introspectedMethod, boolean nestedAnnotationsAsMap) 
    	Assert.notNull(introspectedMethod, "Method must not be null");
    	this.introspectedMethod = introspectedMethod;
    	this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
    
    
    public final Method getIntrospectedMethod() 
    	return this.introspectedMethod;
    
    
    @Override
    public String getMethodName() 
    	return this.introspectedMethod.getName();
    
    
    @Override
    public String getDeclaringClassName() 
    	return this.introspectedMethod.getDeclaringClass().getName();
    
    
    // ...
    
    @Override
    @Nullable
    public Map<String, Object> getAnnotationAttributes(String annotationName) 
    	return getAnnotationAttributes(annotationName, false);
    
    
    @Override
    @Nullable
    public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) 
    	return AnnotatedElementUtils.getMergedAnnotationAttributes(this.introspectedMethod,
    			annotationName, classValuesAsString, this.nestedAnnotationsAsMap);
    
    
    @Override
    @Nullable
    public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) 
    	return getAllAnnotationAttributes(annotationName, false);
    
    
    // ...



MethodMetadata 的标准实现

MethodMetadata 接口的对应方法直接委托 Method,基于反射实现,AnnotatedTypeMetadata 接口对应方法委托给 AnnotatedElementUtils 实现

MetadataReader

上述所有实现,都是委托对应元素直接基于 反射 实现的,因此前提是对应的 Class 必须加载到 JVM 中,而实际的应用场景,并不一定保证对应的 Class 已加载,比如 Spring 的第三方类扫描

因此,MetadataReader 接口抽象元数据的读取,其实现基于 ASM 直接扫描对应文件字节码实现,Spring 提供了唯一实现 SimpleMetadataReader

MetadataReaderFactory

对于 MetadataReader,Spring 也提供了对应的 工厂类 去获取,顶层接口 MetadataReaderFactory,类图如下

SimpleMetadataReaderFactory 返回对应的 SimpleMetadataReader
CachingMetadataReaderFactory 基于 SimpleMetadataReaderFactory 做了缓存,功能更强大
借助它们,我们就可以获取对应的 MetadataReader,同样也可以获取对应的元数据
示例

public class TestMain 

    @Service
    @Configuration
    public class Config 
    
        @RequestMapping
        public void a() 
    
        
    
    
    
    @Test
    public void testReflect() throws IOException 
    
        String component = "org.springframework.stereotype.Component";
        String configuration = "org.springframework.context.annotation.Configuration";
    
        // 基于反射获取
        /*StandardAnnotationMetadata metadata
                = (StandardAnnotationMetadata) AnnotationMetadata.introspect(Config.class);*/
    
        // 基于 MetadataReader 获取
        MetadataReaderFactory factory = new CachingMetadataReaderFactory();
        MetadataReader metadataReader = factory.getMetadataReader("com.example.demo.metadata.TestMain.Config");
        AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
    
        // -------------- AnnotatedTypeMetadata --------
        boolean annotated = metadata.isAnnotated(component);
        System.out.println("是否被指定 直接注解或元注解 标注:" + annotated);
    
        Map<String, Object> annotationAttributes
                = metadata.getAnnotationAttributes(component, true);
        System.out.println("指定 直接注解或元注解 的所有属性:");
        annotationAttributes.forEach((k, v) -> System.out.println(k +":"+ v));
    
        // ------------- AnnotationMetadata
        Set<String> annotationTypes = metadata.getAnnotationTypes();
        System.out.println("目标类上标注的 直接注解 有:");
        annotationTypes.forEach(System.out::println);
    
        Set<String> metaAnnotationTypes
                = metadata.getMetaAnnotationTypes(configuration);
        System.out.println("目标指定注解上的 元注解 有:");
        metaAnnotationTypes.forEach(System.out::println);
    
        boolean b = metadata.hasAnnotation(configuration);
        System.out.println("目标是否被指定 直接注解 标注:" + b);
    
        boolean b1 = metadata.hasMetaAnnotation(component);
        System.out.println("目标是否被指定 元注解 标注:" + b1);
    
        Set<MethodMetadata> annotatedMethods
                = metadata.getAnnotatedMethods("org.springframework.web.bind.annotation.Mapping");
        if (annotatedMethods != null && annotatedMethods.size() > 0) 
            System.out.print("目标类含有被指定 直接注解或元注解 标注的方法,其名称有:");
    
            for (MethodMetadata m : annotatedMethods) 
                System.out.println(m.getMethodName());
            
        
    



如上示例,做个概括:

StandardAnnotationMetadata 有两种方式获取,基于 反射 或基于 MetadataReader
分别对 AnnotatedTypeMetadataAnnotationMetadata 的相关方法做了演示,因为比较容易混淆
ClassMetadataMethodMetadata 相关方法比较简单,不过多演示

以上是关于类和方法元信息注解信息体系(AnnotatedTypeMetadataAnnotationMetadataClassMetadataMethodMetadata)的主要内容,如果未能解决你的问题,请参考以下文章

注解——体系篇

Java_枚举类和注解

Java 注解

Java注解

回顾一下Java注解

注解