目录
- 前言
- 代码流程
-
- 1.JaylinApplication::main (com.jaylin.jaylin)
- 2.SpringApplication::run (org.springframework.boot)
- 3.SpringApplication::run (org.springframework.boot)
- 4.SpringApplication::run (org.springframework.boot)
- 5.SpringApplication::refreshContext (org.springframework.boot)
- 6.SpringApplication::refresh (org.springframework.boot)
- 7.SpringApplication::refresh (org.springframework.boot)
- 8.ServletWebServerApplicationContext::refresh (org.springframework.boot.web.servlet.context)
- 9.AbstractApplicationContext::refresh (org.springframework.context.support)
- 10.AbstractApplicationContext::invokeBeanFactoryPostProcessors (org.springframework.context.support)
- 11.PostProcessorRegistrationDelegate::invokeBeanFactoryPostProcessors (org.springframework.context.support)
- 12.PostProcessorRegistrationDelegate::invokeBeanDefinitionRegistryPostProcessors (org.springframework.context.support)
- 13.ConfigurationClassPostProcessor::postProcessBeanDefinitionRegistry (org.springframework.context.annotation)
- 14.ConfigurationClassPostProcessor::processConfigBeanDefinitions (org.springframework.context.annotation)
- 15.ConfigurationClassParser::parse (org.springframework.context.annotation)
- 16.ConfigurationClassParser::parse (org.springframework.context.annotation)
- 17.ConfigurationClassParser::processConfigurationClass (org.springframework.context.annotation)
- 18.ConfigurationClassParser::doProcessConfigurationClass (org.springframework.context.annotation)
- 19.ComponentScanAnnotationParser::parse (org.springframework.context.annotation)
- 20.ClassPathBeanDefinitionScanner::doScan (org.springframework.context.annotation)
- 21.ClassPathBeanDefinitionScanner::registerBeanDefinition (org.springframework.context.annotation)
- 22.BeanDefinitionReaderUtils::registerBeanDefinition (org.springframework.beans.factory.support)
- 23.DefaultListableBeanFactory::registerBeanDefinition (org.springframework.beans.factory.support)
- 过程中的问题
前言
大家好,我是oxye马儿,最近研究了Springboot的启动流程,非常长,bean加载流程在其中只是一块拼图。然后我就看bean生成流程,总结的过程中,感觉对于Bean生成的流程倒是看了,但是BeanDefinition怎么来的不知道哇,不知道来源只知道使用 那指定不行,所以再看一下BeanDefinition bean定义的生成流程
看的时候发现BeanDefinition和一些Bean一样,不同种类的是在不同阶段生成的,比如
1.框架硬编码的,启动时,先会根据源码中写死的类路径加载一些bean
2.开发自己写的,后面再生成我们平时使用的@Component、@Controller、@Service等注解的bean
3.实现一些Spring源码的接口来加载自定义bean,比如Mybatis就实现了ImportBeanDefinitionRegistrar
等等
这篇先写一下第二种BeanDefinition的生成过程,看看我们开发中注解的Bean是怎么被找到,生成BeanDefinition的
Springboot版本:2.3.2
代码流程
1.JaylinApplication::main (com.jaylin.jaylin)
启动类main函数
package com.jaylin.jaylin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class JaylinApplication {
public static void main(String[] args) {
SpringApplication.run(JaylinApplication.class, args);
}
}
2.SpringApplication::run (org.springframework.boot)
SpringApplication的第一个静态方法run,调用第二个静态方法run
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] {
primarySource }, args);
}
3.SpringApplication::run (org.springframework.boot)
SpringApplication的第二个静态方法run,创建一个SpringApplication实例(会做赋予启动类、推算webApplicationType、设置启动器、设置监听器等操作),并调用非静态方法,第三个run
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
4.SpringApplication::run (org.springframework.boot)
SpringApplication的第三个静态方法run(会做获取监听器、触发一些事件、准备环境、打印banner、创建应用上下文、准备应用上下文、刷新应用上下文等操作)
部分操作↓
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//打印启动团
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//准备应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新应用上下文
refreshContext(context);
//后续操作
afterRefresh(context, applicationArguments);
5.SpringApplication::refreshContext (org.springframework.boot)
刷新应用上下文
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
6.SpringApplication::refresh (org.springframework.boot)
SpringApplication的第一个静态方法refresh,只是调用第二个静态方法refresh,将参数向下转型ApplicationContext -> ConfigurableApplicationContext
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
refresh((ConfigurableApplicationContext) applicationContext);
}
7.SpringApplication::refresh (org.springframework.boot)
SpringApplication的第二个静态方法fresh,调用了入参的refresh方法,实现ConfigurableApplicationContext的refresh的有三个类,这里入参context在前面进行判断时,根据webApplicationType,AnnotationConfigServletWebServerApplicationContext,此类又直接继承了ServletWebServerApplicationContext
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
8.ServletWebServerApplicationContext::refresh (org.springframework.boot.web.servlet.context)
ServletWebServerApplicationContext的refresh直接super.refresh,调用了他先祖AbstractApplicationContext的refresh,好处是添加了异常处理逻辑
@Override
public final void refresh() throws BeansException, IllegalStateException {
try {
super.refresh();
}
catch (RuntimeException ex) {
WebServer webServer = this.webServer;
if (webServer != null) {
webServer.stop();
}
throw ex;
}
}
9.AbstractApplicationContext::refresh (org.springframework.context.support)
AbstractApplicationContext的refresh方法,又关键又长(会做刷新准备、准备BeanFactory、设置钩子方法、调用已注册的BeanFactoryPostProcessor、注册、初始化信息源、注册监听器、初始化各种类型bean等操作)
10.AbstractApplicationContext::invokeBeanFactoryPostProcessors (org.springframework.context.support)
实例化和调用所有BeanFactoryPostProcessor(包括其子类 BeanDefinitionRegistryPostProcessor),BeanFactoryPostProcessor接口是 Spring初始化BeanFactory 时对外暴露的扩展点,Spring ioc 容器允许 BeanFactoryPostProcessor在容器实例化任何bean之前,读取并修改BeanDefinition
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
11.PostProcessorRegistrationDelegate::invokeBeanFactoryPostProcessors (org.springframework.context.support)
PostProcessorRegistrationDelegate的静态方法,方法名称同上一步一样
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
12.PostProcessorRegistrationDelegate::invokeBeanDefinitionRegistryPostProcessors (org.springframework.context.support)
遍历调用给定的 BeanDefinitionRegistryPostProcessor Beans,这里是ConfigurationClassPostProcessor
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
13.ConfigurationClassPostProcessor::postProcessBeanDefinitionRegistry (org.springframework.context.annotation)
从注册表中的配置类 派生更多的BeanDefinition
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);
processConfigBeanDefinitions(registry);
}
14.ConfigurationClassPostProcessor::processConfigBeanDefinitions (org.springframework.context.annotation)
建立并验证基于注册表的配置模型
15.ConfigurationClassParser::parse (org.springframework.context.annotation)
解析方法,入参包装了启动类,其类型是AnnotatedBeanDefinition
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());
}
}
}
}
16.ConfigurationClassParser::parse (org.springframework.context.annotation)
专门处理注解类型AnnotatedBeanDefinition的解析方法
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}
17.ConfigurationClassParser::processConfigurationClass (org.springframework.context.annotation)
处理配置类
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
18.ConfigurationClassParser::doProcessConfigurationClass (org.springframework.context.annotation)
构建配置类,对@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean等注解 注解过的代码进行,此时因为启动类上的注解@SpringBootApplication中包含@ComponentScan,所以走到了@ComponentScan对应的逻辑
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
// Recursively process any member (nested) classes first
processMemberClasses(configClass, sourceClass, filter);
}
// Process any @PropertySource annotations
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");
}
}
// Process any @ComponentScan annotations
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) {
// The config class is annotated with @ComponentScan -> perform the scan immediately
Set<BeanDefinitionHolder> 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) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
// Process any @ImportResource annotations
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);
}
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
// Process default methods on interfaces
processInterfaces(configClass, sourceClass);
// Process superclass, if any
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();
}
}
// No superclass -> processing is complete
return null;
}
19.ComponentScanAnnotationParser::parse (org.springframework.context.annotation)
配置一个类路径扫描器ClassPathBeanDefinitionScanner,把入参中的值赋予它,比如扫描路径resourcePattern值为**/*.class,等等属性,然后开始扫描
return scanner.doScan(StringUtils.toStringArray(basePackages));
20.ClassPathBeanDefinitionScanner::doScan (org.springframework.context.annotation)
在指定的路径下执行扫描,返回注册的bean定义,我们的路径就是启动类所在路径(Packages下)。这里会根据启动类路径(classpath*:com/jaylin/jaylin/**/*.class),使用scanCandidateComponents,扫描到所有class文件到resources数组,然后经过筛选得到一个候选者大军candidates(Set类型),然后依次遍历这些候选者,构造BeanDefinitionHolder,逐个执行registerBeanDefinition
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) {
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);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
21.ClassPathBeanDefinitionScanner::registerBeanDefinition (org.springframework.context.annotation)
使用给定的注册表注册指定的bean
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
22.BeanDefinitionReaderUtils::registerBeanDefinition (org.springframework.beans.factory.support)
将beanName和definitionHolder中的BeanDefinition交给下一步,registry是DefaultListableBeanFactory
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
23.DefaultListableBeanFactory::registerBeanDefinition (org.springframework.beans.factory.support)
经过校验后,新增一个新的BeanDefinition(this.beanDefinitionMap.put(beanName, beanDefinition);)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
过程中的问题
注解怎么扫描到的
第20步中
findCandidateComponents中,获取注解列表,最后生成候选者们
获取注解类
scanCandidateComponents -> getMetadataReader -> CachingMetadataReaderFactory::getMetadataReader -> new SimpleMetadataReader
判断注解是否合适
isCandidateComponent中excludeFilters和includeFilters,启动类注解中只有excludeFilters,但是在Springboot项目启动后,默认应用上下文是AnnotationConfigServletWebServerApplicationContext,然后在SpringApplication::createApplicationContext后续操作中,走到ClassPathScanningCandidateComponentProvider::registerDefaultFilters,第一行就是this.includeFilters.add(new AnnotationTypeFilter(Component.class));
然后呢,@Controller,@Service和@Repository等常见注解,点进去看源码,他们也都注解了**@Component**,所以有这些注解的类才能被加入候选者
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
beanName怎么来的
第20步 doscan中
generateBeanName中,会先尝试获取注解中配置的名称,如果没有,就获取类名,首位小写