Spring 如何通过包名扫描到包中的Bean组件?

要做到这件事要分成两步:

  1. 如何获取指定包路径下符合要求的所有文件
  2. 如何读取找到的字节码信息


1、如何获取指定包路径下符合要求的所有文件

1.1 相关接口

spring 为获取包中的资源提供了ResourceLoader 接口和 ResourcePatternResolver 接口,后者是前者的子接口,在前者的基础上额外定义了通过匹配模式批量获取资源的方法。

Spring 包扫描原理_spring

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提供了两个接口来实现相关功能:MetadataReaderFactoryMetadataReader

前者用于构建后者实例其提供的方法如下:

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 使用方法

CachingMetadataReaderFactorySimpleMetadataReader 则是两者的实现类,其用法如下:

MetadataReader metadataReader = new CachingMetadataReaderFactory().getMetadataReader(resource)

拿到类信息后符合要求的就会创建ScannedGenericBeanDefinition,以备后用。

2.3 大致原理

SimpleMetadataReader 则是通过org.springframework.asm包下的ClassReader获取的字节码信息,org.springframework.asm 是spring实现的asm字节码操作库。