Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?
阅前提醒
全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解。由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位大佬在评论区留言指正。建议在阅读本篇文章之前,先看下我的另一篇博文 一图助你搞明白Spring应用上下文初始化流程! 从而对Spring应用上下文初始化过程有个大概了解。
如果各位小伙伴由于笔者描述不清,而不理解的地方也欢迎在评论区留言哦!该系列文章共有三篇,敬请期待~ ~ ~
正文
一切的开始要从AbstractApplicationContext的refresh方法说起。refresh方法是应用上下文中最重要的方法,没有之一!
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 忽略其他方法...
try {
// 忽略其他方法...
// 调用所有实现了BeanFactoryPostProcessor接口的实现类.
invokeBeanFactoryPostProcessors(beanFactory);
// 其它方法忽略....
}
catch (BeansException ex) {
// 忽略其他方法...
throw ex;
}
finally {
resetCommonCaches();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
将包路径变为IoC容器中的BeanDefinition,对于注解驱动的应用上下文来说这一步需要等到refresh方法执行invokeBeanFactoryPostProcessors方法(XML驱动的应用上下文不是在这一步,而是在obtainFreshBeanFactory方法中)。
该方法由AbstractApplicationContext实现,调用BeanFactoryPostProcessor实现类的任务委派给了PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法完成。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 调用BeanFactoryPostProcessor接口及其子接口BeanDefinitionRegistryPostProcessor
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
BeanFactoryPostProcessor接口是独立于大名鼎鼎的BeanPostProcessor接口之外的一套接口。顾名思义是在BeanFactory实例化之后做一些事情。其子接口BeanDefinitionRegistryPostProcessor是用来向BeanFactory中注册BeanDefinition,该接口最重要的实现便是ConfigurationClassPostProcessor类。
这是IoC容器中最重要的类之一,因为在该类(ConfigurationClassPostProcessor)中完成了从包路径到IoC容器中的BeanDefinition的转换过程。
BeanFactoryPostProcessor中定义postProcessoBeanFactory方法,BeanDefinitionRegistryPostProcessor除了继承BeanFactoryPostProcessor的postProcessBeanFactory之外,还定义了自己的postProcessBeanDefinitionRegistry方法。
这里说一下这两个方法的调用时机,IoC容器是先调用postProcessBeanDefinitionRegistry方法再去调用postProcessBeanFactory方法。也就是说往IoC容器中注册BeanDefinition是早于对BeanFactory进行自定义操作的。这一点可以在PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法中得到体现。
在该方法中最先处理的BeanDefinitionRegistryPostProcessors实现类是调用该方法时传递的beanFactoryPostProcessors集合,那么这些实现类是从哪里获取的?
这些实现类是在创建Spring应用上下文时,通过调用Spring应用上下文的addBeanFactoryPostProcessors方法传递的。因此可以得知通过该方法添加的BeanFactoryPostProcessor实现类会最先被执行。
// 创建注解驱动Spring应用上下文
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
applicationContext.register(ApplicationConfig.class);
// 手动往Spring 应用上下文添加BeanFactoryPostProcessor接口实现类
applicationContext.addBeanFactoryPostProcessor(new MyBeanDefinitionRegistryPostProcessor());
applicationContext.refresh();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
// 判断beanFactory 是否是 BeanDefinitionRegistry类型的,DefaultListableBeanFactory实现了该接口
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 首先执行用户通过ConfigurableBeanFactory#addBeanPostProcessor方法添加的BeanFactoryPostProcessor实现类的postProcessBeanDefinitionRegistry方法
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
BeanDefinitionRegistryPostProcessor registryProcessor =
(BeanDefinitionRegistryPostProcessor) postProcessor;
registryProcessor.postProcessBeanDefinitionRegistry(registry);
registryProcessors.add(registryProcessor);
} else {
regularPostProcessors.add(postProcessor);
}
}
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 使用IoC容器来获取所有的 BeanDefinitionRegistryPostProcessor 接口实现类,通常获取到的只有ConfigurationClassPostProcessor
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// 判断获取到的BeanDefinitionRegistryPostProcessor接口实现类是否实现了PriorityOrdered接口 这涉及优先级问题 PriorityOrdered-> Ordered -> 未实现
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 执行 获取到实现了BeanDefinitionRegistryPostProcessor接口以及PriorityOrdered接口实现类的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 获取IoC容器中所有BeanDefinitionRegisterPostProcess接口实现类,然后判断是否实现了 Orderd接口 。为什么还要再次去IoC容器获取BeanDefinitionRegistryPostProcessor接口实现类,而不是使用前面获取到的?这是因为为了防止用户在BeanDefinitionRegistryPostProcessor接口实现类中又注册了BeanDefinitionRegistryPostProcessor接口实现类,这种情况
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
// 排序
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// 执行 获取到实现了BeanDefinitionRegistryPostProcessor接口以及Ordered接口实现类的postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
// 最后,获取未实现任何排序接口的BeanDefinitionRegistryPostProcessor接口实现类,并执行其postProcessBeanDefinitionRegistry方法
boolean reiterate = true;
while (reiterate) {
reiterate = false;
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (!processedBeans.contains(ppName)) {
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
reiterate = true;
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();
}
// 首先执行用户通过ConfigurableBeanFactory#addBeanPostProcessor方法添加的BeanFactoryPostProcessor实现类的postProcessBeanFactory方法
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else {
invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
// 处理BeanFactoryPostProcessor接口实现类和处理BeanDefinitionRegistryPostProcessor接口逻辑差不多,但不同的一点是因为BeanDefinitionRegistryPostProcessor接口实现类从理论上来说可以无限注册,因此使用while循环来处理这个问题(例如CustomizedBeanDefinitionRegistryPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,然后在其实现的postProcessBeanDefinitionRegistry方法注册了其它BeanDefinitionRegistryPostProcessor接口实现类的BeanDefinition,其它BeanDefinitionRegistryPostProcessor接口实现类又在其实现的postProcessBeanDefinitionRegistry方法中注册了其它BeanDefinitionRegistryPostProcessor接口实现类的BeanDefinition。。。类似于俄罗斯套娃),而BeanFactoryPostProcessor接口实现类没有这个问题,所以处理相对简单,
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (processedBeans.contains(ppName)) {
// skip - already processed in first phase above
} else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
} else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
} else {
nonOrderedPostProcessorNames.add(ppName);
}
}
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String postProcessorName : orderedPostProcessorNames) {
orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String postProcessorName : nonOrderedPostProcessorNames) {
nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
beanFactory.clearMetadataCache();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
执行完用户通过手动调用addBeanFactoryPostProcessor方法添加的BeanDefinitionRegistryPostProcessor实现类的postProcessorBeanDefinitionRegistry方法后,接下里便是获取IoC容器中所有实现BeanDefinitionRegistryPostProcessor接口的实现类的BeanName(包含IoC容器内部注册的)。
需注意的是,这一次获取只能获取到Spring应用上下文自己往IoC容器中添加的BeanDefinitionRegistryPostProcessos实现类,因为还没有开始进行包扫描,无法获取到用户通过@Component注解及其“派生注解”(@Controller、@Service、@Configuration等等)注册的BeanDefinitionRegistryPostProcessor接口实现类。然后遍历获取到的beanName,判断是否实现了PriorityOrderd接口,ConfigurationClassPostProcessor实现了该接口。
通常这里只能获取到一个-ConfigurationClassPostProcessor,接下来便是调用sortPostProcessors方法来进行排序(如果存在多个),之后便是调用invokeBeanDefinitionRegistryPostProcessor方法。将符合匹配条件并且排好序的currentRegistryProcessors集合以及BeanDefinitionRegistry(即DefaultListableBeanFactory)作为方法入参传递。
在invokeBeanDefinitionRegistryPostProcessor方法中,遍历传递的集合,逐个调用其postProcessBeanDefinitionRegistry方法,入参是BeanDefinitionRegistry。
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
// 循环调用所有BeanDefinitionRegistryPostProcessor 接口实现类的postProcessBeanDefinitionRegistry方法
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
这里执行的便是ConfigurationClassPostProcessor的postProcessBeanDefinitionRegisry方法,在该方法中只是进行了一些校验,最重要的是其调用processConfigBeanDefinitions方法,入参为BeanDefinitionRegistry。
// org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.factoriesPostProcessed.contains(registryId)) {
throw new IllegalStateException(
"postProcessBeanFactory already called on this post-processor against " + registry);
}
this.registriesPostProcessed.add(registryId);
// postProcessBeanDefinitionRegistry方法的重点是执行该方法
processConfigBeanDefinitions(registry);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
之所以说ConfigurationClassPostProcessor是IoC容器最重要的类之一,不如说其processConfigBean-Definitions方法才是最重要的。
在该方法中首先获取IoC容器所有的BeanDefinition的Name,在这里能获取的BeanName除了IoC容器内部注册的几个之外,便是用户创建IoC容器时指定的配置类的BeanName。
然后遍历获取到的BeanName,调用ConfigurationClassUtils的checkConfigurationClassCandidate方法,来判断是否是用户注册的配置类。
// org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 遍历BeanDefinitionRegistry现阶段已注册的所有BeanDefinition
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
// 判断是否是用户在创建IoC时注册的配置类。为什么加这个判断,主要是因为在该方法执行之前,Spring应用上下文往IoC容器中保存了很多BeanPostProcessor接口实现类,要过滤掉这些,找到用户注册的配置类。
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// 如果configCandidates集合为空,方法结束,这意味用户没有注册任何初始配置类
if (configCandidates.isEmpty()) {
return;
}
// 以上代码为节选...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
checkConfigurationClassCandidate判断逻辑很简单,可以简单读下。首先判断传递的BeanDefinition是否是AnnotatedBeanDefinition类型的,注解驱动的IoC容器对于用户注册的Bean,都是使用AnnotatedBeanDefinition来描述。IoC容器自己注册的Bean的BeanDeifinition都是RootBeanDefinition类型(父类型为AbstractBeanDefinition类型),因此会进入else if 分支,再通过调用getBeanClass方法获取Class后判断是否是BeanFactoryPostProcessor或者BeanPostProcessor或者AopInfrastructureBean或者EventListenerFactory类型。
IoC容器自己注册的Bean都是实现了这些接口中的某些接口,因此直接返回false。
在该方法的最后,判断用户注册的配置类是否添加了@Configuration注解,如果添加了该注解并且配置该注解的proxyBeanMethods属性为true(默认为true),那么则往该BeanDefinition中设置一个名为“org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass”的属性,value为“full”;
// org.springframework.context.annotation.ConfigurationClassUtils#checkConfigurationClassCandidate
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
String className = beanDef.getBeanClassName();
if (className == null || beanDef.getFactoryMethodName() != null) {
return false;
}
AnnotationMetadata metadata;
// 用户注册的初始配置类都是AnnotatedBeanDefinition 类型
if (beanDef instanceof AnnotatedBeanDefinition &&
className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
} else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) { // Spring应用上下文自己注册的内置BeanDefinition都是RootBeanDefinition
Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||
BeanPostProcessor.class.isAssignableFrom(beanClass) ||
AopInfrastructureBean.class.isAssignableFrom(beanClass) ||
EventListenerFactory.class.isAssignableFrom(beanClass)) {
return false;
}
metadata = AnnotationMetadata.introspect(beanClass);
} else {
// 删除与本次无关代码.....
}
// 删除与本次无关代码.....
return true;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
找到用户注册的配置类之后,将其添加到configCandidates集合中,遍历结束后,判断该集合是否为空,如果为空直接返回,相当于用户没有将任何初始配置类交给IoC容器处理。
如果不为空,接下来便是对这些配置类进行排序(用户可能注册多个配置类,通常是注册一个)。判断调用该方法时传递的BeanDefinitionRegistry(DefaultListableBeanFactory)是否是SingletonBeanRegistry类型的(DefaultListableBeanFactory实现了该接口),如果实现类该接口,判断是否已经设置了BeanName生成器。如果未设置,则设置BeanNameGenerator,通常这里通过调用getSingleton方法获取到的BeanNameGenerator都是null。
最后则是判断environment属性是否为null,如果为null则创建一个StandardEnvironment对象。
// ConfigurationClassPostProcessor#processConfigBeanDefinitions 方法片段
// 对configCandidates进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
});
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
sbr = (SingletonBeanRegistry) registry;
if (!this.localBeanNameGeneratorSet) { // 设置BeanName生成器->BeanNameGenerator
BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
if (generator != null) {
this.componentScanBeanNameGenerator = generator;
this.importBeanNameGenerator = generator;
}
}
}
// 如果环境对象为空,创建一个StandardEnvironment
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
以上只是一些在解析包路径前的准备工作,接下来才是真正开始解析用户在配置类中添加的@ComponentScan注解中指定的包路径。
首先创建一个ConfigurationClassParser对象,传入的构造函数参数有元数据读取器工厂,问题报告器,环境实例,资源加载器,组件名称生成器以及BeanDefinitionRegistry实例。需注意的是这里设置的BeanNameGenerator实例为AnnotationBeanNameGenerator。
/* Using short class names as default bean names by default. */
private BeanNameGenerator componentScanBeanNameGenerator = AnnotationBeanNameGenerator.INSTANCE;
- 1
- 2
然后调用创建好的ConfigurationClassParser的parse方法,传递的参数为前面准备好的用户往IoC容器注册的配置类,可能存在多个,因此是一个集合。这里使用的是do…while循环,循环结束的条件是该集合为空。
// 成员属性metadataReaderFactory
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
// ConfigurationClassPostProcessor的processConfigBeanDefinitions 方法片段
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates); // 执行ConfigurationClassParser#parse 重点!!!
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
// @Bean、实现ImportBeanDefinitionRegistry接口等导入的Bean都是在这里完成的解析 也是重点
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
在ConfigurationClassParser的parse方法中,遍历传递的BeanDefinitionHolder,获取BeanDefinition,然后判断BeanDefinition是否为AnnotatedBeanDefinition类型。对于用户注册的Bean,IoC使用的都是AnnotatedBeanDefinition来描述,所以判断成立,然后调用重载的parse方法。
// org.springframework.context.annotation.ConfigurationClassParser#parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) { // 通常执行这里
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
} else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
} else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
} catch (BeanDefinitionStoreException ex) {
throw ex;
} catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// 扩展一点:SpringBoot中的自动配置就是在这里解析的。
this.deferredImportSelectorHandler.process();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
重载的parse方法先将传递的AnnotationMetadata和BeanName包装成ConfigurationClass对象,然后调用processConfigurationClass方法。
这里解释下AnnotationMetadata是什么,以具体实现StandardAnnotationMetadata为例,通过类结构图可以看出其封装了Class中的添加的注解信息以及Class本身和一些用来获取当前类中注解信息的方法。
// org.springframework.context.annotation.ConfigurationClassParser#parse
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
private static final Predicate<String> DEFAULT_EXCLUSION_FILTER = className ->
(className.startsWith("java.lang.annotation.") || className.startsWith("org.springframework.stereotype."));
- 1
- 2
- 3
- 4
- 5
- 6
- 7
解释下ConfigurationClass是什么。通过ConfigurationClass的构造函数来看下创建该类实例时初始化了哪些属性。可以看到其AnnotationMetadata(方法入参)、Resource(根据Bean的全限定名创建DescriptiveResource实现类)和beanName属性被赋值。
通过ConfigurationClass的UML图可以看出没有实现任何接口。该类的主要作用是在IoC容器解析Class时用来封装一些数据,例如当前Class是否是被其它Class中的@Import注解所导入的、Class的简单名称、Class中添加了@Bean注解的方法等等,便于各个方法间传递。
public ConfigurationClass(AnnotationMetadata metadata, String beanName) {
Assert.notNull(beanName, "Bean name must not be null");
this.metadata = metadata;
this.resource = new DescriptiveResource(metadata.getClassName());
this.beanName = beanName;
}
- 1
- 2
- 3
- 4
- 5
- 6
在processConfigurationClass方法中,首先调用ConditionEvaluator的shouldSkip方法,来判断当前Class是否需要跳过。
ConditionEvaluator的shouldSkip方法判断逻辑很简单,如果传递的AnnotatedTypeMetadata中不包含@Conditional注解,则直接返回false,否则根据方法入参ConfigurationPhase来执行不同阶段条件装配,关于这部分内容,之后我会另写一片文章来介绍。
经过以上判断后,首先从configurationClasses这个Map缓存中根据入参configClass来获取,如果能获取到值,则判断获取到的ConfigurationClass是否是被导入的(即通过其他类中@Import注解导入),configurationClass这个缓存Map中存放的Key和Value都是ConfigurationClasse。
如果是被其他类导入的则结束。否则从该map中移除。最后调用doProcessConfigurationClass方法。
// org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
return;
}
ConfigurationClass existingClass = this.configurationClasses.get(configClass);
if (existingClass != null) {
if (configClass.isImported()) {
if (existingClass.isImported()) {
existingClass.mergeImportedBy(configClass);
}
return;
} else {
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
SourceClass sourceClass = asSourceClass(configClass, filter);
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
} while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
在调用doProcessConfiguration方法之前,IoC容器先根据ConfigurationClass和入参filter来调用asSourceClass方法创建SourceClass实例。
private SourceClass asSourceClass(ConfigurationClass configurationClass, Predicate<String> filter) throws IOException {
AnnotationMetadata metadata = configurationClass.getMetadata();
if (metadata instanceof StandardAnnotationMetadata) {
return asSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass(), filter);
}
return asSourceClass(metadata.getClassName(), filter);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
根据SourceClass的UML图可以看出,其也是用来封装在解析Class过程中的产生临时数据,例如Class所实现的接口、内部类、父类等等。
其成员属性AnnotationMetadata的赋值并不是直接使用ConfigurationClass中的metadata引用,而是根据ConfigurationClass中的metadata属性的getIntrospectedClass来获取解析的Class,然后根据该Class重新创建一个AnnotationMetadata。
// org.springframework.context.annotation.ConfigurationClassParser#asSourceClass
SourceClass asSourceClass(@Nullable Class<?> classType, Predicate<String> filter) throws IOException {
// 首先通过传递的filter来测试当前Class的全限定名是否是以"java.lang.annotation."或者以
//“org.springframework.stereotype.”开始。
if (classType == null || filter.test(classType.getName())) {
return this.objectSourceClass;
}
try {
for (Annotation ann : classType.getDeclaredAnnotations()) {
AnnotationUtils.validateAnnotation(ann);
}
return new SourceClass(classType);
}
catch (Throwable ex) {
// Enforce ASM via class name resolution
return asSourceClass(classType.getName(), filter);
}
}
// 根据Class来重新创建AnnotationMetadata实例
public SourceClass(Object source) {
this.source = source;
if (source instanceof Class) {
this.metadata = AnnotationMetadata.introspect((Class<?>) source);
} else {
this.metadata = ((MetadataReader) source).getAnnotationMetadata();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
在doProcessConfigurationClass方法中,首先获取ConfigurationClass的Metadata属性,判断当前类是否添加了@Component注解,如果添加了,则调用processMemberClasses方法。
通过AnnotationConfigUtils的attributeForRepatable方法来查找当前AnnotationMetadata中的@Prop-ertySources注解信息,如果存在该注解,则调用processPropertySource方法。
如上,通过AnnotationConfigUtils的attributeForRepatable方法来获取当前AnnotationMetadata中的@ComponentScans或者注解信息,如果获取到componentScans不为空,遍历该集合,通过Compo-nentScanAnnotationParser的parse方法来解析获取到的AnnotationAttribute(将@ComponentScan中配置的包路径下的Class转换为BeanDefinition就是在这里完成的)。
接下来便是处理Class中的@Import注解和@ImportResource注解。
调用retrieveBeanMethodMetadata方法来获取当前Class中的添加@Bean注解的方法,如果存在将其添加到ConfigurationClass的beanMethods属性中。
调用processInterfaces方法来处理当前Class所实现的接口。
// org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 如果当前Class中存在@Component注解,则调用processMemberClasses方法 处理内部类
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
processMemberClasses(configClass, sourceClass, filter);
}
// 处理当前Class中所有的@PropertySource注解
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
// 处理当前Class中@ComponentScan或者@ComponentScans注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { // 这里又通过ConditionEvaluator实例的shouldSkip方法来判断Class中的@Conditional条件装配的时机是否成熟
for (AnnotationAttributes componentScan : componentScans) {
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // 通过ComponentScanParser的parse方法来解析配置类,主线!!!
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());// 对于通过@ComponentScan注解扫描到的配置类都会递归调用parse方法,为什么要这么处理,下一篇文章会有相关分析
}
}
}
}
//处理所有@Import注解
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// 处理所有@ImportResource注解
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
String[] resources = importResource.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
configClass.addImportedResource(resolvedResource, readerClass);
}
}
// 如果当前Class中存在添加@Bean注解的方法,将其添加到ConfigurationClass的beanMethods属性中
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// 当前当前Class实现的接口中的默认方法上的@Bean注解,系列文章中会分析
processInterfaces(configClass, sourceClass);
// 当前类如果有父类,处理父类
if (sourceClass.getMetadata().hasSuperClass()) {
String superclass = sourceClass.getMetadata().getSuperClassName();
if (superclass != null && !superclass.startsWith("java") &&
!this.knownSuperclasses.containsKey(superclass)) {
this.knownSuperclasses.put(superclass, configClass);
// Superclass found, return its annotation metadata and recurse
return sourceClass.getSuperClass();
}
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
在processMemberClasses方法中,首先通过SourceClass的getMemberClass方法来获取当前类的内部类,由于可能存在多个内部类,所以是一个集合。
如果集合不为空,遍历该集合取出每一个内部类(这里已经转换为SourceClass),然后调用Configura-tionClassUtils的isConfigurationCandidate方法来判断是否是一个需要受IoC容器来管理的Class。如果是一个需要受IoC容器管理的Class,添加到candidates集合中。
最后遍历candidates集合,判断是否已经被处理过(即当前实例的importStack属性中包不包含该SourceClass,ImportStack是一个双端队列,其继承于JDK中的ArrayDeque),如果已经被处理过则向问题报告器中添加一个循环导入问题,否则回调processConfigurationClass方法,最后从队列中移除。
private final ImportStack importStack = new ImportStack();
private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
Predicate<String> filter) throws IOException {
// 获取到外部类的内部类信息封装为-> SourceClass
Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
if (!memberClasses.isEmpty()) {
List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
for (SourceClass memberClass : memberClasses) {// 外部类中可能存在多个内部类,因此循环处理
if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
candidates.add(memberClass);
}
}
OrderComparator.sort(candidates);
for (SourceClass candidate : candidates) {
if (this.importStack.contains(configClass)) {
this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
} else {
this.importStack.push(configClass);
try { // 继续调用processConfigurationClass方法
processConfigurationClass(candidate.asConfigClass(configClass), filter);
}
finally {
this.importStack.pop();
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
ConfigurationClassUtils的isConfigurationCandidate方法在这里有必要讲一下,因为该方法很多处都有使用到,该方法的作用就是根据入参AnnotationMetadata来判断是否是一个需要受IoC容器管理的Class。
其判断逻辑也很简单,首先判断当前Class是否是一个接口,如果是,直接返回false。否则遍历其成员属性candidateIndicators,然后判断Class中是否添加了这些注解。candidateIndicators(集合)中的注解全限定名是在静态代码块中添加的,IoC容器往其中添加了@Component、@ComponentScan、@Import、@ImportResource这四个注解的全限定名。
如果类中未添加以上四个注解,那么最后判断当前类中是否存在@Bean注解。
因此可以得出结论,如果需要让一个Class受IoC容器管理,那么在类上添加@Component或@ComponentScan或@Import或@ImportResource注解中任何一个即可。如果以上四个注解中的任意一个都没有使用,那么在类中的方法上添加@Bean注解也可以,该类也是会受IoC容器管理。
// org.springframework.context.annotation.ConfigurationClassUtils#isConfigurationCandidate
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
if (metadata.isInterface()) {
return false;
}
// 是否存在@Component或@ComponentScan或@Import或@ImportResource注解
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
try { // 类注解元数据中是否存在@Bean注解
return metadata.hasAnnotatedMethods(Bean.class.getName());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
}
return false;
}
}
private static final Set<String> candidateIndicators = new HashSet<>(8);
// 在静态代码块中向candidateIndicators这个集合中,添加@Component、@ComponentScan、@Import、@ImportResource这四个注解的全限定名
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
SourceClass的getMemberClass方法实现其实就是通过JDK提供的getDeclaredClasses方法来获取当前类中定义的内部类,如果获取失败,那么则通过ASM(读取字节码)来获取当前类中定义的内部类。通过调用asSource方法来将Class转换为SourceClass。
//org.springframework.context.annotation.ConfigurationClassParser.SourceClass#getMemberClasses
public Collection<SourceClass> getMemberClasses() throws IOException {
Object sourceToProcess = this.source;
if (sourceToProcess instanceof Class) {
Class<?> sourceClass = (Class<?>) sourceToProcess;
try {
// 首先使用JVM提供反射来获取内部类
Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
List<SourceClass> members = new ArrayList<>(declaredClasses.length);
for (Class<?> declaredClass : declaredClasses) {
members.add(asSourceClass(declaredClass, DEFAULT_EXCLUSION_FILTER));
}
return members;
}
catch (NoClassDefFoundError err) {
// 如果获取失败,则使用ASM读取字节码获取内部类数据
sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());
}
}
MetadataReader sourceReader = (MetadataReader) sourceToProcess;
String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
List<SourceClass> members = new ArrayList<>(memberClassNames.length);
for (String memberClassName : memberClassNames) {
try {
members.add(asSourceClass(memberClassName, DEFAULT_EXCLUSION_FILTER));
}
catch (IOException ex) {
// Let's skip it if it's not resolvable - we're just looking for candidates
if (logger.isDebugEnabled()) {
logger.debug("Failed to resolve member class [" + memberClassName +
"] - not considering it as a configuration class candidate");
}
}
}
return members;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
回到主线,在ConfigurationClassParser的doProcessConfigurationClass方法中,通过ComponentScanAnnotationParser的parse方法来解析从Class中获取到@ComponentScan注解。
在parse方法中,首先根据@ComponentScan注解的属性useDefaultFilters值来创建一个ClassPathBeanDefinitionScanner。然后就是获取其他属性值来配置该扫描器,例如获取用户在@ComponScan注解的includeFilter属性值设置到扫描器中等等。
顺便说下MyBatis和Spring整合包中对Dao层包路径进行扫描的扫描器-ClassPathMapperScanner便是继承自该类。
最后调用ClassPathBeanDefinitionScanner的doScan方法。
// org.springframework.context.annotation.ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
// 解析@ComponentScan注解的nameGenerator()方法返回值
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
// 解析@ComponentScan注解的nameGenerator()方法返回值
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
// 解析@ComponentScan注解的scopeResolver()方法返回值
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
// 解析@ComponentScan注解的resourcePattern()方法返回值
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
// 解析@ComponentScan注解的includeFilters()方法返回值
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
// 解析@ComponentScan注解的excludeFilters()方法返回值
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
// 解析@ComponentScan注解的lazyInit()方法返回值
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
// 解析@ComponentScan注解的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);
}
// 解析@ComponentScan注解的basePackageClasses()方法返回值
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
// 如果用户未在配置类中的@ComponentScan注解的basePackages或basePackageClasses配置任何数据,那么默认以配置类所在的包路径作为资源扫描路径这也是为什么添加了@SpringBootApplication注解的类要放在最外面的原因。
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));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
在ClassPathBeanDefinitionScanner的doScan方法中,遍历用户配置的包扫描路径(@ComponentScan的basePackages的值是一个数组),对于每一个包扫描路径都是通过调用findCacndidateComponents来获取指定包路径上资源并构造成BeanDefinition返回。
然后遍历这些根据指定包路径下的用户交由IoC容器管理并且符合IoC容器加载规范(什么是IoC容器加载规范在接下来的源码中可以看到)的Class生成的BeanDefinition,通过BeanNameGenerator来生成BeanName,默认是将类名的首字母小写。
然后判断BeanDefinition是否是AbstractBeanDefinition类型,如果是则调用postProcessBeanDefinition方法;是否是AnnotatedBeanDefinition类型,如果是则调用AnnotationConfigUtils的processCommonDefinitionAnnotations方法。
最后通过调用registerBeanDefinition方法将BeanDefinition注册到IoC容器中。
// org.springframework.context.annotation.ClassPathBeanDefinitionScanner#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) {
// 本次分析重点:如何根据包扫描路径加载指定类路径下的资源->findCandidateComponents
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);// 根据BeanDefinition中@Scope注解生成->ScopeMetadata
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 使用BeanNameGenerator来生成beanName
if (candidate instanceof AbstractBeanDefinition) { // 通常判断成立在该方法中主要是给BeanDefinition的部分属性赋一些默认值
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);// 处理通用注解,例如@Lazy、@Primary、@DependsOn、@Role、@Description
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); // 这里是解析@Scope注解,有兴趣的同学可以查看我的另一篇博文-> 《@Scope注解的proxyMode的作用以及如何影响IoC容器的依赖查找》 那里会详细分析。
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
先从findCandidateComponents方法看起,这个方法并不是ClassPathBeanDefinitionScanner定义的方法,而是在其父类ClassPathScanningCandidateComponentProvider中定义并实现。在findCacndidateComponents方法中,首先判断用户是否使用了索引(主要是为了解决在运行期Spring 应用上下文去扫描包路径下的资源比较耗时,使用索引在编译期就可以确定那些类是用户交给Spring管理的,从而减少运行期的启动时间)。
Spring索引官方文档
// ClassPathScanningCandidateComponentProvider#findCandidateComponents
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) { // 使用索引
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
} else { // 未使用索引
return scanCandidateComponents(basePackage);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
通常是没有使用索引的,因此执行scanCandidateComponents方法。在该方法中先通过resolverBas-ePackage方法将用户指定的的包路径中的“.”替换为盘符("/"),然后在前面拼接上“classpath*:”前缀,在后面拼接上“**/*.class”后缀。
以指定的包路径“com.zhang.spring”为例,经过以上替换和拼接后,新的包搜索路径为“classpath*:com/zhang/spring/**/*.class”。
之后便是通过调用getResourcePatternResolver()方法来获取资源解析器(这里获取到的是Annotation-ConfigApplicationContext,该类实现了ResourcePatternResolver接口),然后调用其getResources方法,传入拼接好的包路径,返回处理好的Resource数组。
遍历Resource数组,判断该资源是否可读,如果是可读的通过MetadataReaderFactory的getMetada-taReader方法来根据Resource对象来获取一个MetadataReader。
把一个Bean交给IoC容器管理需要经过两步。第一步是用户在需要交给IoC容器管理的类上添加指定的注解(这里指由IoC容器从磁盘读取Class然后到内存中构建对应的BeanDefinition注册到IoC容器,不假他手。当然也可以使用其它手段来注册,例如用户自己创建BeanDefinition然后注册到IoC容器中也是可以的)。第二步便是在这里完成的,就是这两次的isCandidateComponent方法调用,这是两个重载方法,这两次isCandidateComponent方法调用究竟是判断什么,在之后会详讲。
经过这两次判断的Resource,才会被添加到返回值candidates集合中。
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;
// org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
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); // 对于磁盘上所有的Class资源以Resource对象描述
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); // 使用ASM来通过解析字节码获取类元数据,这里提出一个问题,Spring为什么不通过反射来获取类元数据,而是通过ASM读取字节码这种方式?
if (isCandidateComponent(metadataReader)) { // 判断是否是一个合适的组件
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) { // 这次是根据BeanDefinition来判断是否是一个合适的组件
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
先看下getResource方法是如何根据指定包路径来加载Class文件的。AnnotationConfigApplicationContext并未实现该方法,而是由其父类GenericApplicationContext实现。
在GenericApplicationContext实现的getResources方法中,首先判断自己的resourceLoader属性是否是ResourcePatternResolver类型,这个判断通常为false,因为该属性为null。然后调用父类的getResources方法。
// org.springframework.context.support.GenericApplicationContext#getResources
public Resource[] getResources(String locationPattern) throws IOException {
if (this.resourceLoader instanceof ResourcePatternResolver) {
return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
}
return super.getResources(locationPattern);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在父类AbstractApplicationContext的getResources方法,通过调用私有属性ResourcePatternResolver的getResources方法来完成资源加载。这里使用的是PathMatchingResourcePatternResolver。
private ResourcePatternResolver resourcePatternResolver;
// 在构造函数中直接为resourcePatternResolver赋值
public AbstractApplicationContext() {
this.resourcePatternResolver = getResourcePatternResolver();
}
protected ResourcePatternResolver getResourcePatternResolver() {
return new PathMatchingResourcePatternResolver(this);
}
// org.springframework.context.support.AbstractApplicationContext#getResources
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
因此这里调用的便是PathMatchingPatternResolver的getResources方法。在该方法中,首先判断要搜索的资源路径是否是以“classpath:*”开始。因为在前面的scanCandidateComponents方法中已经拼接上了该前缀,因此判断成立。
调用getPathMatcher方法来获取路径匹配器PathMatcher,先将前缀“classpath*:“截取掉,然后调用其isPattern方法。
这里使用到的路径匹配器为AntPathMatcher, 之所以能够匹配成功,是因为其匹配规则就是判断给定的路径中是否包含 “” 或 “?”或成对出现的 “{” 和 “}”。通常情况下,第一次调用该方法时该判断成立(注意是第一次调用时,因为IoC容器拼接的后缀为”**/.class“),调用findPathMatchingResources方法。
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
// org.springframework.core.io.support.PathMatchingResourcePatternResolver#getResources
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // 判断资源路径是否是以“classpath*:”开始
// 第一次会执行这个分支,其判断逻辑便是截取掉classpath*:前缀后,资源路径名中是否还包含“*”或者“?” 或者存在成对出现“{”和“}”。
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
return findPathMatchingResources(locationPattern);
}
else {
// 第二次进入该方法会执行这里,因为截取掉了后缀“**/*.class”
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
} else {
int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
locationPattern.indexOf(':') + 1);
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
} else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
// org.springframework.util.AntPathMatcher#isPattern
public boolean isPattern(@Nullable String path) {
if (path == null) {
return false;
}
boolean uriVar = false;
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c == '*' || c == '?') {
return true;
}
if (c == '{') {
uriVar = true;
continue;
}
if (c == '}' && uriVar) {
return true;
}
}
return false;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
在findPathMatchingResources方法中,首先通过调用determineRootDir方法来确定要扫描的资源路径,其实在这里就是将前面拼接的“/.class”后缀去截取掉。以“classpath:com/zhang/esign//.class”资源路径为例,经过determineRootDir处理后,得到的rootDir便为 “classpath:com/zhang/spring/”。得到的subPattern便为“**/*.class”。
然后回调getResources方法,传入的localtionPattern为处理过后的“classpath*:com/zhang/spring”。而这一次的调用PathMatcher的isPattern方法将不会成立,因为传入的资源路径已经被处理过后缀。进入else分支,执行findAllClassPathResources方法,在执行该方法前会把IoC容器拼接的前缀截取掉,例如“classpath*:com/zhang/spring”被截取为“com/zhang/spring”。
// org.springframework.core.io.support.PathMatchingResourcePatternResolver#findPathMatchingResources
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
//在这里回调getResources方法传入的路径为处理过后缀的路径,例如"classpath*:com/zhang/spring"
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
} else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
}else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
在findAllClassPathResources方法中,首先判断传入的资源路径是否以"/"开始,如果是以”/“开始,先截取掉。然后调用doFindAllClassPathResources方法,传入处理好的资源路径。
可以发现,兜兜转转之后,最终使用的扫描路径还是用户在@ComponentScan注解的basePackages属性中指定的包路径,只是将“.”替换为了“/”。
//org.springframework.core.io.support.PathMatchingResourcePatternResolver#findAllClassPathResources
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
// 这里的资源路径就变为了"com/zhang/spring",是用户在@ComponentScan注解中配置的包路径(替换"."之后)
Set<Resource> result = doFindAllClassPathResources(path);
if (logger.isTraceEnabled()) {
logger.trace("Resolved classpath location [" + location + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
在doFindAllClassPathResources方法中,IoC容器使用类加载器的getResources方法来获取指定相对路径的绝对路径。例如传入的相对路径为”com/zhang/spring“,返回的便是”file:/D:/…/com/zhang/s-pring/“。
将获取到的绝对路径转换为Spring自己的Resource对象,这里使用的是UrlResource,然后返回。
protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
Set<Resource> result = new LinkedHashSet<>(16);
ClassLoader cl = getClassLoader();
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
while (resourceUrls.hasMoreElements()) {
URL url = resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
if ("".equals(path)) {
addAllClassLoaderJarRoots(cl, result);
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
doFindAllClassPathResources方法出栈,getResources方法出栈,返回到调用帧findPathMatchingResources中。
遍历获取到的Resource数组,获取Resource对象封装的URL, 然后判断URL的协议,前面通过ClassLoader根据相对路径获取的绝对路径为文件协议,即”file“。因此会进入else分支,执行doFindPathMatchingFileResources方法,传入的参数为当前Resource实例,和子匹配模式(**/*.class)。
可以看到IoC支持多种的资源加载方法,例如”bundle“、”vfs“、”jar“。
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
rootDirResource = resolveRootDirResource(rootDirResource);
URL rootDirUrl = rootDirResource.getURL();
if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
if (resolvedUrl != null) {
rootDirUrl = resolvedUrl;
}
rootDirResource = new UrlResource(rootDirUrl);
}
if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
} else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
} else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isTraceEnabled()) {
logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return result.toArray(new Resource[0]);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
在doFindPathMatchingFileResources方法中,首先通过UrlResource对象的getFile方法获取到JDK中的File对象,然后调用其getAbsoluteFile方法获取到根文件对象,通常是一个文件夹,因为用户在@Co-mponentScan注解的basePackages属性中指定的是包路径。
最后调用doFindMatchingFileSystemResources方法。
// PathMatchingResourcePatternResolver#doFindPathMatchingFileResources
protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
throws IOException {
File rootDir;
try {
rootDir = rootDirResource.getFile().getAbsoluteFile();
} catch (FileNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot search for matching files underneath " + rootDirResource +
" in the file system: " + ex.getMessage());
}
return Collections.emptySet();
} catch (Exception ex) {
if (logger.isInfoEnabled()) {
logger.info("Failed to resolve " + rootDirResource + " in the file system: " + ex);
}
return Collections.emptySet();
}
return doFindMatchingFileSystemResources(rootDir, subPattern);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
在doFindMatchingFileSystemResources方法中,通过调用retrieveMatchingFiles方法来获取根文件夹下(包含子文件下)的所有后缀名为".class"的文件。
遍历获取到的File数组,构造成FileSystemResource对象添加到返回值result集合中返回。
// PathMatchingResourcePatternResolver#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 = retrieveMatchingFiles(rootDir, subPattern);
Set<Resource> result = new LinkedHashSet<>(matchingFiles.size());
for (File file : matchingFiles) {
result.add(new FileSystemResource(file));
}
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在retrieveMatchingFiles方法中,首先判断根文件File对象是否存在、是否是文件夹、是否是可读的,如果这三项中的任何一项未满足,直接结束。
然后再对根文件File对象的路径进行拼接,例如根文件对象的Path是以"D:“开始,拼接成“/D:”,例如在根文件的Path拼接上”**/*.class"。
经过一系列处理后,最后调用doRetrieveMatchingFiles方法,传入拼接好的路径以及根文件对象和结果集Set集合。
// PathMatchingResourcePatternResolver#retrieveMatchingFiles
protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
if (!rootDir.exists()) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist");
}
return Collections.emptySet();
}
if (!rootDir.isDirectory()) {
if (logger.isInfoEnabled()) {
logger.info("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory");
}
return Collections.emptySet();
}
if (!rootDir.canRead()) {
if (logger.isInfoEnabled()) {
logger.info("Skipping search for matching files underneath directory [" + rootDir.getAbsolutePath() +
"] because the application is not allowed to read the directory");
}
return Collections.emptySet();
}
String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
if (!pattern.startsWith("/")) {
fullPattern += "/";
}
fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
Set<File> result = new LinkedHashSet<>(8);
doRetrieveMatchingFiles(fullPattern, rootDir, result);
return result;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
在doRetrieveMatchingFiles方法中,通过根文件夹获取到其下的子文件夹以及子文件然后遍历,对于遍历的每一个文件夹和文件都会和传入的fullPath进行匹配,如果匹配通过才会进行相应的处理。例如如果是文件夹则递归调用方法本身,如果是文件则加入到传入的result集合中。
经过这一系列处理后,指定包路径下的所有.class文件都被加载到内存中(这里是所有.class文件,还没有筛选掉哪些不需要受IoC容器管理的类)。
// PathMatchingResourcePatternResolver#doRetrieveMatchingFiles
protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
if (logger.isTraceEnabled()) {
logger.trace("Searching directory [" + dir.getAbsolutePath() +
"] for files matching pattern [" + fullPattern + "]");
}
for (File content : listDirectory(dir)) {
String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
if (!content.canRead()) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
"] because the application is not allowed to read the directory");
}
} else {
// 如果是文件夹 递归
doRetrieveMatchingFiles(fullPattern, content, result);
}
}
if (getPathMatcher().match(fullPattern, currPath)) {
result.add(content);
}
}
}
// PathMatchingResourcePatternResolver#listDirectory
protected File[] listDirectory(File dir) {
File[] files = dir.listFiles();
if (files == null) {
if (logger.isInfoEnabled()) {
logger.info("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
}
return new File[0];
}
Arrays.sort(files, Comparator.comparing(File::getName));
return files;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
获取到指定包路径下所有后缀名为".class"文件后,doRetrieveMatchingFiles方法出栈,retrieveMatc-hingFiles方法出栈,doFindMatchingFileSystemResources方法将扫描的File对象集合遍历构造成File-SystemResource对象出栈,doFindMatchingFileResources方法出栈,findPathMatchingResources方法将Resource集合转换为数组后出栈,getResources方法出栈,最后返回到调用帧scanCandidateCo-mponents方法中。
之前提到的scanCandidateComponents方法中的两次isCandidateComponent方法调用,在这里详细阅读一下。
首先是根据MetadataReaderFactory的getMetadataReader方法来获取MetadataReader对象,传入的参数为Resource对象。在根据MetadataReaderFactory来获取MetadataReader对象时,Spring使用了ASM技术,主要是通过解析字节码来获取Class的一些信息,例如当前Class的父类是谁、当前Class实现了哪些接口、当前Class的全限定名是什么、当前Class的访问标志是什么等等,这里就不展开详讲了,感兴趣的同学可以阅读下ClassReader#accept方法。
// ClassPathScanningCandidateComponentProvider#scanCandidateComponents 方法片段
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
第一次筛选是否是一个需要受IoC管理的Bean,是根据MetadataReader来判断的。这一次的判断是根据用户在@ComponentScan注解中配置的exclude属性和include属性来过滤。
可以看到最先匹配的条件的exclude,再匹配的include。这意味着如果某些或者某个Bean满足Exclud-e中的条件,即使又满足include中的条件,也依然不会被添加到IoC容器中,因为只要满足exclude就直接返回false了。
// ClassPathScanningCandidateComponentProvider#isCandidateComponent(org.springframework.core.type.classreading.MetadataReader)
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;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
如果只配置了@ComponentScan注解的basePackages属性,未修改其useDefaultFilters属性,那么会发现在这一次的isCandidateComponent方法中便会将那些未添加Spring或者JDK中提供的用来声明依赖关系的注解的类给过滤掉(includeFilters集合中只包含@Component和@ManageBean,这代表没有添加这两个注解的Class都会被过滤掉)。
这是因为在之前的ComponentScanAnnotationParser的parse方法中创建ClassPathBeanDefinitionSca-nner时,传递的构造参数useDefaultFilters的值是从用户声明的@ComponentScan注解中获取到,该属性默认为true。
可以看下ClassPathBeanDefinitionScanner的构造函数,在其构造函数内,判断传递的useDefaultFilt-ers是否为true,如果判断成立,则执行registerDefaultFilters方法。该方法定义在其父类ClassPathSc-anningCandidateComponentProvider中,在该方法中往includeFilters中添加了@Component注解,以及JDK中的ManagedBean和Named注解。
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);
}
// ClassPathScanningCandidateComponentProvider#registerDefaultFilters
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.
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
在这次遍历includeFilters集合时,还完成了另一件事情,那就是对于Class中@Conditional注解的解析。这个解析是在isConditionMatch方法中完成的。
在isConditionMatch方法中通过调用ConditionEvaluator的shouldSkip方法来完成的。需注意的是这次调用的shouldSkip方法和在ConfigurationClassParser中的processConfigurationClass方法中调用的shouldSkip方法名一致,但参数列表不一样。但最终调用的都是同一个shouldSkip方法。
// ClassPathScanningCandidateComponentProvider#isConditionMatch
private boolean isConditionMatch(MetadataReader metadataReader) {
if (this.conditionEvaluator == null) {
this.conditionEvaluator =
new ConditionEvaluator(getRegistry(), this.environment, this.resourcePatternResolver);
}
return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
在这个只需要传递AnnotatedTypeMetadata参数的shouldSkip方法中,直接委派给了两个参数的sho-uldSkip方法(这里就不再展开讲解@Conditional注解是如何被解析的,因为篇幅已经太大了)。
// ConditionEvaluator#shouldSkip(org.springframework.core.type.AnnotatedTypeMetadata)
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
return shouldSkip(metadata, null);
}
- 1
- 2
- 3
- 4
第二次的isCandidateComponent方法调用,是先通过MetadataReader来构造出ScannerGenericBea-nDefinition然后调用。
这一次的判断条件是,当前Class是否是一个独立的(即非内部类)并且是一个具体的(即非抽象的)或者是抽象类但是类中的方法添加了@Lookup注解(@Lookup注解可以用来解决在单例Bean中引用原型B-ean,因为单例Bean只被实例化一次,所以原型Bean也变成了单例Bean的问题。具体如何使用请参考官网文档)。
// ClassPathScanningCandidateComponentProvider#isCandidateComponent
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
- 1
- 2
- 3
- 4
- 5
- 6
通过这两次判断的BeanDefinition才会被加入到返回值集合-candidates中。
scanCandidateComponents方法出栈,findCandidateComponents方法出栈,返回到ClassPathBean-DefinitionScanner的doScan方法帧中,遍历获取到的BeanDefinition。
对于遍历到的每一个BeanDefinition,首先通过ScopeMetadataResolver的resolveScopeMetadata方法来获取其作用域,然后设置到BeanDefinition中。
通过BeanNameGenerator来生成BeanName设置到BeanDefinition中。
判断BeanDefinition是否为AbstractBeanDefinition类型,在之前的scanCandidateComponents方法中创建的BeanDefinition为ScannedGenericBeanDefinition,该类型继承于AbstractBeanDefinition,实现了AnnotatedBeanDefinition。
因此该判断和接下来的是否是AnnotatedBeanDefinition类型的判断都成立,分别执行postProcessBe-anDefinition和AnnotationConfigUtils的processCommonDefinitionAnnotations方法。
如果checkCandidate方法返回为true,那么根据当前BeanDefinition来创建BeanDefinitionHolder,调用AnnotationConfigUtils的applyScopeProxyModel方法来设置代理模型(通过解析类中添加的@Scop-e注解的proxyModel属性),最后调用registerBeanDefinition方法注册到IoC容器中
// ClassPathBeanDefinitionScanner#doScan方法片段
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里使用的是AnnotationScopeMetadataResolver,在其reolveScopeMetadata方法中,首先创建一个ScopeMetadata,在ScopeMetadata类中,scopeName的默认值是singleton,scopeProxyModel的默认值是NO。
然后通过AnnotationConfigUtils的attributesFor方法来获取类中@Scope注解,如果能获取到,将获取到的注解属性值value和proxyModel设置到ScopeMetadata中,最后返回。
protected Class<? extends Annotation> scopeAnnotationType = Scope.class;
// AnnotationScopeMetadataResolver#resolveScopeMetadata
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
if (definition instanceof AnnotatedBeanDefinition) {
AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
annDef.getMetadata(), this.scopeAnnotationType);
if (attributes != null) {
metadata.setScopeName(attributes.getString("value"));
ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
if (proxyMode == ScopedProxyMode.DEFAULT) {
proxyMode = this.defaultProxyMode;
}
metadata.setScopedProxyMode(proxyMode);
}
}
return metadata;
}
/**
* ScopeMetadata
*
**/
public class ScopeMetadata {
private String scopeName = BeanDefinition.SCOPE_SINGLETON;
private ScopedProxyMode scopedProxyMode = ScopedProxyMode.NO;
public void setScopeName(String scopeName) {
Assert.notNull(scopeName, "'scopeName' must not be null");
this.scopeName = scopeName;
}
public String getScopeName() {
return this.scopeName;
}
public void setScopedProxyMode(ScopedProxyMode scopedProxyMode) {
Assert.notNull(scopedProxyMode, "'scopedProxyMode' must not be null");
this.scopedProxyMode = scopedProxyMode;
}
public ScopedProxyMode getScopedProxyMode() {
return this.scopedProxyMode;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
在postProcessBeanDefiniton方法中,首先调用BeanDefinition自己的applyDefault方法,传入BeanD-efinitionDefaults实例。BeanDefinitionDefaults可以看作是一个默认模版,定义BeanDefinition的默认配置。
在AbstractBeanDefinition类中的applyDefaults方法中,为当前BeanDefinition设置是否懒加载、依赖注入模型(默认为AUTOWIRE_NO)、依赖检查、初始化方法名称(默认为null)、是否强制要有初始化方法(默认为false)、销毁方法名称(默认为null)、是否强制要有销毁方法(默认为false)。
执行完BeanDefinition自己的applyDdefaults方法后,接下来便是判断autowireCandidatePatterns属性是否为null,默认为null,所以不会执行BeanDefinition的setAutowireCandidate方法。
// ClassPathBeanDefinitionScanner#postProcessBeanDefinition
protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
beanDefinition.applyDefaults(this.beanDefinitionDefaults);
if (this.autowireCandidatePatterns != null) {
beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
}
}
// AbstractBeanDefinition#applyDefaults
public void applyDefaults(BeanDefinitionDefaults defaults) {
Boolean lazyInit = defaults.getLazyInit();
if (lazyInit != null) {
setLazyInit(lazyInit);
}
setAutowireMode(defaults.getAutowireMode());
setDependencyCheck(defaults.getDependencyCheck());
setInitMethodName(defaults.getInitMethodName());
setEnforceInitMethod(false);
setDestroyMethodName(defaults.getDestroyMethodName());
setEnforceDestroyMethod(false);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCJubDqx-1609491657067)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a4b3e7bf-dfd0-4e6d-b83f-61ad3fd4db0d/Untitled.png)]
在AnnotationConfigUtils的processCommonDefinitionAnnotations方法中,直接调用重载方法proce-ssCommonDefinitionAnnotations方法。
在该方法中首先从AnnotatedTypeMetadata获取@Lazy注解属性值,如果获取到的属性值不为空,则将用户添加的@Lazy注解的属性值设置到BeanDefinition的lazyInit属性中。
判断类中是否添加了@Primary注解,如果添加了,则设置BeanDefinition的primary属性为true;
获取用户配置的@DependsOn注解的属性值,如果不为空,则设置BeanDefinition的dependsOn属性。
获取@Role注解的属性值,如果不为空,则设置BeanDefinition的role属性;
最后获取@Description注解属性值,如果不为空,则设置BeanDefinition的的description属性。
以上注解属性值都是从注解元数据-AnnotatedTypeMetadata中(每一个Class都有对应的一个实例,其创建时机在前面贴出的scanCandidateComponents方法中,通过ASM来解析字节码获取类的元数据)获取。
// AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition)
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
processCommonDefinitionAnnotations(abd, abd.getMetadata());
}
// AnnotationConfigUtils#processCommonDefinitionAnnotations(org.springframework.beans.factory.annotation.AnnotatedBeanDefinition, org.springframework.core.type.AnnotatedTypeMetadata)
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"));
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
checkCandidate方法的主要是判断当前IoC容器中是否存在当前BeanName的BeanDefinition,如果不存在直接返回true。如果存在重名的BeanDefinition,先根据BeanName获取已注册进IoC容器中的B-eanDefinition,然后调用isCompatible方法来判断已注册的和新构建的BeanDefinition的是否兼容,如果兼容返回true,意味着新构建的BeanDefinition不会被注册进IoC容器中,如果不兼容抛出异常。
// ClassPathBeanDefinitionScanner#checkCandidate
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {
if (!this.registry.containsBeanDefinition(beanName)) {
return true;
}
BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);
BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();
if (originatingDef != null) {
existingDef = originatingDef;
}
if (isCompatible(beanDefinition, existingDef)) {
return false;
}
throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +
"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +
"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
在isCompatible方法中,判断已注册的BeanDefinition类型不是ScannedGenericBeanDefinition或者新构建的BeanDefinition的来源(getSource())是否与已存在的BeanDefinition的来源一致或者新构建的B-eanDefinition等于已存在的BeanDefinition。如果这三种条件有一项满足,该方法则返回true。
// ClassPathBeanDefinitionScanner#isCompatible
protected boolean isCompatible(BeanDefinition newDefinition, BeanDefinition existingDefinition) {
return (!(existingDefinition instanceof ScannedGenericBeanDefinition) || // explicitly registered overriding bean
(newDefinition.getSource() != null && newDefinition.getSource().equals(existingDefinition.getSource())) || // scanned same file twice
newDefinition.equals(existingDefinition)); // scanned equivalent class twice
}
- 1
- 2
- 3
- 4
- 5
- 6
通过该方法的判断逻辑我们可以得出这样一个结论,如果有两个同名的类,即使分别处在不同包下,如果未指定beanName,而是由IoC容器来生成默认的beanName,那么将会抛出异常。因为IoC容器默认的beanName生成策略是将类名首字母小写,这样不同包下的相同名字的类会得到相同的bean-Name,而由于它们的BeanDefinition信息不一样,从而导致IoC容器抛出异常,注册失败。
结束语
本篇分析到这里就结束了,由于该篇笔记是笔者在几个月前在笔记软件上写的,图片粘贴到CSDN失败,所以部分图片是现补,会出现Idea的背景图片不一样,如果给您带来不适,还望见谅,谢谢!
能看到最后的同学都是不甘于只会使用Spring,而是期望了解Spring是怎么运行的,如何去扩展它,仅希望该系列的笔记能让您在阅读相关源码时容易一些,在这里让我们一起学习,一起成长,一起变的更强,一起面向RMB编程!
原文章:
Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?的更多相关文章
- Spring源码情操陶冶-AnnotationConfigBeanDefinitionParser注解配置解析器
本文承接前文Spring源码情操陶冶-自定义节点的解析,分析spring中的context:annotation-config节点如何被解析 源码概览 对BeanDefinitionParser接口的 ...
- Spring源码情操陶冶-PropertyPlaceholderBeanDefinitionParser注解配置解析器
本文针对spring配置的context:property-placeholder作下简单的分析,承接前文Spring源码情操陶冶-自定义节点的解析 spring配置文件应用 <context: ...
- spring源码分析之cache注解
Spring 3.1 引入了激动人心的基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如EHCache 或者 OSCache),而是一个对缓存使用的抽象 ...
- Spring源码学习之:模拟实现BeanFactory,从而说明IOC容器的大致原理
spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后 ...
- 【spring源码分析】@Value注解原理
class org.springframework.context.support.PropertySourcesPlaceholderConfigurer 该类实现了的接口:1.org.spring ...
- Spring Ioc源码分析系列--@Autowired注解的实现原理
Spring Ioc源码分析系列--@Autowired注解的实现原理 前言 前面系列文章分析了一把Spring Ioc的源码,是不是云里雾里,感觉并没有跟实际开发搭上半毛钱关系?看了一遍下来,对我的 ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
- 框架-spring源码分析(一)
框架-spring源码分析(一) 参考: https://www.cnblogs.com/heavenyes/p/3933642.html http://www.cnblogs.com/BINGJJF ...
- Spring源码分析之IOC的三种常见用法及源码实现(二)
Spring源码分析之IOC的三种常见用法及源码实现(二) 回顾上文 我们研究的是 AnnotationConfigApplicationContext annotationConfigApplica ...
随机推荐
- Yii2表头强制不换行
在Yii2中使用GridView生成的表格有一个小问题,那就是表头的列宽是根据列的值自适应的,但有时值的长度比较小,表头就会出现下面这种情况(表头不在同一行,非常的不利于数据的查看) 设置强制不换行可 ...
- Schema的快速入门
Schema的快速入门 如果是简单元素直接 <element name="" type=""></element> Schema开发过 ...
- Gentoo 后的几个细节的完善
Gentoo 后的几个细节的完善 目录 Gentoo 后的几个细节的完善 细节一:引导分区与 cdrom 开机正确挂载 细节二:可预见的命名规则的网络接口名称改为传统的 eth0 细节三:为管理员用户 ...
- UVA OJ 623 500!
500! In these days you can more and more often happen to see programs which perform some useful cal ...
- 还在手动部署jar包吗?快速掌握Jenkins安装,教你使用Jenkins实现持续交付
Jenkins Jenkins: 开源软件项目 基于Java开发的一种持续集成工具 用于监控持续重复的工作 旨在提供一个开放易用的软件平台, 便于软件的持续集成 基于Docker安装Jenkins 与 ...
- CSS3边界图片
目录 border-image border-image-slice border-image-width border-image-outset border-image-repeat border ...
- ruby基础(二)
ruby语法基础 1.方法 方法时对象定义的与该对象相关的操作.在Ruby中,对象的所有的操作都被封装成 方法. 语法糖:语法糖是一种为了照顾一般人的习惯而产生的特殊语法. ruby中一切数据都是对象 ...
- 【转载】有图 KVM折腾记..
KVM折腾记...https://lengjibo.github.io/KVM%E6%8A%98%E8%85%BE%E8%AE%B0/ Veröffentlicht am 2018-09-20 | ...
- Nginx——Docker下安装部署
前言 Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务 . 一. 环境说明 docker: 18.09.9-ce nginx: 1.1 ...
- 云计算OpenStack核心组件---nova计算服务(7)
一.nova介绍 Nova 是 OpenStack 最核心的服务,负责维护和管理云环境的计算资源.OpenStack 作为 IaaS 的云操作系统,虚拟机生命周期管理也就是通过 Nova 来实现的. ...