ClassPathBeanDefinitionScanner

HeJin大约 2 分钟源码解析bean的注入

应用场景

  • 识别加了指定注解的类,把这些类的BeanDefinition对象注册到容器中。这个时候就可以使用ClassPathBeanDefinitionScanner
  • 在使用ImportBeanDefinitionRegistrar注册bean的时候,我们需要手动创建BeanDefinition对象。如果需要导入的bean很多,全部手动创建就不现实了。这时候就可以结合ClassPathBeanDefinitionScanner扫描进行使用。

使用

public class MyComponentRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                return true;
            }
        });

        scanner.scan("com.service");
    }
}

这里使用了ImportBeanDefinitionRegistrar注册bean,并且使用ClassPathBeanDefinitionScanner扫描了com.service包下的所有类。因为IncludeFilter过滤器总是返回true,所以会扫描这个包下的所有类,并注入到容器中。

@Service
public class AdminService {
}
public class GroupService {
}
public class UserService {
}

测试:

@SpringBootApplication
@Import(MyComponentRegistrar.class) // 导入ImportBeanDefinitionRegistrar
public class SpringBootAnalysisApp {
    private static final Logger logger = LoggerFactory.getLogger(SpringBootAnalysisApp.class);

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(SpringBootAnalysisApp.class, args);
        AdminService adminService = applicationContext.getBean(AdminService.class);
        GroupService groupService = applicationContext.getBean(GroupService.class);
        UserService userService = applicationContext.getBean(UserService.class);
        logger.info("adminService: {}", adminService);
        logger.info("groupService: {}", groupService);
        logger.info("userService: {}", userService);
    }
}

结果:

com.sanfen.SpringBootAnalysisApp         : adminService: com.service.AdminService@1ddd3478
com.sanfen.SpringBootAnalysisApp         : groupService: com.service.GroupService@43cf6ea3
com.sanfen.SpringBootAnalysisApp         : userService: com.service.UserService@37c5fc56

我们发现com.service包下的所有类都被注册到了Spring容器中。不过在实际的开发中,我们只希望导入我们自己需要的,比如加了某些注解的,就可以实现自己的过滤规则进行导入。

自定义注解扫描

自定义一个注解:

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

ClassPathBeanDefinitionScanner只扫描加了注解@MyComponent的类。编写我们的过滤规则:

public class MyComponentRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
        scanner.addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                // 只扫描加了注解@MyComponent的类
                return metadataReader.getAnnotationMetadata().hasAnnotation("com.registrar.MyComponent");
            }
        });

        scanner.scan("com.service");
    }
}

com.service包:

@Service
public class AdminService {
}
public class GroupService {
}
@MyComponent
public class UserService {
}

此时只有UserService类会被注册到容器中。

@SpringBootApplication
@Import(MyComponentRegistrar.class)
public class SpringBootAnalysisApp {
    private static final Logger logger = LoggerFactory.getLogger(SpringBootAnalysisApp.class);

    public static void main(String[] args) {
        ApplicationContext applicationContext = SpringApplication.run(SpringBootAnalysisApp.class, args);
        UserService userService = applicationContext.getBean(UserService.class);
        logger.info("userService: {}", userService);
    }
}

结果:

com.sanfen.SpringBootAnalysisApp         : userService: com.service.UserService@21fff664