【源码】Spring —— ClassMetadata AnnotatedTypeMetadata 解读

前言

ClassMetadata AnnotatedTypeMetadata 可以理解为对 Class元数据Annotation元数据 的抽象

版本

Spring 5.1.x

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() {

			}
		});
	}
}

元数据

元数据:数据的数据。比如Class信息就是一种元数据。

Metadataorg.springframework.core.type包名下,还有用于读取的子包classreading也是重要知识点。此体系大致的类结构列出如下图:

在这里插入图片描述

可以看到顶层接口有两个:ClassMetadata

在这里插入图片描述

AnnotatedTypeMetadata

在这里插入图片描述

元注解

注解上的注解Spring将其定义为元注解(meta-annotation),如 @Component标注在 @Service上,@Component就被称作为元注解。后面我们就将注解的注解称为元注解。

ClassMetadata

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

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

}

StandardClassMetadata

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

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

    @Override
	public boolean isIndependent() { 	// 是否“独立”,TopLevelClass 或者 NestedClass
		return (!hasEnclosingClass() || 
				(this.introspectedClass.getDeclaringClass() != null &&
						Modifier.isStatic(this.introspectedClass.getModifiers())));
	}

	// 略

}

直接注解 & 元注解

方便行文,做一个约定:

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

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

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

AnnotatedTypeMetadata

顶层接口,所有可被注解标注类型元数据的抽象,其实主要提供了两个核心方法:

  1. 根据 全类名 判断是否被指定注解标注
  2. 根据 全类名 返回指定注解的属性集合(包括元注解)
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);

}

MethodMetadata

方法元数据的抽象,同时也继承了 AnnotatedTypeMetadata,毕竟方法上也可以加注解

提供的方法都很眼熟,Method 的相关方法

public interface MethodMetadata extends AnnotatedTypeMetadata {

	String getMethodName();

	String getDeclaringClassName();

	String getReturnTypeName();

	boolean isAbstract();

	boolean isStatic();

	boolean isFinal();

	boolean isOverridable();

}

StandardMethodMetadata

MethodMetadata 的标准实现

  • MethodMetadata 接口的对应方法直接委托 Method,基于 反射 实现
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);

}

AnnotationMetadata

  • AnnotatedTypeMetadata 接口对应方法委托给 AnnotatedElementUtils 实现

注解元数据,同时继承了 ClassMetadataAnnotatedTypeMetadata,毕竟 注解 也是类,同时也可被 注解 标注,它提供的方法总结如下:

  • 返回目标上所有 直接注解 的全类名集合
  • 返回目标指定注解上所有 元注解 的全类名集合
  • 目标是否被指定 直接注解 标注
  • 目标是否被指定 元注解 标注
  • 目标是否含有被 直接注解或元注解 标注的方法
  • 返回上述方法的 MethodMetadata 集合 ??? 没懂什么意思
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);

}

StandardAnnotationMetadata

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

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

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));
	}
   
    @Override
	@SuppressWarnings("deprecation")
	public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
		Set<MethodMetadata> annotatedMethods = null;
		if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
			try {
				Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
				for (Method method : methods) {
					if (isAnnotatedMethod(method, annotationName)) {
						if (annotatedMethods == null) {
							annotatedMethods = new LinkedHashSet<>(4);
						}
						annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
					}
				}
			}
			catch (Throwable ex) {
				throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
			}
		}
		return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
	}
	// 略

}

AnnotationMetadataReadingVisitor

继承自ClassMetadataReadingVisitor,同样的ClassMetadata部分实现交给了它。

说明:ClassMetadataReadingVisitororg.springframework.core.type.classreading包下的类,同包的还有我下面重点讲述的MetadataReader。此实现类最终委托给AnnotationMetadataReadingVisitor来做的,而它便是ClassMetadataReadingVisitor的子类(MetadataReader的底层实现就是它,使用的ASM的ClassVisitor模式读取元数据)。

// @since 2.5
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
	...
}

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

MetadataReader

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

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

此接口是一个访问ClassMetadata等的简单门面,实现是委托给org.springframework.asm.ClassReaderClassVisitor来处理的,它不用Class加载进JVM就可以拿到元数据,因为它读取的是资源:Resource,这是它最大的优势所在。

MetadataReaderFactory

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

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 的相关方法做了演示,因为比较容易混淆

  • ClassMetadata MethodMetadata 相关方法比较简单,不过多演示

总结

学习如上相关主要是 Spring 内部大量使用到了 元数据,比如 ConfigurationClassParser 类对配置类的解析中

当然,在我们日常开发中也可以使用这些类,对元数据的统一封装也是一种很重要框架思维

值得一提的是,Spring 5.2 以后对于上述实现做了调整,修改为基于 MergedAnnotations 实现,提供了更加丰富、灵活的功能

参考

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

【源码】Spring —— ClassMetadata AnnotatedTypeMetadata 解读

posted @ 2021-09-11 10:12  笨拙的小菜鸟  阅读(468)  评论(0编辑  收藏  举报