ClassPathBeanDefinitionScanner

3,061 阅读2分钟

ClassPathBeanDefinitionScanner作用就是将指定包下的类通过一定规则过滤后, 将Class 信息包装成 BeanDefinition 的形式,注册到IOC容器中。

实际执行包扫描,进行封装的函数是findCandidateComponents,findCandidateComponents定义在父类中。ClassPathBeanDefinitionScanner的主要功能实现都在这个函数中。

执行流程

关键步骤代码

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
        try {
            // 1.根据指定包名 生成包搜索路径
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
            //2. 资源加载器 加载搜索路径下的 所有class 转换为 Resource[]
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            // 3. 循环 处理每一个 resource 
            for (Resource resource : resources) {
            
                if (resource.isReadable()) {
                    try {
                        // 读取类的 注解信息 和 类信息 ,信息储存到  MetadataReader
                        // 
                        MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                     // 执行判断是否符合 过滤器规则,函数内部用过滤器 对metadataReader 过滤  
                        if (isCandidateComponent(metadataReader)) {
                            //把符合条件的 类转换成 BeanDefinition
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setResource(resource);
                            sbd.setSource(resource);
                            // 再次判断 如果是实体类 返回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;
    }

自定义扫描器

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyBean {

}

@MyBean
public class TestScannerBean {

}

编写扫描器

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));
        }
    }

测试自定义扫描器

    @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);
        //将beanDefinetion实例化放到容器中
        context.refresh();
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        System.out.println(beanCount);
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }

//这个就是我们扫描到的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

补充

ClassPathBeanDefinitionScanner继承自ClassPathScanningCandidateComponentProvider,构造时要求指定一个BeanDefinitionRegistry对象,其扩展了一个scan方法,可以同时指定多个要扫描的包。底层在扫描bean定义时还是使用的父类的findCandidateComponents方法,但是扫描后会自动利用持有的BeanDefinitionRegistry自动对bean定义进行注册

注册bean定义的bean名称会使用持有的BeanNameGenerator生成,默认是AnnotationBeanNameGenerator;如果对应的bean定义是AnnotatedBeanDefinition类型的,还会处理对应的一些注解定义

假设现有HelloOne和HelloTwo两个类,分别位于包com.elim.learn.spring.bean.registry.one和com.elim.learn.spring.bean.registry.two下,它们类上都标注了HelloAnnotation注解,

如果现在需要只扫描包com.elim.learn.spring.bean.registry.one和com.elim.learn.spring.bean.registry.two下类上拥有HelloAnnotation注解的类作为bean,则可以定义如下BeanDefinitionRegistryPostProcessor进行bean扫描。

public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor {

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
		ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry);
		TypeFilter includeFilter = new AnnotationTypeFilter(HelloAnnotation.class);
		scanner.addIncludeFilter(includeFilter);
		String[] basePackages = {"com.elim.learn.spring.bean.registry.one", "com.elim.learn.spring.bean.registry.two"};
		scanner.scan(basePackages);
	}

}
测试代码如下:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes={CustomBeanDefinitionRegistry.class})
public class CustomBeanDefinitionRegistryTest {

	@Autowired
	private ApplicationContext applicationContext;
	
	@Test
	public void assertBean() {
		Assert.assertNotNull(this.applicationContext.getBean(HelloOne.class));
		Assert.assertNotNull(this.applicationContext.getBean(HelloTwo.class));
	}
	
}