Spring 如何通过包名扫描到包中的Bean组件?
要做到这件事要分成两步:
- 如何获取指定包路径下符合要求的所有文件
- 如何读取找到的字节码信息
1、如何获取指定包路径下符合要求的所有文件
1.1 相关接口
spring 为获取包中的资源提供了ResourceLoader
接口和 ResourcePatternResolver
接口,后者是前者的子接口,在前者的基础上额外定义了通过匹配模式批量获取资源的方法。
1.2 使用方法
PathMatchingResourcePatternResolver
则是两者的实现类,使用方法如下:
// packageSearchPath 为匹配模式 如:classpath*:top.sysh/**/*.class
Resource[] resources = new PathMatchingResourcePatternResolver().getResources(packageSearchPath)
1.3 大致原理
PathMatchingResourcePatternResolver
底层还是通过CLassLoader
的 getResources 方法来获取,只不过提供了一些更加实用的功能,如对通配符的支持等。
classLoader != null ? classLoader.getResources(path) : ClassLoader.getSystemResources(path);
2、如何读取找到的字节码信息
2.1 相关接口
spring提供了两个接口来实现相关功能:MetadataReaderFactory
和 MetadataReader
前者用于构建后者实例其提供的方法如下:
public interface MetadataReaderFactory {
MetadataReader getMetadataReader(String className) throws IOException;\
MetadataReader getMetadataReader(Resource resource) throws IOException;
}
后者则是对具体信息包装
public interface MetadataReader {
Resource getResource();
ClassMetadata getClassMetadata();
AnnotationMetadata getAnnotationMetadata();
}
ClassMetadata 封装了类的基本信息,如类名、是不是接口、父类。。。
AnnotationMetadata 则封装了类的基本信息和注解信息,如是否标记有某注解,注解的类路径,标有某注解的所有方法元数据。。。(AnnotationMetadata 是 ClassMetadata 的子接口)
2.2 使用方法
CachingMetadataReaderFactory
和 SimpleMetadataReader
则是两者的实现类,其用法如下:
MetadataReader metadataReader = new CachingMetadataReaderFactory().getMetadataReader(resource)
拿到类信息后符合要求的就会创建ScannedGenericBeanDefinition,以备后用。
2.3 大致原理
SimpleMetadataReader
则是通过org.springframework.asm包下的ClassReader获取的字节码信息,org.springframework.asm 是spring实现的asm字节码操作库。