目录
使用Class和Classloader定位资源
通过Classloader定位资源: 路径不要带/开头,默认就是以classpath为根路径
// 文件路径
ClassLoader classLoader = TestPath.class.getClassLoader();
URL resource = classLoader.getResource("testpath.properties");
System.out.println(resource);
// 得到结果: file:/G:/5.workspace/3.my-projects/demo/**/target/classes/testpath.properties
// 目录路径
URL resource2 = classLoader.getResource("com/txd/util");
System.out.println(resource2);
// 得到结果:file:/G:/5.workspace/3.my-projects/demo/l**/target/classes/com/txd/util
通过Class定位资源:路径带/就是以classpath为根路径(效果同ClassLoader) ; 路径不带/就是相对当前使用的class路径
// 文件路径
URL resource1 = TestPath.class.getResource("/testpath.properties");
System.out.println(resource1);
// 结果:/ file:/G:/5.workspace/3.my-projects/demo/**/target/classes/testpath.properties
// 目录路径:
URL resource3 = TestPath.class.getResource("test");
System.out.println(resource3);
// 结果:G:/5.workspace/3.my-projects/demo/**/target/classes/com/txd/util/test
spring中的ClassPathBeanDefinitionScanner(基于spring5.1.2)
看spring的源代码发现,主要的方法就是doScan
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 从配置的路径下扫描过滤得到所有的bean
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
// scope注解
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
// bean的默认配置
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
// 覆盖bean默认配置
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
// 注册到IOC容器
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
首先来看第一段:
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
从classpath下解析配置路径下的资源。分三种情况:vfs文件,普通文件,jar文件。扫描的类通过ScannedGenericBeanDefinition来保存描述其类信息。
第二段:通过scopeMetadataResolver得到scope注解元数据信息,存到ScannedGenericBeanDefinition中
第三段:添加bean的默认配置
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
跟进代码:发现主要是lazyInit默认false,autowireMode默认0(和自动注入:配置0需要手动通过@Autowired注入)
第四段:通过注解配置覆盖bean的默认配置
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
跟进代码可用看待几个常用的注解
第五段:注入ioc
registerBeanDefinition(definitionHolder, this.registry);
mybatis通过重写ClassPathBeanDefinitionScanner的isCandidateComponent(metadataReader)来过滤mapper接口
springboot项目中通过mybatis的自动配置类MybatisAutoConfiguration引入了AutoConfiguredMapperScannerRegistrar
@Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
public MapperScannerRegistrarNotFoundConfiguration() {
}
public void afterPropertiesSet() {
MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
}
}
而AutoConfiguredMapperScannerRegistrar又是ImportBeanDefinitionRegistrar的实现,在解析Import的时候执行registerBeanDefinitions注入MapperScannerConfigurer。MapperScannerConfigurer又是BeanDefinitionRegistryPostProcessor的实现,spring初始化会执行postProcessBeanDefinitionRegistry方法,其中使用到了ClassPathMapperScanner。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(this.lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
}
scanner.registerFilters();
// 扫描mapper接口配置,然后通过isCandidateComponent过滤得到所有接口的bean
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
重写的isCandidateComponent只要是接口就扫描到spring中成为bean