博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Boot 基于注解驱动源码分析--自动扫描
阅读量:5952 次
发布时间:2019-06-19

本文共 6532 字,大约阅读时间需要 21 分钟。

上一篇文章介绍了spring的自动配置,那么spring是如何扫描包下所有类信息?本篇学习一下关于@ComponentScan、@ComponentScans注解解析和ClassPathBeanDefinitionScanner、PathMatchingResourcePatternResolver类的源码分析。

ClassPathBeanDefinitionScanner

A bean definition scanner that detects bean candidates on the classpath,  registering corresponding bean definitions with a given registry ({@code BeanFactory}  or {@code ApplicationContext}).复制代码

一个bean定义扫描器用来检测类路径下候选bean,提供一个注册器(registry)注册相应的bean定义

PathMatchingResourcePatternResolver

A {@link ResourcePatternResolver} implementation that is able to resolve a specified resource location path into one or more matching Resources.The source path may be a simple path which has a one-to-one mapping to a target {@link org.springframework.core.io.Resource}, or alternativelymay contain the special "{@code classpath*:}" prefix and/orinternal Ant-style regular expressions (matched using Spring's{@link org.springframework.util.AntPathMatcher} utility).Both of the latter are effectively wildcards.复制代码

一个ResourcePatternResolver的实现,可以为一个或者更多的资源解析指定的资源匹配位置路径;源路径可能是一个简单的路径一对一的映射目标或者另外可能含有特殊的前缀和基于ant正则表达式

大致了解两个关键类后,从注解解析入口开始进行源码分析

@ComponentScan 注解解析

在上一篇文章中了解到注解解析是在ConfigurationClassParser的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)方法中进行

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)			throws IOException {		......				// 处理 @ComponentScan 注解				//获取资源类的ComponentScan、ComponentScans注解信息		Set
componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // 配置类上有@ComponentScan注解,立即执行扫描 Set
scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } ...... return null; }复制代码

这里的this.componentScanParser 是 ComponentScanAnnotationParser

public Set
parse(AnnotationAttributes componentScan, final String declaringClass) { Assert.state(this.environment != null, "Environment must not be null"); Assert.state(this.resourceLoader != null, "ResourceLoader must not be null"); //类路径Bean定义扫描器 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry, componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader); ...... 省略读取注解信息,为ClassPathBeanDefinitionScanner初始化赋值 //扫描器扫描 return scanner.doScan(StringUtils.toStringArray(basePackages));复制代码

ClassPathBeanDefinitionScanner扫描

指定包进行扫描

protected Set
doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); Set
beanDefinitions = new LinkedHashSet
(); for (String basePackage : basePackages) { //通过包路径寻找候选组件 Set
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); } 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; }复制代码

通过包路径寻找候选组件

扫描classpath下的候选组件

public Set
findCandidateComponents(String basePackage) { Set
candidates = new LinkedHashSet
(); try { String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + '/' + this.resourcePattern; //通过资源解析器获取resources Resource[] resources = this.resourcePatternResolver.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 = this.metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); 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;复制代码

这里的this.resourcePatternResolver是通过PathMatchingResourcePatternResolver获取resources;通过MetaDataReader实例化ScannedGenericBeanDefinition。

以上属于原创文章,转载请注明作者

QQ:208275451

你可能感兴趣的文章
0基础搭建Hadoop大数据处理-初识
查看>>
ASP.NET Core MVC请求超时设置解决方案
查看>>
CentOS 7 安装Mono 和 MonoDevelop
查看>>
Easyui 让Window弹出居中与最大化后居中
查看>>
greenplum 单表 数据扫描
查看>>
JQuery 的跨域方法 可跨任意网站
查看>>
Netty系列之Netty高性能之道
查看>>
Jenkins不同job之间传递参数
查看>>
解决在Windows 2003的 IIS 6.0 中无法上传超过200K的附件以及无法下载超过4M的附件问题...
查看>>
Message和handler传递对象
查看>>
Weka算法Classifier-tree-J48源代码分析(一个)基本数据结构和算法
查看>>
如何将自己的代码自动添加版权信息[转]
查看>>
PLSQL用DBMS_JOB建立作业
查看>>
实战 SQL Server 2008 数据库误删除数据的恢复
查看>>
从函数调用来思考多态
查看>>
[Oracle]如果误删了某个数据文件,又没有被备份,能否恢复?
查看>>
Access访问错误集锦:Access关键字
查看>>
iostat命令详解
查看>>
女子监狱第一季/全集Orange Is the New Black迅雷下载
查看>>
vc中运行外部程序的方法
查看>>