元数据


元数据:数据的数据。比如Class就是一种元数据。Metadata在org.springframework.core.type包名下,还有用于读取的子包classreading也是重要知识点。此体系大致的类结构列出如下图:


元数据 - 图1

元数据 - 图2
元数据 - 图3



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







(一)Metadata






metata在org.springframework.core.type包名下,主要有以下相关接口和类

元数据 - 图4

1.案例

案例:ZJJ_Spring_2020/05/20_11:27:04_iii6o



2.ClassMetadata接口


基本是Class相关的一致的,所以ClassMetadata实质是对Class的一种抽象和适配.

来看一下ClassMetadata的接口

// @since 2.5
public interface ClassMetadata {

// 返回类名(注意返回的是最原始的那个className)
String getClassName();
boolean isInterface();
// 是否是注解
boolean isAnnotation();
boolean isAbstract();
// 是否允许创建 不是接口且不是抽象类 这里就返回true了
boolean isConcrete();
boolean isFinal();
// 是否是独立的(能够创建对象的) 比如是Class、或者内部类、静态内部类
boolean isIndependent();
// 是否有内部类之类的东东
boolean hasEnclosingClass();
@Nullable
String getEnclosingClassName();
boolean hasSuperClass();
@Nullable
String getSuperClassName();
// 会把实现的所有接口名称都返回 具体依赖于Class#getSuperclass
String[] getInterfaceNames();

// 基于:Class#getDeclaredClasses 返回类中定义的公共、私有、保护的内部类
String[] getMemberClassNames();
}


继承图
元数据 - 图5

StandardClassMetadata


ClassMetadata的实现类StandardClassMetadata,构造函数中传入的参数是Class,方法基本是对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 boolean isInterface() {
return this.introspectedClass.isInterface();
}
@Override
public String[] getMemberClassNames() {
LinkedHashSet memberClassNames = new LinkedHashSet<>(4);
for (Class<?> nestedClass : this.introspectedClass.getDeclaredClasses()) {
memberClassNames.add(nestedClass.getName());
}
return StringUtils.toStringArray(memberClassNames);
}
}


3.MethodsMetadata接口

用来描述java.lang.reflect.Method。
它是子接口,主要增加了从Class里获取到MethodMetadata们的方法:

// @since 2.1 可以看到它出现得更早一些
public interface MethodsMetadata extends ClassMetadata {
// 返回该class所有的方法
Set getMethods();
// 方法指定方法名的方法们(因为有重载嘛~)
Set getMethods(String name);
}


名字上请不要和MethodMetadata搞混了,MethodMetadata是AnnotatedTypeMetadata的子接口,代表具体某一个Type(方法上的注解);而此类是个ClassMetadata,它能获取到本类里所有的方法Method(MethodMetadata)~



4.AnnotatedTypeMetadata

对注解元素的封装适配
什么叫注解元素(AnnotatedElement)?比如我们常见的Class、Method、Constructor、Parameter等等都属于它的子类都属于注解元素。简单理解:只要能在上面标注注解都属于这种元素。Spring4.0新增的这个接口提供了对注解统一的、便捷的访问,使用起来更加的方便高效了。

// @since 4.0
public interface AnnotatedTypeMetadata {

// 此元素是否标注有此注解~~~~
// annotationName:注解全类名
boolean isAnnotated(String annotationName);

// 这个就厉害了:取得指定类型注解的所有的属性 - 值(k-v)
// annotationName:注解全类名
// classValuesAsString:若是true表示 Class用它的字符串的全类名来表示。这样可以避免Class被提前加载
@Nullable
Map getAnnotationAttributes(String annotationName);
@Nullable
Map getAnnotationAttributes(String annotationName, boolean classValuesAsString);

// 参见这个方法的含义:AnnotatedElementUtils.getAllAnnotationAttributes
@Nullable
MultiValueMap getAllAnnotationAttributes(String annotationName);
@Nullable
MultiValueMap getAllAnnotationAttributes(String annotationName, boolean classValuesAsString);
}



它的继承树如下:
元数据 - 图6

两个子接口相应的都提供了标准实现以及基于ASM的Visitor模式实现。


