交流
商城
MCN
登入
注册
首页
提问
分享
讨论
建议
公告
动态
发表新帖
发表新帖
第6 章:ClassPathBeanDefinitionScanner
分享
未结
0
1007
李延
LV6
2021-05-22
悬赏:20积分
# 作用 在ConfigurationClassPostProcessor类分析过程中,我们解析到springboot 在扫描到有@ComponentScans注解的类是调用ClassPathBeanDefinitionScanner。 用于扫描指定路径下的class。并将其注册到registry中,也就是解析我们日常使用的Service、Controller等注解过程。 # 构造函数 ```java public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) { this(registry, true); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { this(registry, useDefaultFilters, getOrCreateEnvironment(registry)); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) { this(registry, useDefaultFilters, environment, (registry instanceof ResourceLoader ? (ResourceLoader) registry : null)); } public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, @Nullable ResourceLoader resourceLoader) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); this.registry = registry; if (useDefaultFilters) { registerDefaultFilters(); } setEnvironment(environment); setResourceLoader(resourceLoader); } ``` 我们看到所有的方法最终调用了最后一个方法。其中主要逻辑为设置一些属性值。其中需要关注的是registerDefaultFilters方法 ```java protected void registerDefaultFilters() { this.includeFilters.add(new AnnotationTypeFilter(Component.class)); ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader(); try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false)); logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-250 1.1 API (as included in Java EE 6) not available - simply skip. } try { this.includeFilters.add(new AnnotationTypeFilter( ((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false)); logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } ``` 我们看到主要是设置includeFilters的值。而AnnotationTypeFilter就是对于扫描到的class注解过滤过程。 在这里添加了Component、javax.annotation.ManagedBean、javax.inject.Named 这3个类的扫描。 而我们使用的Service、Controller注解上面就是有Component注解。 # doScan 方法 ```java 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) { ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); candidate.setScope(scopeMetadata.getScopeName()); String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //注解的解析 if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //注册到register中 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; } ``` 在上面方法中我们看到参数为basePackages,是一个包的路径。这个值在@ComponentScan中可以设置,默认情况下为注解的class所在的包。 其中主要的过程有: 1. 通过findCandidateComponents方法获取到所有的bean定义 2. 如果是注解Bean,通过AnnotationConfigUtils.processCommonDefinitionAnnotations解析注解 3. 将解析到的BeanDefinition注册到register中。 我们分别看一下前2步的过程。 ## findCandidateComponents ```java public Set<BeanDefinition> findCandidateComponents(String basePackage) { if (this.componentsIndex != null && indexSupportsIncludeFilters()) { return addCandidateComponentsFromIndex(this.componentsIndex, basePackage); } else { return scanCandidateComponents(basePackage); } } private Set<BeanDefinition> scanCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<>(); try { // 扫描路径拼接 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; // 加载出所有的文件 Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); // 遍历每个文件 for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource); //通过isCandidateComponent 判断是否为我们需要找的内容 if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { if (debugEnabled) { logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; } ``` 方法一开始是拼接了一个扫描路径,具体如下图: packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern  我们看到就是扫描指定包下的所有class文件。 之后有对于所有扫描到的文件通过isCandidateComponent方法判断是否符合我们要求,如果符合将结果返回。具体我们看一下isCandidateComponent方法: ```java protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException { for (TypeFilter tf : this.excludeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return false; } } for (TypeFilter tf : this.includeFilters) { if (tf.match(metadataReader, getMetadataReaderFactory())) { return isConditionMatch(metadataReader); } } return false; } ``` 我们看到其实就是通过excludeFilters排除我们不需要的class。通过includeFilters确定我们需要的内容。 而在构造函数中,我们看到在includeFilters添加了对Component注解的识别,所以在我们使用中的Service注解会被识别。对于其他规则我们在方法调用处说明。 ## AnnotationConfigUtils.processCommonDefinitionAnnotations 在扫描到需要的bean之后,对于注解bean。调用这个方法,添加对其他注解的支持 ```java public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) { processCommonDefinitionAnnotations(abd, abd.getMetadata()); } static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { AnnotationAttributes lazy = attributesFor(metadata, Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } else if (abd.getMetadata() != metadata) { lazy = attributesFor(abd.getMetadata(), Lazy.class); if (lazy != null) { abd.setLazyInit(lazy.getBoolean("value")); } } if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class); if (dependsOn != null) { abd.setDependsOn(dependsOn.getStringArray("value")); } AnnotationAttributes role = attributesFor(metadata, Role.class); if (role != null) { abd.setRole(role.getNumber("value").intValue()); } AnnotationAttributes description = attributesFor(metadata, Description.class); if (description != null) { abd.setDescription(description.getString("value")); } } ``` 通过上面代码,我们看到很多我们日常使用的注解: - Lazy - Primary - DependsOn - Role - Description 到目前为止。我们知道了spring对class扫描的完整过程。下面我们看一下,springboot在调用doScan方法前都设置了什么东西,其中重点关注:excludeFilters和includeFilters # 调用 在ConfigurationClassPostProcessor中,代码如下: ```java public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) { //创建对象 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); //设置BeanNameGenerator Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator"); boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass); scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator : BeanUtils.instantiateClass(generatorClass)); ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy"); if (scopedProxyMode != ScopedProxyMode.DEFAULT) { scanner.setScopedProxyMode(scopedProxyMode); } else { Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver"); scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass)); } scanner.setResourcePattern(componentScan.getString("resourcePattern")); //加载注解上的includeFilters规则 for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addIncludeFilter(typeFilter); } } //加载注解上的excludeFilters规则 for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) { for (TypeFilter typeFilter : typeFiltersFor(filter)) { scanner.addExcludeFilter(typeFilter); } } // 设置lazyInit默认值 boolean lazyInit = componentScan.getBoolean("lazyInit"); if (lazyInit) { scanner.getBeanDefinitionDefaults().setLazyInit(true); } // 设置basePackages,也就是扫描包路径 Set<String> basePackages = new LinkedHashSet<>(); String[] basePackagesArray = componentScan.getStringArray("basePackages"); for (String pkg : basePackagesArray) { String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg), ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); Collections.addAll(basePackages, tokenized); } for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) { basePackages.add(ClassUtils.getPackageName(clazz)); } if (basePackages.isEmpty()) { basePackages.add(ClassUtils.getPackageName(declaringClass)); } //添加一个排除当前扫描类的过滤器 scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) { @Override protected boolean matchClassName(String className) { return declaringClass.equals(className); } }); return scanner.doScan(StringUtils.toStringArray(basePackages)); } ``` 通过上面代码 我们看到在@ComponentScan注解上面还有设置includeFilters与excludeFilters 我们在具体注解的文章中进行解析
回帖
消灭零回复
提交回复
热议榜
java 相关知识分享
8
好的程序员与不好的程序员
6
写给工程师的十条精进原则
5
spring boot以jar包运行配置的logback日志文件没生成
5
一步一步分析SpringBoot启动源码(一)
5
MockMvc测试
5
【吐槽向】是不是有个吐槽的板块比较好玩
4
logstash jdbc同步mysql多表数据到elasticsearch
3
IntelliJ IDEA 优质License Server
3
.gitignore忽略规则
3
SpringBoot启动源码分析
3
一步一步分析SpringBoot启动源码(三)
3
2
一步一步分析SpringBoot启动源码(二)
2
积分不够将无法发表新帖
2
官方产品
Meta-Boot - 基于MCN
MCN - 快速构建SpringBoot应用
微信扫码关注公众号