自学内容网 自学内容网

spring 中的字节码文件访问 -- classreading 包

位于 spring-core 模块下的 org.springframework.core.type.classreading 包提供了读取类中元数据的功能。其实就是在不加载类的情况下,获取 class 文件中定义的类的相关信息:类名、接口、注解、方法及其注解、字段及其注解等。方便 spring 进行类型或指定注解的判断,对符合条件的类才进行加载并进行实例化对象的创建。

下面我们先来看看这个 classreading 包下都有哪些类。

主要类介绍

为方便使用,自 spring 5.2 版本后,废弃了 classreading 包下不少操作类。今天要介绍的这些类都是 classreading 包下正在使用的类,不包含废弃的类。

MetadataReaderFactory

这是一个接口,也是 spring 中获取 MetadataReader 操作的入口,主要功能就是创建、获取 MetadataReader 实例对象。

MetadataReader getMetadataReader(String className) throws IOException;

MetadataReader getMetadataReader(Resource resource) throws IOException;

实现类有两个:

  • SimpleMetadataReaderFactory,MetadataReaderFactory 接口的主要实现类,MetadataReader 的创建就在此完成。
  • CachingMetadataReaderFactory,继承 SimpleMetadataReaderFactory, 扩展了缓存创建的 MetadataReader 的功能。
@Override
public MetadataReader getMetadataReader(Resource resource) throws IOException {
return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader());
}

这是创建 MetadataReader 的入口。当传参为 String 类型的 className 时,通过创建 SimpleMetadataReaderFactory 时传入的 resourceLoader,调用 getResource 方法得到 Resource 后,再调用上面的 getMetadataReader 方法创建 SimpleMetadataReader 实例对象。

MetadataReader

这是一个接口,提供了对获取 class 文件元数据信息的简单封装。

Resource getResource();

ClassMetadata getClassMetadata();

AnnotationMetadata getAnnotationMetadata();

目前实现类只有一个,SimpleMetadataReader,基于 ASM 中的 ClassReader 来实现 class 文件的读取功能,传入的 ClassVisit 为 SimpleAnnotationMetadataReadingVisitor。并且在 SimpleMetadataReader 中实现了对 getClassMetatdata 和 getAnnotationMetadata 的统一,返回的都是 SimpleAnnotationMetadataReadingVisitor 中生成的 SimpleAnnotationMetadata。

SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
// 引入 ClassVisit
SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
// 利用 ClassReader 接收 ClassVisit 访问这个类
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
this.resource = resource;
this.annotationMetadata = visitor.getMetadata();
}

class 文件访问

可以看到,是通过 SimpleAnnotationMetadataReadingVisitor 来对 class 文件进行访问的,SimpleAnnotationMetadataReadingVisitor 继承 ASM 中的 ClassVisit,通过重写 ClassVisit 中的相关方法,实现对 class 文件中类的元数据获取。主要关注以下几个重写的方法:

  • visit: 访问 className,access,superClassName,interfaceNames
  • visitAnnotation: 注解的访问,委托给 MergedAnnotationReadingVisitor 来处理
  • visitMethod: 方法的访问,委托给 SimpleMethodMetadataReadingVisitor 来处理
  • visitEnd: 封装 SimpleAnnotationMetadata,赋值给 metadata 属性            

其中,不管是类上面的注解,还是方法中的注解,最终都是通过 MergedAnnotationReadingVisitor 来访问的;不管是接口还是类,一个方法对应一个 SimpleMethodMetadataReadingVisit 实例对象。

下面我们来看下具体的访问过程:

在 ClassReader 中定义了读取 class 文件中类相关信息的操作流程和方法,之后通过 ClassVisit 进行访问。ClassVisit 是一个抽象类,也就是说,具体的访问操作,由用户自行指定。而 ClassReader 是固定的,按照虚拟机规范中 class 的文件结构,对 class 文件中的各个部分进行读取。

visit

@Override
public void visit(int version, int access, String name, String signature,
@Nullable String supername, String[] interfaces) {

this.className = toClassName(name);
this.access = access;
if (supername != null && !isInterface(access)) {
this.superClassName = toClassName(supername);
}
this.interfaceNames = new String[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
this.interfaceNames[i] = toClassName(interfaces[i]);
}
}

此方法的主要作用,就是将 ClassReader 读取的类相关信息,赋值给 SimpleAnnotationMetadataReadingVisitor 中的相关变量。

visitAnnotation

如果 class 文件定义的类上有注解,会调用此方法:

@Override
@Nullable
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return MergedAnnotationReadingVisitor.get(this.classLoader, getSource(),
descriptor, visible, this.annotations::add);
}

可以看到,注解的访问委托给了 MergedAnnotationReadingVisitor 去操作。

