• 【源码】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 解读

  • 相关阅读:
    servlet(二)
    servlet(一)
    static、final、static final的区别
    java基础知识(一)
    IntelliJ IDEA中Debug的使用技巧
    IDEA从SVN中导入多模块项目
    Java实现MD5加密
    冒泡排序
    ulimit: core file size: cannot modify limit: Operation not permitted
    Linux 禁用摄像头
  • 原文地址:https://www.cnblogs.com/tangliMeiMei/p/15253931.html
Copyright © 2020-2023  润新知