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