前言
前面2章我们梳理了AnnotatedBeanDefinitionReader
的构造过程
今天我们来梳理ClassPathBeanDefinitionScanner
的构造过程
源码解析
我们首先顺着ClassPathBeanDefinitionScanner这个类的构造函数看
- 首先看有没有父类,有父类的话看父类的构造函数
public ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment) {
this.logger = LogFactory.getLog(this.getClass());
this.resourcePattern = "**/*.class";
this.includeFilters = new LinkedList();
this.excludeFilters = new LinkedList();
if (useDefaultFilters) {
this.registerDefaultFilters();
}
this.setEnvironment(environment);
this.setResourceLoader((ResourceLoader)null);
}
第一个遇见是日志,第二个语句是加载的资源路径,第三个是创建List容器,第四个if我们传入的是true
,默认加载配置文件,
我们调转到registerDefaultFilters
这个方法中,发现了这样的一句话
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
这个是默认加载所有包含了Component
这个注解的类,意味着我们的service,controller
本质上都是Component
这里是如何实现找到根部注解Component
?
用递归。这里需要排除元注解@Doument
避免出现死循环。
加载完成后注入之前this中加载的环境对象
this.setEnvironment(environment);
下一步装载资源对象,我们走进去看一看
this.setResourceLoader(resourceLoader);
public void setResourceLoader(@Nullable ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
this.componentsIndex = CandidateComponentsIndexLoader.loadIndex(this.resourcePatternResolver.getClassLoader());
}
- 第一句:提供了一个
ResourcePatternUtils
工具类,根据资源类型对象获得一个资源解析器对象
- 第二句:根据资源类型构建一个元数据工厂,我们进入进去看看
public CachingMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) {
super(resourceLoader);
if (resourceLoader instanceof DefaultResourceLoader) {
this.metadataReaderCache = ((DefaultResourceLoader)resourceLoader).getResourceCache(MetadataReader.class);
} else {
this.setCacheLimit(256);
}
}
获取元数据工厂的代码=》
super(resourceLoader);
看资源有没有实例,没有创建一个默认实例
2.从缓存里拿一个对象付给Map集合
第三句:得到一个类加载器放置进缓存当中返回一个下标。
到现在为止,ClassPathBeanDefinitionScanner
类就构建完成了。
this()总结
this()关键字做的事情
1.构建BeanFctory工厂
2.构建后置处理器
3.将注入的对象转为BeanDefinitionHolder
对象
4.在Scanner
添加一个Compont
过滤器
this()图解
Scan()构建
this();
this.scan(basePackages);
this.refresh();
解析scan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
this.doScan(basePackages);
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}
第一行代码是获得Bean数量
第二行代码我们走进去看看
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
return this.componentsIndex != null && this.indexSupportsIncludeFilters() ? this.addCandidateComponentsFromIndex(this.componentsIndex, basePackage) : this.scanCandidateComponents(basePackage);
}
这里我们没有实例化出来componentsIndex 对象因此走的是scanCandidateComponents
方法
findCandidateComponents>scanCandidateComponents
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
//构建集合
LinkedHashSet candidates = new LinkedHashSet();
try {
//凭借扫描包的路径
//classpath*:E:\J2EE-CSDN\Spring-Source code\Spring-SourceCode01\target\classes/**/*.class ,找到该路径下面所有.class文件
//这里resolveBasePackage调用的是刚刚初始化ApplicationContext强转为的RsourceLoader对象
String packageSearchPath = "classpath*:" + this.resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = this.getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = this.logger.isTraceEnabled();
boolean debugEnabled = this.logger.isDebugEnabled();
Resource[] var7 = resources;
int var8 = resources.length;
for(int var9 = 0; var9 < var8; ++var9) {
Resource resource = var7[var9];
if (traceEnabled) {
this.logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
//拿到元数据
MetadataReader metadataReader = this.getMetadataReaderFactory().getMetadataReader(resource);
//判断是否有Component注解
if (this.isCandidateComponent(metadataReader)) {
//创建Bean
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
//设置进Bean定义对象
sbd.setSource(resource);
if (this.isCandidateComponent((AnnotatedBeanDefinition)sbd)) {
if (debugEnabled) {
this.logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
} else if (debugEnabled) {
this.logger.debug("Ignored because not a concrete top-level class: " + resource);
}
} else if (traceEnabled) {
this.logger.trace("Ignored because not matching any filter: " + resource);
}
} catch (Throwable var13) {
throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, var13);
}
} else if (traceEnabled) {
this.logger.trace("Ignored because not readable: " + resource);
}
}
return candidates;
} catch (IOException var14) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", var14);
}
}
this.getResourcePatternResolver().getResources(packageSearchPath);对这个详解
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith("classpath*:")) {
return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));
} else {
int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(58) + 1;
return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};
}
}
这里是递归我们把程序跑起来看看
第一次进判断否包含classpath*:
判断是否包含满足含*条件
this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length()))
这里自然满足走进了findPathMatchingResources
构建资源容器代码
然后拿到头尾字段名称
根据拿到头尾名称判断是否是jar包,是不是vfs文件
如果不是则调用doFindPathMatchingJarResources
doFindPathMatchingJarResources
中是一个返回绝对路径的方法和调用doFindMatchingFileSystemResources
方法
这个方法递归加载所有文件
protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
}
Set<File> matchingFiles = this.retrieveMatchingFiles(rootDir, subPattern);
Set<Resource> result = new LinkedHashSet(matchingFiles.size());
Iterator var5 = matchingFiles.iterator();
while(var5.hasNext()) {
File file = (File)var5.next();
result.add(new FileSystemResource(file));
}
return result;
}
最后将资源对象返回回去
梳理完getResources
获得资源代码后我们再
回过头我们去梳理下扫描的这个核心代码scanCandidateComponents
我们再看下一步 this.getMetadataReaderFactory().getMetadataReader(resource);
元数据梳理的梳理。
首先.getMetadataReaderFactory()从缓存拿了一个了一个元数据工厂的时候会调用构造函数
public CachingMetadataReaderFactory(@Nullable ClassLoader classLoader) {
super(classLoader);
this.setCacheLimit(256);
}
父类构造了一个资源加载对象,加载元数据需要加载类帮助吧。
设置了一下最大值
public SimpleMetadataReaderFactory(@Nullable ClassLoader classLoader) {
this.resourceLoader = classLoader != null ? new DefaultResourceLoader(classLoader) : new DefaultResourceLoader();
}
.getMetadataReaderFactory()从缓存拿了一个了一个元数据工厂
public final MetadataReaderFactory getMetadataReaderFactory() {
if (this.metadataReaderFactory == null) {
this.metadataReaderFactory = new CachingMetadataReaderFactory();
}
return this.metadataReaderFactory;
}
获得metadataReaderCache
是Map
走第一个分叉,判断是否含有这个资源对象没有则构建一个对象super.getMetadataReader(resource);
public MetadataReader getMetadataReader(Resource resource) throws IOException {
if (this.metadataReaderCache instanceof ConcurrentMap) {
MetadataReader metadataReader = (MetadataReader)this.metadataReaderCache.get(resource);
if (metadataReader == null) {
metadataReader = super.getMetadataReader(resource);
this.metadataReaderCache.put(resource, metadataReader);
}
return metadataReader;
} else if (this.metadataReaderCache != null) {
synchronized(this.metadataReaderCache) {
MetadataReader metadataReader = (MetadataReader)this.metadataReaderCache.get(resource);
if (metadataReader == null) {
metadataReader = super.getMetadataReader(resource);
this.metadataReaderCache.put(resource, metadataReader);
}
return metadataReader;
}
} else {
return super.getMetadataReader(resource);
}
}
super.getMetadataReader(resource);
进行字节码的解析
是否是内部类,是否是运行时的编码
到现在为止doScan
里的方法 Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
就基本解析完了
我们返回到doScan
方法中
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
String[] var3 = basePackages;
int var4 = basePackages.length;
for(int var5 = 0; var5 < var4; ++var5) {
String basePackage = var3[var5];
//扫描加载将字节码文件转为 Set<BeanDefinition>对象
Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
Iterator var8 = candidates.iterator();
while(var8.hasNext()) {
BeanDefinition candidate = (BeanDefinition)var8.next();
//是咧对象拿到默认的作用域(静态属性)
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
//设置scope的作用域
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
}
if (this.checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
this.registerBeanDefinition(definitionHolder, this.registry);
}
}
}
详看this. .resolveScopeMetadata(candidate);
其实是new了一个scopeMetadata
对象里面静态字段为设置为单列
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();//这句
if (definition instanceof AnnotatedBeanDefinition) {
//转为AnnotatedBeanDefinition 对象,提供.getMetadata()方法,
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition)definition;
//将元数据为Key,Socpe为value转为linkHashMap<key,object>对象
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = (ScopedProxyMode)attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
看看我们候选bean的scope已经变为singleton
在设置好scope
特性后我们看看如何拿到BeanName
的。
this.beanNameGenerator.generateBeanName(candidate, this.registry)
;
首先看beanNameGenerator
,发现它等于一个单列对象AnnotationBeanNameGenerator.INSTANCE
我们找到AnnotationBeanNameGenerator
中的generateBeanName
方法
进去后发现获得一个注解的元数据,再重中获取元数据的特性个数
AnnotationMetadata amd = annotatedDef.getMetadata();
Set<String> types = amd.getAnnotationTypes();
遍历个数,传入实例和属性获得当前特性对象
AnnotationAttributes attributes AnnotationConfigUtils.attributesFor(amd, type)
拿到所有注解类型
Set<String> metaTypes = (Set)this.metaAnnotationTypesCache.computeIfAbsent(type, (key) -> {
Set<String> result = amd.getMetaAnnotationTypes(key);
return result.isEmpty() ? Collections.emptySet() : result;
});
this.isStereotypeWithNameValue(type, metaTypes, attributes)
判断当前类型是否是Compont
注解类型,是否包含value
属性,如果包含则BeanName
是Value
的值,没有则返回一个空null
if (this.isStereotypeWithNameValue(type, metaTypes, attributes)) {
Object value = attributes.get("value");
if (value instanceof String) {
String strVal = (String)value;
if (StringUtils.hasLength(strVal)) {
if (beanName != null && !strVal.equals(beanName)) {
throw new IllegalStateException("Stereotype annotations suggest inconsistent component names: '" + beanName + "' versus '" + strVal + "'");
}
beanName = strVal;
}
}
}
如果返回为空则执行return this.buildDefaultBeanName(definition, registry);
兜底方案默认首字母小写设置BeanName
图解
this()和Scan()图解
总结:
这里面有很多方法我没有写上去因为太过臃肿了,我把核心代码的解读和核心条件判断写了上去,其他的非核心没写,但是各位下去如果试着解读一下也能解读个大概意思出来。
其实读源码最重要的是猜想和验证,不然对着全是ABC,试着去猜想你会怎么做后读起来很快