Spring 扫描原理源码分析
第一次写文章,若有错请指正
spring容器启动
众所周知spring启动得实例化一个容器xxxApplicationContext。实例化有着4种构造方法,其中与扫描有关的是下面两种传入一个类和一段字符串。
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
register(componentClasses);
refresh();
}
public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}
两种实例化方法就只有一行代码不同,本文先介绍第一种实例化方法,传入一个类的。
register(componentClasses) 注册一个类
这行代码主要就是把传进来的类解析成BeanDefinition并且添加到BeanDefinitionMap当中。
//删除了不相关的代码
public void register(Class<?>... componentClasses) {
this.reader.register(componentClasses);
}
------------------------------
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
------------------------------
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}
------------------------------
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
@Nullable BeanDefinitionCustomizer[] customizers) {
//把传进来的类转换成bd对象
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(supplier);
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
//下面就是设置bd的属性了
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
if (qualifiers != null) {
for (Class<? extends Annotation> qualifier : qualifiers) {
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
if (customizers != null) {
for (BeanDefinitionCustomizer customizer : customizers) {
customizer.customize(abd);
}
}
//在这里把bd封装成bdHolder对象方便注册到工厂的Map当中
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
----------------
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name. 在工厂里注册bean definition
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
}
--------------
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//一般来讲 从Map中取得的是空的 要是不为空的话经过一系列的判断,最后还是会把新的放着Map中
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
//省略了日志输出
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
//判断存放已经创建Bean的Set集合是不是空的 一般来讲还是为空的,因为这个时候spring容器还没有刷新
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
//这一步就是把bd放在Map当中
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
//替换bdNames的List集合
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
}
上面的代码就是把一个类转换成bd然后放到bdMap当中,这一步至关重要。执行完后register后则是Spring容器的刷新流程了,实际扫描的入口是在refresh()的invokeBeanFactoryPostProcessors(beanFactory) 这个方法,执行Bean工厂的后置处理器。
Bean工厂的后置处理器
Bean工厂的后置处理器有很多,也可以自定义,本文不多介绍,只讲解使用的。
与spring扫描相关的后置处理器是ConfigurationClassPostProcessor这个类,这个类是spring内置的,实现了BeanDefinitionRegistryPostProcessor这个接口。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
}
}
-----------------------
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// 这里就是从bd中获取实现了BeanDefinitionRegistryPostProcessor这个接口的类
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
//从容器中获取这个Bean 并且加入到一个集合当中
currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
processedBeans.add(ppName);
}
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
执行postProcessBeanDefinitionRegistry方法
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
currentRegistryProcessors.clear();
}
------------------------
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
//调用这个方法
processConfigBeanDefinitions(registry);
}
上面的都是一些处理流程,最终执行的方法是processConfigBeanDefinitions这个方法。这个方法尤为重要,就是这个方法处理了扫描的。
checkConfigurationClassCandidate 检查是不是一个配置类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//这里主要是判断是不是一个配置类
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
}
上面的代码是从工厂的bdMap中遍历,找到是配置类的bd然后添加到configCandidates这个配置类候选者的集合里面。
public static boolean checkConfigurationClassCandidate(
BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
//先从元数据中获取@Configuration这个注解的信息
Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
//config不为空且里面的一个属性的值不为false,就把这个bd称为一个完整的(full)的配置类
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
//上面的条件不符合就来到这里,判断是不是一个lite的配置类。要是上下两个判断都不符合就不是一个配置类。
else if (config != null || isConfigurationCandidate(metadata)) {
beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
return false;
}
}
--------------------------------------------
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
// 这里就是判断是不是一个lite的配置类了,candidateIndicators包含着@Component、@ComponentScan、@Import、@ImportResource这4个注解
for (String indicator : candidateIndicators) {
if (metadata.isAnnotated(indicator)) {
return true;
}
}
}
--------------
static {
candidateIndicators.add(Component.class.getName());
candidateIndicators.add(ComponentScan.class.getName());
candidateIndicators.add(Import.class.getName());
candidateIndicators.add(ImportResource.class.getName());
}
在这个方法里面最关键的就是这几行代码,只有Spring识别到你是一个配置类才会把你进行解析的,至于full和lite的区别可以看这篇博文
parser.parse(candidates);
这里还算不上扫描的入口,还是在解析着上面通过筛选的配置类。就是实例化一个配置类的解析对象(ConfigurationClassParser)来对bd进行解析
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 {
StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
//解析
parser.parse(candidates);
parser.validate();
--------------------------------------
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
//这里就是对这些bd分别解析
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());
}
}
this.deferredImportSelectorHandler.process();
}
------------------------------
将bd封装成一个ConfigurationClass对象
之所以封装成一个ConfigurationClass对象是因为要把解析注解得到的数据给存起来,方便在下面注册给Spring容器中的工厂。
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
//封装成一个ConfigurationClass对象
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
-----------------
//最终一个注解形式的配置类bd的解析入口是
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
doProcessConfigurationClass Spring基本注解解析
这个方法非常长,可是作用也非常大。主要是对
1.@PropertySource
2.@ComponentScan
3.@Import
4.@ImportResource
5.@Bean
6.重写接口的方法
7.进程超类的方法
对这7个注解和方法进行解析,本文着重讲解@ComponentScan这个注解的解析,这个注解正是扫描的关键。
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// 处理@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 这个parser就是将标注类上的@ComponentScan进行解析
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 这里是对扫描得到的bd进行解析,类似上面的步骤。
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());
}
}
}
}
}
要特别注意的是,在this.componentScanParser.parse()这个方法里有一个重要的步骤,先是new出一个扫描的对象,而这个扫描的对象是默认把@Component给扫描到的。
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
-----------------------
//实例化ClassPathBeanDefinitionScanner 对象
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
Environment environment, @Nullable ResourceLoader resourceLoader) {
//useDefaultFilters这个值在@ComponentScan注解上缺省值的true的,所以会把含有@Component注解的类给扫描到。
if (useDefaultFilters) {
registerDefaultFilters();
}
}
------------------
protected void registerDefaultFilters() {
this.includeFilters.add(new AnnotationTypeFilter(Component.class));
..........................
}
在扫描前对@ComponentScan传入的包名进行获取
//演示类
@ComponentScan("com.zsk")
public class TestMain {
}
//找到注解中的要扫描的包的值
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
//扫描入口
return scanner.doScan(StringUtils.toStringArray(basePackages));
通过这个扫描的对象调用doScan方法,进行Spring类的扫描,将符合规则的类给扫描进Spring容器。同样调用doScan方法的还有上面提到过的实例化Spring容器的构造方法。
只不过这里是直接传入扫描的路径,与传入一个类不同的是,没有提前注册一个类到Spring容器当中。
回到doScan方法当中,真正进行扫描的是这个方法scanCandidateComponents
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) {
/**
* 找到包名下加了@Component注解的类 解析成为BeanDefinition
*/
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
---------------------
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
//就是这个方法
return scanCandidateComponents(basePackage);
}
}
---------------------
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//示例:比如我扫描包的路径是com.zsk 那么转换后的格式是 “classpath*:com/zsk/**/*.class”
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
//获取正则表达式符合的所有类
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
boolean traceEnabled = logger.isTraceEnabled();
boolean debugEnabled = logger.isDebugEnabled();
for (Resource resource : resources) {
if (traceEnabled) {
logger.trace("Scanning " + resource);
}
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
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);
}
return candidates;
}
---------------------
上面的代码是把包下全部的类都给获取得到了,接下来就是要进行筛选,把在类上标注了@Component注解的类个添加到一个集合里面。调用isCandidateComponent进行判断,isCandidateComponent()这个方法会调用两次;第一次调用这个方法是先判断是否和excludeFilters相符的类,然后判断该类是否是应该跳过的类。第二次调用是判断是否是一个独立和具体的类都true的话就把产生的bd放进一个Set集合里面传出去,这个方法的路程走完了:findCandidateComponents(basePackage);
//第一次调用
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);
-----------------------
private boolean isConditionMatch(MetadataReader metadataReader) {
return !this.conditionEvaluator.shouldSkip(metadataReader.getAnnotationMetadata());
}
-----------------------
}
}
return false;
}
接着把符合条件的类转化为一个Bean Definition对象后放在一个集合里面,传回出去scanCandidateComponents这个方法,对符合条件的bd对象赋值,然后注册到工厂的bdMap中。
for (String basePackage : basePackages) {
/**
* 找到包名下加了@Component注解的类 解析成为BeanDefinition
*/
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
/**
* 设置BeanDefinition的属性
*/
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
/**
* 注册到工厂的BeanDefinitionMap中
*/
if (checkCandidate(beanName, candidate)) {
//封装成一个bdHolder对象传递
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
doScan方法就这样完成了,回到解析@ComponentScan注解的代码上,接下来就是对得到的bd集合进行类似的解析。
// Process any @ComponentScan annotations 处理@ComponentScan注解
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
for (AnnotationAttributes componentScan : componentScans) {
// 得到符合扫描条件得bd对象
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// 对bd对象进行解析
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());
}
}
}
}
完成doProcessConfigurationClass方法,把封装的ConfigurationClass对象给存储进来。
do {
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);
//存放到configurationClasses中
this.configurationClasses.put(configClass, configClass);
loadBeanDefinitions 加载BeanDefinitions
回到一开始的processConfigBeanDefinitions方法,接下来就是对封装的ConfigurationClass对象进行处理,加载到Spring容器中。
//得到ConfigurationClass的集合
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
/**
* 加载从配置类扫描到的@Import、@Bean方法、@ImportResource等 注册到BeanDefinitionMap中
*/
this.reader.loadBeanDefinitions(configClasses);
Spring扫描到这里算是完成了,下面的代码是对上面扫描解析进行校验,看看是不是把扫描得到得配置类都给解析完了
/**
* 下面是对扫描前和扫描后的bdMap进行对比
*/
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set<String> alreadyParsedClasses = new HashSet<>();
//把上面解析完的类添加到alreadyParsedClasses--已经解析完的类
for (ConfigurationClass configurationClass : alreadyParsed) {
alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
}
for (String candidateName : newCandidateNames) {
//判断是不是新的bd
if (!oldCandidateNames.contains(candidateName)) {
BeanDefinition bd = registry.getBeanDefinition(candidateName);
//判断bd是否是候选的配置类并且是不是已经解析完的类
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
!alreadyParsedClasses.contains(bd.getBeanClassName())) {
//添加到candidates再次解析
candidates.add(new BeanDefinitionHolder(bd, candidateName));
}
}
}
//新的bdMap就变成旧的bdMap
candidateNames = newCandidateNames;
}
}
while (!candidates.isEmpty());
至此,Spring得扫描就到此完成了。总的来说,Spring得扫描就是通过得到包得路径然后扫描包下每一个类,得到类的元数据。通过判断是否是一个配置类来进行解析。