SpringBoot
2021-04-18 20:18:08 0 举报
AI智能生成
SpingBoot在启动时候坐了哪些事情:两条路:注解和Run方法
作者其他创作
大纲/内容
线路一:@SpringBootApplication
@SpringBootConfiguration 配置类
@Configuration<br>public @interface SpringBootConfiguration{}
SpringBoot的配置类 ,标注在某个类上 , 表示这是一个SpringBoot的配置类
@EnableAutoConfiguration 开启自动配置功能
@AutoConfigurationPackage<br>@Import(AutoConfigurationImportSelector.class)<br>public @interface EnableAutoConfiguration {
@AutoConfigurationPackage 作用是将添加该注解的类所在的packpage作为自动配置进行管理(要扫描哪些包)
@Import(AutoConfigurationPackages.Registrar.class)<br>public @interface AutoConfigurationPackage {}
Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器
@Import(AutoConfigurationPackages.Registrar.class) 自动配置导入选择器
AutoConfigurationImportSelector自动配置导入选择器,那么它会导入哪些组件的选择器呢?<br>
点进去看会发现有这样一个方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {}
99:getAutoConfigurationEntry //获取自动配置类<br>
123:getCandidateConfigurations //获取候选配置<br>
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),<br> getBeanClassLoader()); //获取配置,什么配置?:META-INF/spring.factories(这里在断言中就可以看到)<br>
假如没有断言呢?继续向下跟踪
loadFactoryNames方法(使用给定的类加载器从“ META-INF / spring.factories”加载给定类型的工厂实现的标准类名)
loadSpringFactories
OK,我们现在知道SpringBoot的自动配置是哪里来的了,下面我们返回Run方法,请回到开头←
@ComponentScan自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中<br>
路线二:Run方法追踪
public static void main(String[] args) {<br> SpringApplication.run(SpringBoot1Application.class, args);<br> }
这里可以总结为两步<br>
1、一直深入不是run方法为止
public ConfigurableApplicationContext run(String... args)
prepareEnvironment(listeners, bootstrapContext, applicationArguments)
printBanner(environment);
createApplicationContext();
refreshContext(context);
refresh((ApplicationContext) context);
refreshContext(context)<br>
applicationContext.refresh();
public void refresh() throws BeansException, IllegalStateException {<br> synchronized(this.startupShutdownMonitor) {<br> // context设置 启动日期/当前状态/初始环境/验证<br> this.prepareRefresh();<br> // 创建一个bean工程,<br> ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();<br> this.prepareBeanFactory(beanFactory);<br><br> try {<br> // 注册scope <br> this.postProcessBeanFactory(beanFactory);<br> // 调用所有bean工厂,对bean进行处理<br> this.invokeBeanFactoryPostProcessors(beanFactory);<br> // 拦截bean,<br> this.registerBeanPostProcessors(beanFactory);<br> // 国际化操作<br> this.initMessageSource();<br> // 广播事件<br> this.initApplicationEventMulticaster();<br> // 特殊bean 比如tomcat<br> this.onRefresh();<br> // 注册监听器<br> this.registerListeners();<br> this.finishBeanFactoryInitialization(beanFactory);<br> this.finishRefresh();<br> }<br>
super.refresh();
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);<br>
继续跟进方法:invokeBeanFactoryPostProcessors
postProcessBeanDefinitionRegistry
processConfigBeanDefinitions(registry);
331:parser.parse(candidates);//解析候选配置
if (bd instanceof AnnotatedBeanDefinition) {<br> parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());<br> }<br> else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {<br> parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());<br> }<br> else {<br> parse(bd.getBeanClassName(), holder.getBeanName());<br> }//谁便选一条即可
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
processConfigurationClass
sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
递归处理配置类及其超类层次结构
点进去这个类你会发现注解的处理是在这里进行的
我们现在只关心线路一中的public String[] selectImports(AnnotationMetadata annotationMetadata) {}是哪里调用的
我们知道,在主启动类上的import注解里面会加载配置类,那么谁来调用加载呢?
找到310行,对于@Import注解的处理<br>
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);
581:String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());<br>
是不是又回到了最初的起点?
registerBeanPostProcessors(beanFactory);
registerListeners();
public ConfigurableApplicationContext run(String... args) {<br> // 创建计时器<br> StopWatch stopWatch = new StopWatch();<br> stopWatch.start();<br> <br> ConfigurableApplicationContext context = null;<br> Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();<br> // 设置属性<br> this.configureHeadlessProperty();<br> <br> // spring中监听器的使用,这些对象想创建,就得知道监听器的全路径<br> // 会从spring.factory中读取<br> SpringApplicationRunListeners listeners = this.getRunListeners(args);<br> listeners.starting();<br><br> Collection exceptionReporters;<br> try {<br> // 初始化默认应用参数<br> ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);<br> // 根据监听器和默认的参数 来准备spring所需要的环境<br> ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);<br> this.configureIgnoreBeanInfo(environment);<br> // 打印出banner<br> Banner printedBanner = this.printBanner(environment);<br> // 创建应用上下文<br> context = this.createApplicationContext();<br> // 异常报告<br> exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);<br> // 准备应用上下文 <br> this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);<br> // 刷新上下文<br> this.refreshContext(context);<br> // 刷新<br> this.afterRefresh(context, applicationArguments);<br> stopWatch.stop();<br> if (this.logStartupInfo) {<br> (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);<br> }<br><br> listeners.started(context);<br> // 执行callRunners, 支持自定义run方法<br> this.callRunners(context, applicationArguments);<br> } <br>
2、通过构造方法加载初始化<br>
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {<br> // 设置一些资源加载器<br> this.resourceLoader = resourceLoader;<br> // 加载类资源不能为空<br> Assert.notNull(primarySources, "PrimarySources must not be null");<br> // 做一个数据结构的转换<br> this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));<br> // 判断当前项目类型是什么(判断该应用是否为WEB应用)<br> this.webApplicationType = WebApplicationType.deduceFromClasspath();<br> //从Spring工厂获取Bootstrap注册表初始化程序<br> this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();<br> //加载所有可用初始化器<br> setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));<br> //设置说有可用监听程序监听器<br> setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));<br> // 根据class 推断应用的入口类 通过run方法调用main方法<br> this.mainApplicationClass = deduceMainApplicationClass();<br>}
WebApplicationType.deduceFromClasspath();
判断了我们的工程到底是个什么类型(web工程,基本的spring工程等)
getSpringFactoriesInstances(ApplicationContextInitializer.class));
getSpringFactoriesInstances(type, new Class<?>[] {});
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
loadSpringFactories(classLoaderToUse)
通过类加载器获取META-INF/spring.factories文件生成并返回实例工厂<br>
Pom文件<br>
为什么有些启动器和依赖不用写版本号?
点进去看看
再点进去看看,你会发现,里面存有大量的版本管理
收藏
0 条评论
下一页