【源码】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() {

			}
		});
	}
}

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

}

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

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

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 实现

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 很常用,值得学习

MetadataReader

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

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

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】

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值