@Nullable
static <A extends Annotation> AnnotationVisitor get(@Nullable ClassLoader classLoader,
@Nullable Object source, String descriptor, boolean visible,
Consumer<MergedAnnotation<A>> consumer) {
// 不可见的,直接返回 null
if (!visible) {
return null;
}

// 举例:org.springframework.stereotype.Component
String typeName = Type.getType(descriptor).getClassName();
if (AnnotationFilter.PLAIN.matches(typeName)) {
return null;
}

try {
// 用指定的 classLoader 加载 typeName 表示的注解类型
Class<A> annotationType = (Class<A>) ClassUtils.forName(typeName, classLoader);
return new MergedAnnotationReadingVisitor<>(classLoader, source, annotationType, consumer);
}
catch (ClassNotFoundException | LinkageError ex) {
return null;
}
}

此时还未开始访问注解,只是创建了 AnnotationVisit,供 ClassReader 使用。在 ClassReader#readElementValues 方法中,才去执行注解的访问。之后执行 annotationVisitor.visitEnd() 进入 MergedAnnotationReadingVisitor#visitEnd 中执行。

@Override
public void visitEnd() {
MergedAnnotation<A> annotation = MergedAnnotation.of(
this.classLoader, this.source, this.annotationType, this.attributes);
this.consumer.accept(annotation);
}

创建一个 TypeMappedAnnotation 实例对象,调用 consumer 方法,此时的 consumer 就是创建 MergedAnnotationReadingVisitor 时传入的 lambda 表达式,其实就是将得到的 TypeMappedAnnotation 实例对象加入 annotations 集合,这个 annotations 集合是 SimpleAnnotationMetadataReadingVisitor 中的字段。

需要注意的一点,SimpleAnnotationMetadataReadingVisitor 并未重写 visitField 方法,也就是在此处不需要处理 Field 上的相关注解。

visitMethod

@Override
@Nullable
public MethodVisitor visitMethod(
int access, String name, String descriptor, String signature, String[] exceptions) {

// Skip bridge methods - we're only interested in original
// annotation-defining user methods. On JDK 8, we'd otherwise run into
// double detection of the same annotated method...
if (isBridge(access)) {
return null;
}
return new SimpleMethodMetadataReadingVisitor(this.classLoader, this.className,
access, name, descriptor, this.annotatedMethods::add);
}

针对方法的访问,委托给 SimpleMethodMetadataReadingVisitor 来处理,一个方法对应一个 SimpleMethodMetadataReadingVisitor 实例对象。顺便说下,针对类中的 "<init>" 和               "<cinit>" 两个特殊方法,也会创建 SimpleMethodMetadataReadingVisitor 实例对象来访问

方法上如果存在注解,调用 SimpleMethodMetadataReadingVisitor#visitAnnotation。

@Override
@Nullable
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
return MergedAnnotationReadingVisitor.get(this.classLoader, getSource(),
descriptor, visible, this.annotations::add);
}

可以看到,对注解的访问,不管是方法注解,还是类注解,都是通过 MergedAnnotationReadingVisitor 作为 AnnotationVisit 来操作的。

待类中所有方法都访问结束后,调用 SimpleMethodMetadataReadingVisitor#visitEnd。

@Override
public void visitEnd() {
if (!this.annotations.isEmpty()) {
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.methodName, this.access,
this.declaringClassName, returnTypeName, getSource(), annotations);
this.consumer.accept(metadata);
}
}

主要是针对方法上注解的操作,封装 SimpleMethodMetadata,填充 SimpleAnnotationMetadataReadingVisitor 中 annotatedMethods 字段。

visitEnd

待 ClassReader#accept 方法执行到最后,会调用 ClassVisit#visitEnd 方法,此处调用 SimpleAnnotationMetadataReadingVisitor#visitEnd。

@Override
public void visitEnd() {
// 存在内部类的情况
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
// 方法上注解
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
// 类上注解
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
// 将访问到的关于类的信息,封装到 SimpleAnnotationMetadata 中
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
this.enclosingClassName, this.superClassName, this.independentInnerClass,
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
}

将访问到的关于类的信息,最后都封装到了 SimpleAnnotationMetadata 类中,赋值给 SimpleAnnotationMetadataReadingVisitor 中 metadata 字段。

这样就完成了对类的访问。接着回到 SimpleMetadataReader 构造方法中,将 SimpleAnnotationMetadataReadingVisitor 中 metadata 字段返回,并赋值给 SimpleMetadataReader 中 annotationMetadata 字段。

创建完 SimpleMetadataReader 之后,如果使用的工厂是 CachingMetadataReaderFactory,会将这个 MetadataReader 实例对象缓存到 metadataReaderCache,key 为 Resource 实例对象。


原文地址:https://blog.csdn.net/zlk252620068/article/details/140431919

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!