在Spring框架中,类路径扫描和BeanDefinition解析是一个核心流程。为了高效地管理资源,Spring不会将扫描路径下的所有类直接加载到JVM内存中,而是采用了基于字节码分析的技术来实现这一过程。具体来说,Spring利用ASM(字节码 manipulator)技术,直接解析Class文件的字节码,提取所需的元数据信息。
这种设计理念有两个关键优势:
- 避免了不必要的类加载操作,减少了内存占用;
- 能够以更高效的方式获取注解信息和接口实现,而无需将完整的类加载到JVM中。
通过这种方式,Spring能够在类加载阶段就筛选出需要注册为Bean的类,从而优化了资源使用效率,既保证了功能的灵活性,又实现了对系统资源的有效管理。
MetaDataReader
MetadataReader是一个接口,它主要定义了三个方法
- getResource()
- getClassMetadata()
- getAnnotationMetadata()
Spring在扫描解析类时,会通过ASM解析并生成MetadataReader的实现类SimpleMetadataReader
,而AnnotationMetadata
是继承自ClassMetadata
的接口,因此通过SimpleMetadataReader
的getClassMetadata()
和getAnnotationMetadata()
返回的其实都是AnnotationMetadata对象
final class SimpleMetadataReader implements MetadataReader {
private static final int PARSING_OPTIONS =
(ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
private final Resource resource;
private final AnnotationMetadata annotationMetadata;
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
SimpleAnnotationMetadataReadingVisitor visitor = new SimpleAnnotationMetadataReadingVisitor(classLoader);
// 这里通过classReader读取resource对应的文件,然后将信息存入visitor中
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
this.resource = resource;
this.annotationMetadata = visitor.getMetadata();
}
private static ClassReader getClassReader(Resource resource) throws IOException {
try (InputStream is = resource.getInputStream()) {
try {
return new ClassReader(is);
}
catch (IllegalArgumentException ex) {
throw new ClassFormatException("ASM ClassReader failed to parse class file - " +
"probably due to a new Java class file version that is not supported yet. " +
"Consider compiling with a lower '-target' or upgrade your framework version. " +
"Affected class: " + resource, ex);
}
}
}
@Override
public Resource getResource() {
return this.resource;
}
@Override
public ClassMetadata getClassMetadata() {
return this.annotationMetadata;
}
@Override
public AnnotationMetadata getAnnotationMetadata() {
return this.annotationMetadata;
}
}
ClassMetadata
ClassMetadata主要存储类相关的信息,包括类名,父类名,实现了那些接口的接口名等。具体例子如下
public static void main(String[] args) throws IOException {
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.ww.Order");
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 类名
String className = classMetadata.getClassName();
// 实现了那些接口
String[] interfaceNames = classMetadata.getInterfaceNames();
System.out.println(className);
System.out.println(interfaceNames[0]);
// 用SimpleMetadataReaderFactory的类加载器,验证JVM没有加载Order类
// 使用ClassLoader的findLoadedClass方法,如果jvm加载过类了,该方法将返回类的class对象,没加载过则返回null
ClassLoader classLoader = SimpleMetadataReaderFactory.class.getClassLoader();
Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
findLoadedClass.setAccessible(true);
System.out.println(findLoadedClass.invoke(classLoader, className) != null);
}
@Service
public class Order implements PriorityOrdered {
@Value("李四")
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void print() {
System.out.println(user.getName());
}
@Override
public int getOrder() {
return 10;
}
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Good{
private String goodName;
public String getGoodName() {
return goodName;
}
public void setGoodName(String goodName) {
this.goodName = goodName;
}
}
}
可以看到分别获取到了类名和实现的接口名,同时findLoadedClass
方法返回的是null,表示没有加载Order类
AnnotationMetadata
AnnotationMetadata
接口,主要保存的是类上注解的相关信息,同时,因为它继承自ClassLoader,因此也有ClassLoader对应的方法。
这里Order类还是上面的类,注意一下Order类上的注解是@Service
public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory();
MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader("com.ww.Order");
// ClassMetadata classMetadata = metadataReader.getClassMetadata();
// // 类名
// String className = classMetadata.getClassName();
// // 实现了那些接口
// String[] interfaceNames = classMetadata.getInterfaceNames();
//
// System.out.println(className);
// System.out.println(interfaceNames[0]);
// // 验证JVM没有加载Order类
// // 使用ClassLoader的findLoadedClass方法,如果jvm加载过类了,该方法将返回类的class对象,没加载过则返回null
// ClassLoader classLoader = SimpleMetadataReaderFactory.class.getClassLoader();
// Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
// findLoadedClass.setAccessible(true);
// System.out.println(findLoadedClass.invoke(classLoader, className) != null);
//
// System.out.println("===========");
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
System.out.println(annotationMetadata.getClassName());
System.out.println(annotationMetadata.hasMetaAnnotation(Component.class.getName()));
System.out.println(annotationMetadata.hasAnnotation(Component.class.getName()));
}
这里同样能获取类的名字以及是否存在某个注解
hasMetaAnnotation(String metaAnnotationName)方法
hasMetaAnnotation(String metaAnnotationName)
方法,主要检查类上注解的元信息中是否存在给定的metaAnnotationName
名字的注解。比如现在Order类上的@Service
注解中,就包括了@Component
注解。因此这个方法这里返回的true
hasAnnotation(String annotationName)方法
hasAnnotation(String annotationName)
方法,主要检查类上是否有给定的annotationName
名字的注解,这里Order类上是@Service
注解,当然不是@Component
注解,因此返回的false。