ClassPathBeanDefinitionScanner
大约 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