SpringIOC就是将对象的创建过程交给Spring容器,Spring容器在启动的时候会扫面所有的组件(带有@Component、@Bean等注解)存放在beanDefinationMap中,然后再需要创建对象的时候,取出bean定义信息,实例化对象即可。这里我们将分析一下组件扫描的过程。
注意:本文的中心思想是看Spring如何扫描到某个组件,并将其添加到容器中,其余问题不探究,不要走偏。
Spring初始化即容器的初始化,容器初始化的入口在AbstractApplicationContext.refresh()方法:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
//添加BeanPostProcessor到BeanFactory,用于在Bean初始化前后包装Bean
postProcessBeanFactory(beanFactory);
//调用BeanFactoryPostProcessor,所有的bean定义将在这里加载,这是我们这节关注的重
//点
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
resetCommonCaches();
}
}
在invokeBeanFactoryPostProcessors的调用链中,其会从已有的容器中找到满足执行条件的所有BeanDefinitionRegistryPostProcessor类
调用其postProcessBeanDefinitionRegistry方法注册所有组件到容器中。这里你可以通过断掉调试,最终执行扫描所有类文件的BeanDefinitionRegistryPostProcessor类就是ConfigurationClassPostProcessor。、
那ConfigurationClassPostProcessor类是从哪里来的呢?其流程如下:
1.创建ApplicationContext的时候,优先会在构造函数中创建一个DefaultListableFactory容器。
public GenericApplicationContext() { this.beanFactory = new DefaultListableBeanFactory(); }
2.同时创建ApplicationContext的时候,创建AnnotatedBeanDefinitionReader对象(假设是Web环境)
public AnnotationConfigServletWebServerApplicationContext( DefaultListableBeanFactory beanFactory) { super(beanFactory); this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }
3.创建AnnotatedBeanDefinitionReader对象时,注册ConfigurationClassPostProcess对象,对于解析各种组件注解。
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(environment, "Environment must not be null"); this.registry = registry; this.conditionEvaluator = new ConditionEvaluator(registry, environment, null); AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry); }
org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)中代码如下:
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)); }
至此,我们可以知道容器在refresh过程中,会调用钩子函数org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry注册beanDefinition到beanDefinitionMap之中,而具体扫描程序中含有@Component、@Bean等注解的类将其转换为beanDefinition并添加到容器中的实现类为ConfigurationClassPostProcess。接下来我们主要分析该类是如何做的?
该类中主要通过org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法扫描,该方法的主要步骤分为:
1.找到候选需要解析的类
//这里如果我们是通过SpringBoot程序启动的,那么在SpringBoot.run方法中会将启动类加载到容器中
//而我们的启动类都会添加@SpringBootApplication注解,该注解包含@Configuration注解
String[] candidateNames = registry.getBeanDefinitionNames();
for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//该方法中会检测对应的bean类是否存在@Configuration注解,存在则加入候选解析类
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
2.从我们的SpringBoot启动类开始解析
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set candidates = new LinkedHashSet<>(configCandidates);
Set alreadyParsed = new HashSet<>(configCandidates.size());
do {
//解析候选类,即根据启动类找到带有@Compontent注解的类
parser.parse(candidates);
parser.validate();
Set 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());
}
//利用reader加载所有的.class文件,生成beanDefinition放入beanDefinitionMap
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
candidates.clear();
if (registry.getBeanDefinitionCount() > candidateNames.length) {
String[] newCandidateNames = registry.getBeanDefinitionNames();
Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
Set 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());
3.ConfigurationClassParser 解析过程
主要调用该方法解析org.springframework.context.annotation.ConfigurationClassParser#processConfigurationClass:
//初始加载时,configClass为我们的启动类
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
//加载该bena定义的前置校验
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);
}
// Otherwise ignore new imported config class; existing non-imported class overrides it.
return;
}
else {
// Explicit bean definition found, probably replacing an import.
// Let's remove the old one and go with the new one.
this.configurationClasses.remove(configClass);
this.knownSuperclasses.values().removeIf(configClass::equals);
}
}
// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass = asSourceClass(configClass);
do {
//真正的加载过程,循环加载项目中的所有组件类
sourceClass = doProcessConfigurationClass(configClass, sourceClass);
}
while (sourceClass != null);
this.configurationClasses.put(configClass, configClass);
}
该方法org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass中会依次处理@Import,@ImportResource,@ComponentScan、方法上的@Bean 所指向位置的组件。比如我们所熟悉的@CompotentScan:
// Process any @ComponentScan annotations
//读取该类上的所有@ComponentScan注解
Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
//遍历
for (AnnotationAttributes componentScan : componentScans) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
//利用ComponentScanParser解析@ComponentScan注解
Set scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and parse recursively if needed
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
if (ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(), this.metadataReaderFactory)) {
parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
}
}
}
}
4.利用ComponentScanParser解析@ComponentScan注解
该方法的主要处理逻辑为:
1.找到basePackages注解,即要扫描的类的包范围
Set basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
basePackages.addAll(Arrays.asList(tokenized));
}
for (Class clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
2.如果没有配置,则取启动类所在包
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
3.利用ClassPathBeanDefinitionScannerClas扫描包下所有类,解析成beanDefinition,具体方法在org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents,有兴趣的可以研究研究。
其实整个过程并不难理解:
1.Spring启动的时候,创建ApplicationContext,同时创建钩子类ConfigurationClassPostProcessor。
2.在调用refresh时候,调用钩子方法ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry做扫描工作。
3.在钩子方法中,从SpringBoot启动类开始,寻找basePackage指定扫描包,如果找寻不到,取当前启动类所在包,加载包下所有的.class文件。
4.对于带有@Component注解的类,加载进来生成beanDefinition添加到容器当中。