从SpringApplication开始

一般情况下启动SpringBoot都是新建一个类包含main方法,然后使用SpringApplication.run来启动程序:

@SpringBootApplication
public class AutoConfigApplication { public static void main(String[] args){
ConfigurableApplicationContext configurableApplicationContext = SpringApplication.run(AutoConfigApplication.class,args);
}
}

SpringApplication.run接收两个参数分别为:primarySource、运行参数(args),上面的代码使用AutoConfigApplication.class作为primarySource。SpringApplication还有一个实例方法也叫run,SpringBoot的大部分启动都由实例run方法来完成的,其中构造ApplicationContext由createApplicationContext方法完成:

protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

createApplicationContext根据this.webApplicationType来构造ApplicationContext,不同的环境都会使用不同的实例,但本文非web环境所有构造的时候会使用AnnotationConfigApplicationContext类。创建AnnotationConfigApplicationContext的时候会调用默认构造方法

public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotationConfigApplicationContext默认构造函数创建两个对象:

  • reader(AnnotatedBeanDefinitionReader):用于手动注册bean
  • scanner(ClassPathBeanDefinitionScanner): 用于扫描Component、Repository、Service等注解

AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner会注册一些注解处理器,注册的方式都是使用AnnotationConfigUtils的registerAnnotationConfigProcessors方法

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) { ... 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));
}
...
return beanDefs;
}

最终AnnotationConfigApplicationContext构造方法执行完成后ApplicationContext会有以下BeanDefinition:

构造完ApplicationContext后SpringApplicaiton紧接着会加载primarySource,上面提到 过primarySource是在运行的时候传递进来的(AutoConfigApplication.class),加载过程中不贴代码了,只要知道最终ApplicaitonContext中会多一个AutoConfigApplication的BeanDefinition:

小结

总的来说SpringApplicaiton主要干了这些事:

  • 创建AnnotationConfigApplicationContext
  • 加载一些处理注解的后处理器如:ConfigurationClassPostProcessor
  • primarySource加载进ApplicationContext

最重要的一点是,现在是有一个AnnotationConfigApplicationContext里面包含了primarySource(AutoConfigApplication)以及ConfigurationClassPostProcessor。打个断点在ApplicaitonContext刷新之前打印下context中的bean的名称,可以确定这样说没毛病!

@Configuration啥时候被解析?

虽说有了primarySource和ConfigurationClassPostProcessor后处理器,还是需要有个执行的入口。ConfigurationClassPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,BeanDefinitionRegistryPostProcessor会在ApplicationContext的refresh操作时被处理:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
invokeBeanFactoryPostProcessors(beanFactory);
...
}
} public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { ...
//找出所有类型为BeanDefinitionRegistryPostProcessor的bean的名称
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
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
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
...
} private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
//调用postProcessBeanDefinitionRegistry方法
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}

invokeBeanDefinitionRegistryPostProcessors会调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,通过断点调试工具确认下ConfigurationClassPostProcessor有没有在这一步被处理:

调试输出postProcessors集合里面有一个了ConfigurationClassPostProcessor元素,说明了ConfigurationClassPostProcessor的执行入口没有问题。

ConfigurationClassPostProcessor处理器

ConfigurationClassPostProcessor首先会判断在ApplicationContext中的bean是否被@Configuration注解标记,然后使用ConfigurationClassParser来解析@Configuration,ConfigurationClassPostProcessor的解析@Configuration的大致流程:

  1. 使用ConfigurationClassUtils.checkConfigurationClassCandidate检查BeanDefinition是否@Configuration注解标记
  2. 对@Configuration进行排序
  3. 使用ConfigurationClassParser解析@Configuration注解的信息
  4. 使用ConfigurationClassBeanDefinitionReader解析BeanDefinition
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); //获取所有BeanDefinitio名称
String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) {
BeanDefinition beanDef = registry.getBeanDefinition(beanName);
//如果是full、lite则说明已经处理过的类
if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
if (logger.isDebugEnabled()) {
logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
}
}
//检查BeanDefinition是否有@Configuration注解
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
} //如果没有找到@Configuration标记的类,则返回不作处理也
if (configCandidates.isEmpty()) {
return;
} //对@Configuration进行排序
configCandidates.sort((bd1, bd2) -> {
int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
return Integer.compare(i1, i2);
}); ... 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 {
//解析@Configuration class
parser.parse(candidates);
parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed); //读取BeanDefinition
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses); candidates.clear();
...
}
while (!candidates.isEmpty());
...
}

最后还是通过调试工具看一下示例中的的启动类AutoConfigApplication没有被处理:

图上显示configCandidates中有一个名称为autoConfigApplication的BeanDefinition的元素,说明AutoConfigApplication会被当作配置类解析,但是AutoConfigApplication并没有使用@Configuration注解,为什么还会被当做配置类呢?其实@Configuration在@SpringBootApplication注解中:

红色背景列出来的就是@Configuration注解,它是@SpringBootConfiguration的元注解。

最新文章

  1. 安卓真机调试 出现Installation error: INSTALL_FAILED_UPDATE_INCOMPATIBLE....
  2. python小细节
  3. 自己用C语言写单片机PIC18 serial bootloader
  4. HDOJ 3507 Print Article
  5. Unity3D基础知识梳理
  6. HDU3247 AC自动机+dp
  7. MySQL索引建立与删除
  8. C#中有关string和byte[]转换的问题
  9. web.xml常用元素
  10. Struts配置详解
  11. Django_简介
  12. orcal - 分组
  13. Vladik and Favorite Game CodeForces - 811D (思维+BFS+模拟+交互题)
  14. VMWare 14.1 15 Pro 安装 macOS Mojave 10.14.1系统 遇到的问题解决方案
  15. js 对象数组查找元素常用方法
  16. django2.1---admin 修改模块的名字为中文显示
  17. MySQL 性能监控4大指标——第二部分
  18. 安装VS2012出问题后,反复重启电脑。
  19. 安装部署OpenPAI + VSCode 提交
  20. MVVM特点、源(数据)与目标(如:控件等)的映射

热门文章

  1. python接口自动化2-第一次发送get请求
  2. Vue-cli父子组件之间传参
  3. The Mininum Function Value (luoguP2085 最小函数值)
  4. python 2.x中的中文
  5. 【Autofac打标签模式】Component和Autowired
  6. KMP算法关键
  7. Java Web(1)高并发业务
  8. VS无法新建项目
  9. Udp 异步通信(三)
  10. postman的监控接口响应时间monitor