ASM 是一个通用的 Java 字节码操作和分析框架。它可以用于修改现有类或直接以二进制形式动态生成类。 ASM 虽然提供与其他 Java 字节码框架如 Javassist,CGLIB类似的功能,但是其设计与实现小而快,且性能足够高。
Spring 直接将 ASM 框架核心源码内嵌于 Spring-core中,目前`Spring 5.1使用ASM 7 版本。

StandardMethodMetadata是基于反射的标准实现.
MethodMetadataReadingVisitor是基于ASM的实现的,继承自ASM``的org.springframework.asm.MethodVisitor采用Visitor的方式读取到元数据。



5.AnnotationMetadata

这是理解Spring注解编程的必备知识,它是ClassMetadata和AnnotatedTypeMetadata的子接口,具有两者共同能力,并且新增了访问注解的相关方法。可以简单理解为它是对注解的抽象。


经常这么使用得到注解里面所有的属性值:
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annoMetadata, annType);

// @since 2.5
public interface AnnotationMetadata extends ClassMetadata, AnnotatedTypeMetadata {

//拿到当前类上所有的注解的全类名(注意是全类名)
Set getAnnotationTypes();
// 拿到指定的注解类型
//annotationName:注解类型的全类名
Set getMetaAnnotationTypes(String annotationName);

// 是否包含指定注解 (annotationName:全类名)
boolean hasAnnotation(String annotationName);
//这个厉害了,用于判断注解类型自己是否被某个元注解类型所标注
//依赖于AnnotatedElementUtils#hasMetaAnnotationTypes
boolean hasMetaAnnotation(String metaAnnotationName);

// 类里面只有有一个方法标注有指定注解,就返回true
//getDeclaredMethods获得所有方法, AnnotatedElementUtils.isAnnotated是否标注有指定注解
boolean hasAnnotatedMethods(String annotationName);
// 返回所有的标注有指定注解的方法元信息。注意返回的是MethodMetadata 原理基本同上
Set getAnnotatedMethods(String annotationName);
}


StandardAnnotationMetadata

继承了StandardClassMetadata,很明显关于ClassMetadata的实现部分就交给此父类了,自己只关注于AnnotationMetadata接口的实现。

// @since 2.5
public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {

// 很显然它是基于标准反射类型:java.lang.annotation.Annotation
// this.annotations = introspectedClass.getAnnotations()
private final Annotation[] annotations;
private final boolean nestedAnnotationsAsMap;



// 获取本Class类上的注解的元注解们
@Override
public Set 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;
}

}


6.AnnotationMetadataReadingVisitor


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

(二)MetadataReader



你是否有疑问:为何Spring要提供一个标准实现和一个ASM的实现呢?这里就能给你答案。
此接口是一个访问ClassMetadata等的简单门面,实现是委托给org.springframework.asm.ClassReader、ClassVisitor来处理的,它不用把Class加载进JVM就可以拿到元数据,因为它读取的是资源:Resource,这是它最大的优势所在。

public interface MetadataReader {

Resource getResource();

ClassMetadata getClassMetadata();

AnnotationMetadata getAnnotationMetadata();
}


它的继承树如下:
元数据 - 图7

1.SimpleMetadataReader

它是基于ASM的org.springframework.asm.ClassReader的简单实现。请注意:此类是非public的,而是default包访问权限。

final class SimpleMetadataReader implements MetadataReader {
private final Resource resource;
private final ClassMetadata classMetadata;
private final AnnotationMetadata annotationMetadata;


SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
InputStream is = new BufferedInputStream(resource.getInputStream());
ClassReader classReader;
try {
classReader = new ClassReader(is);
} catch (IllegalArgumentException ex) {
throw new NestedIOException(“ASM ClassReader failed to parse class file - “ + “probably due to a new Java class file version that isn’t supported yet: “ + resource, ex);
} finally {
is.close();
}



AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
classReader.accept(visitor, ClassReader.SKIP_DEBUG);

this.annotationMetadata = visitor;

this.classMetadata = visitor;
this.resource = resource;
}

}


2.MethodsMetadataReader


子接口,新增接口方法:获取到本类所有的方法元数据们.

public interface MethodsMetadataReader extends MetadataReader {
MethodsMetadata getMethodsMetadata();

}


它所有的实现都是委托给静态内部类MethodsMetadataReadingVisitor去做的,它继承自上面的AnnotationMetadataReadingVisitor并且实现了接口MethodsMetadata的相关方法。
它的唯一实现类DefaultMethodsMetadataReader的访问权限也是包级别非public,略。

(三)MetadataReaderFactory


MetadataReader的实现都并未public暴露出来,所以我们若想得到它的实例,就只能通过此工厂。

public interface MetadataReaderFactory {

MetadataReader getMetadataReader(String className) throws IOException;
MetadataReader getMetadataReader(Resource resource) throws IOException;
}



继承树如下:元数据 - 图8

1.SimpleMetadataReaderFactory


利用ResourceLoader的简单实现,加载进资源后,new SimpleMetadataReader(resource)交给此实例分析即可。

public class SimpleMetadataReaderFactory implements MetadataReaderFactory {


private final ResourceLoader resourceLoader;


@Override
public MetadataReader getMetadataReader(String className) throws IOException {
try {

String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
Resource resource = this.resourceLoader.getResource(resourcePath);
return getMetadataReader(resource);
} catch (FileNotFoundException ex) {



}
}


@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}
}


此工厂生产的是SimpleMetadataReader。



2.CachingMetadataReaderFactory


它继承自SimpleMetadataReaderFactory,没有其它特殊的,就是提供了缓存能力private Map metadataReaderCache,提高访问效率。
因为有了它,所以SimpleMetadataReaderFactory就不需要被直接使用了,用它代替。Spring内自然也使用的便是效率更高的它喽~

因为MetadataReader的实现类都是包级别的访问权限,所以它的实例只能来自工厂

案例:ZJJ_Spring_2020/05/20_12:24:14_nsife

3.MethodsMetadataReaderFactory


它继承自SimpleMetadataReaderFactory,唯一区别是它生产的是一个MethodsMetadataReader(DefaultMethodsMetadataReader),从而具有了读取MethodsMetadata的能力。
此类可认为从没有被Spring内部使用过,暂且可忽略(spring-data工程有用)
Factory工厂的实现都是非常简单的,毕竟只是为了生产一个实例而已。