1. 簡介
SpringBoot項(xiàng)目中或者 Spring項(xiàng)目中配置<context:component-scan base-package="com.example.demo" />
,那么在IOC 容器初始化階段(調(diào)用beanFactoryPostProcessor階段) 就會采用ClassPathBeanDefinitionScanner進(jìn)行掃描包下 所有類苞七,并將符合過濾條件的類注冊到IOC 容器內(nèi)立莉。Mybatis 的Mapper注冊器(ClassPathMapperScanner) 是同過繼承ClassPathBeanDefinitionScanner,并且自定義了過濾器規(guī)則來實(shí)現(xiàn)的髓废。具體的 調(diào)用過程并不會在這里說明宾添,只是想在這里描述ClassPathBeanDefinitionScanner是如何 掃描 和 注冊BeanDefinition的。
2. 作用
ClassPathBeanDefinitionScanner作用就是將指定包下的類通過一定規(guī)則過濾后 將Class 信息包裝成 BeanDefinition 的形式注冊到IOC容器中。
- 根據(jù)指定掃描報(bào)名 生成匹配規(guī)則。
例如:classpath*:com.example.demo/**/*.class
- resourcePatternResolver(資源加載器)根據(jù)匹配規(guī)則 獲取 Resource[] 娜饵。
- Resource數(shù)組中每一個(gè) 對象 都是對應(yīng)一個(gè) Class 文件,Spring 用Resource定位資源官辈, 封裝了資源的IO操作。
- 這里的 Resource 實(shí)際類型是 FileSystemResource.
- 資源加載器 其實(shí)就是 容器 本身遍坟。
- meteDataFactory根據(jù) Resouce 獲取到 MetadataReader 對象
- MetadataReader 提供了 獲取 一個(gè)Class 文件的 ClassMetadata 和 AnnotationMetadata 的 操作拳亿。
- 根據(jù)過濾器規(guī)則 匹配 MetadataReader中的類 進(jìn)行過濾,比如 是否是Componet 注解標(biāo)注的類愿伴。
- 轉(zhuǎn)換 MetadataReader 為 BeanDefinition.
- 將BeanDefinition 注冊到 BeanFactory.
3. 默認(rèn)的過濾器注冊
過濾器用來過濾 從指定包下面查找到的 Class ,如果能通過過濾器肺魁,那么這個(gè)class 就會被轉(zhuǎn)換成BeanDefinition 注冊到容器。
如果在實(shí)例化ClassPathBeanDefinitionScanner時(shí)隔节,沒有說明要使用用戶自定義的過濾器的話鹅经,那么就會采用下面的默認(rèn)的過濾器規(guī)則寂呛。
注冊了@Component
過濾器到 includeFiters ,相當(dāng)于 同時(shí)注冊了所有被@Component
注釋的注解,包括@Service
瘾晃,@Repository
,@Controller
贷痪,同時(shí)也支持java EE6 的javax.annotation.ManagedBean
和 JSR-330 的 @Named
注解。
protected void registerDefaultFilters() {
// 添加Component 注解過濾器
//這就是為什么 @Service @Controller @Repostory @Component 能夠起作用的原因蹦误。
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
try {
// 添加ManagedBean 注解過濾器
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
logger.debug("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.
}
try {
// 添加Named 注解過濾器
this.includeFilters.add(new AnnotationTypeFilter(
((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
}
catch (ClassNotFoundException ex) {
// JSR-330 API not available - simply skip.
}
}
4. 執(zhí)行掃描(doScan)
實(shí)際執(zhí)行包掃描,進(jìn)行封裝的函數(shù)是findCandidateComponents,findCandidateComponents定義在父類中劫拢。ClassPathBeanDefinitionScanner的主要功能實(shí)現(xiàn)都在這個(gè)函數(shù)中。
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
try {
// 1.根據(jù)指定包名 生成包搜索路徑
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//2. 資源加載器 加載搜索路徑下的 所有class 轉(zhuǎn)換為 Resource[]
Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
// 3. 循環(huán) 處理每一個(gè) resource
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
// 讀取類的 注解信息 和 類信息 强胰,信息儲存到 MetadataReader
//
MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
// 執(zhí)行判斷是否符合 過濾器規(guī)則舱沧,函數(shù)內(nèi)部用過濾器 對metadataReader 過濾
if (isCandidateComponent(metadataReader)) {
//把符合條件的 類轉(zhuǎn)換成 BeanDefinition
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setResource(resource);
sbd.setSource(resource);
// 再次判斷 如果是實(shí)體類 返回true,如果是抽象類,但是抽象方法 被 @Lookup 注解注釋返回true
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
}
//省略了 部分代碼
}
catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}
5. 自定義掃描器
通過自定義的掃描器,掃描指定包下所有被@MyBean 注釋的類偶洋。
5.1 定義一個(gè)注解熟吏,并注釋一個(gè)類
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyBean {
}
@MyBean
public class TestScannerBean {
}
5.2 編寫掃描器
class MyClassPathDefinitonScanner extends ClassPathBeanDefinitionScanner{
private Class type;
public MyClassPathDefinitonScanner(BeanDefinitionRegistry registry,Class<? extends Annotation> type){
super(registry,false);
this.type = type;
}
/**
* 注冊 過濾器
*/
public void registerTypeFilter(){
addIncludeFilter(new AnnotationTypeFilter(type));
}
}
5.3 測試自定義掃描器
- 測試代碼
@Test
public void testSimpleScan() {
String BASE_PACKAGE = "com.example.demo";
GenericApplicationContext context = new GenericApplicationContext();
MyClassPathDefinitonScanner myClassPathDefinitonScanner = new MyClassPathDefinitonScanner(context, MyBean.class);
// 注冊過濾器
myClassPathDefinitonScanner.registerTypeFilter();
int beanCount = myClassPathDefinitonScanner.scan(BASE_PACKAGE);
context.refresh();
String[] beanDefinitionNames = context.getBeanDefinitionNames();
System.out.println(beanCount);
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
- 測試結(jié)果
7
//這個(gè)就是我們掃描到的bean
testScannerBean
//下面這些 是 父類掃描器 注冊的 beanFactory后置處理器
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
6. 總結(jié)
通過對ClassPathBeanDefinitionScanner的分析,終于揭開了Spring 的類掃描的神秘面紗,其實(shí)玄窝,就是對指定路徑下的 所有class 文件進(jìn)行逐一排查牵寺,對符合條件的 class ,封裝成 BeanDefinition注冊到IOC 容器。
理解ClassPathBeanDefinitionScanner的工作原理哆料,可以幫助理解Spring IOC 容器的初始化過程缸剪。
同時(shí)對理解MyBatis 的 Mapper 掃描 也是有很大的幫助。
因?yàn)?MyBatis 的MapperScannerConfigurer的底層實(shí)現(xiàn)也是一個(gè)ClassPathBeanDefinitionScanner的子類东亦。就像我們自定義掃描器那樣杏节,自定定義了 過濾器的過濾規(guī)則。