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 实例对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值