学习整理摘自
http://www.yund.tech/zdetail.html?type=1&id=2b2a032bd98dfeb5e4113ef6624722d2
springboot启动原理
先看启动类注解:SpringBootApplication
@SpringBootApplication =
@Configuration(SpringBootConfiguration)+
@EnableAutoConfiguration+
@ComponentScan。
@Configuration
@Configuration及@bean两个注解可以创建一个简单的spring的配置类,用来替代xml的配置文件
@Configuration的注解类标识这个类可以使用Spring IoC容器作为bean定义的来源。
@Bean注解告诉Spring,一个带有@Bean的注解方法将返回一个对象,该对象应该被注册为在Spring应用程序上下文中的bean。
@ComponentScan
功能是自动扫描符合加载条件的组件,比如(@Component)或者bean定义,最终将这些bean定义加载到ioc容器中
spring会默认从声明@componentScan的类的package包下开始扫面,所以springboot的启动类最好放在根目录下,因为默认不指定basePackages
@EnableAutoConfiguration
借助@import的支持,收集和注册特定场景相关的bean定义
类似EnableAutoScheduling是通过@import将spring调度框架相关的bean定义加载到ioc容器
类似EnableMBeanExport是通过@import将JMX相关的bean加载到IOC容器中
而@EnableAutoConfiguration 借助@Import(AutoConfigurationImportSelector.class)将符合自动装配的bean加载到ioc容器中。也会根据类路径中的jar依赖为项目自动装配;比如spring-boot-starter-web会自动添加tomcat和springmvc进行配置
借助于Spring框架原有的一个工具类:SpringFactoriesLoader(下面详解)的支持@EnableAutoConfiguration可以智能的自动配置功效才得以大功告成!
每个配置文件一般都有以下的条件注解:
@ConditionalOnClass : classpath中存在该类时起效
@ConditionalOnMissingClass : classpath中不存在该类时起效
@ConditionalOnBean : DI容器中存在该类型Bean时起效
@ConditionalOnMissingBean : DI容器中不存在该类型Bean时起效
@ConditionalOnSingleCandidate : DI容器中该类型Bean只有一个或@Primary的只有一个时起效
@ConditionalOnExpression : SpEL表达式结果为true时
@ConditionalOnProperty : 参数设置或者值一致时起效
@ConditionalOnResource : 指定的文件存在时起效
@ConditionalOnJndi : 指定的JNDI存在时起效
@ConditionalOnJava : 指定的Java版本存在时起效
@ConditionalOnWebApplication : Web应用环境下起效
@ConditionalOnNotWebApplication : 非Web应用环境下起效
SpringFactoriesLoader做法:
主要功能是从指定的配置文件中spring.factories加载配置
SpringFactoriesLoader是一个抽象类,类中定义的静态属性定义了其加载资源的路径public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories",此外还有三个静态方法:
loadFactories:加载指定的factoryClass并进行实例化。
loadFactoryNames:加载指定的factoryClass的名称集合。
instantiateFactory:对指定的factoryClass进行实例化。
流程:先获取类加载器,然后调用loadFactoryNames方法获取所有的指定资源的名称集合,接着调用instantiateFactory方法实例化这些资源类并将其添加到result集合中。
总结: @EnableAutoConfiguration 自动配置就是从classpath中搜寻所有的META-INF/spring.factories配置文件,并将org.springframework.boot.autoconfigure.EnableutoConfiguration对应的配置通过反射实例化为对应了@Configuration的javaConfig形式的ioc容器类,然后加载到ioc容器中
讲完注解接下来讲run
一.首先实例一个SpringApplication然后执行run()
对于上下文的解释:
上下文,上下文代表了程序当下所运行的环境,联系你整个app的生命周期与资源调用,是程序可以访问到的所有资源的总和,资源可以是一个变量,也可以是一个对象的引用。-- 出自知乎
要做4件事情;
1.是否创建一个web类型的ApplicationContext通过deduceWebApplicationType()(主要是看dispatcherHandler和dispatcherServlet能不能被加载)
2.运用springFactoriesLoader在应用的classpath中加载所有可用的ApplicationContextInitializer
3.同样适用springFactoriesLoader加载classpath所有可用的ApplicationListener。将2.3步中获取的META-INF/spring.factories中的属性加载到springFactoriesLoader的cache中
4.推断并设置main方法的定义类
二.run()主要做了以下的工作:
主要流程:获取监听器 > 构造应用上下文环境 > 初始化应用上下文 > 刷新应用上下文前的准备工作 > 刷新应用上下文(做了ioc容器初始化过程) > 刷新后的拓展接口
1.获取监听器
主要通过getspringFactoriesInstance方法将Application初始化时候加载的监听器实例化,生成运行时监听器EventPublishingRunListener,然后启动监听器listener.starting(https://www.cnblogs.com/youzhibing/p/9603119.html - 摘自)
2.构造上下文环境
getOrCreateEnvironment:创建一个环境,判断webapplicationType是个什么玩意,如果是webApplicationType.SERVLET 那就创建一个 StandardServletEnvironment
Configuration():重排,移除应用环境中的PropertySource以及通过spring.profiles.avtive属性,在配置文件的处理期间进行其他的配置文件的激活,分别通过configurePropertySources()及configureProfiles()
listeners.environmentPrepared() :触发监听器,内部方法与listener.starting的实现一致(没有理解暂时不写)
bindToSpringApplication(environment) :将环境信息environment绑定到SpringApplication
总结:具体做了加载外部资源到environment中,包括命令行参数,serveltConfigInitParams,application.yml(.properties),初始化日志系统
3.初始上下文
之前有一个好玩的是打印banner,可以重新修改下自己玩
总结:通过webApplicationType来获取对应的上下文,然后通过反射方式进行实例化(学习https://www.cnblogs.com/youzhibing/p/9686969.html)
什么是BeanDefinition:
Spring容器里通过BeanDefinition对象来表示Bean,BeanDefinition描述了Bean的配置信息;根据BeanDefinition实例化bean,并放到bean缓存中。
spring bean配置方式:
有三种:基于XML的配置方式 、基于注解的配置方式和基于Java类的配置方式。
基于XML,这个我们都很熟,类似:
基于注解,这个我们也用的比较多,入@Component、@Service、@Controller等
基于java类,spring的推荐配置方式,@Configuration配合@Bean
4.刷新应用上下文前的准备工作
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
将springApplication中的部分属性应用到上下文,springApplication中的environment,initializers,listener应用到spring上下文中
context.setEnvironment(environment):
将context中的environment替换成SpringApplication中创建的environment
postProcessApplicationContext(context);
由于当前SpringApplication实例的属性:beanNameGenerator和resourceLoader都为null,所以此方法目前相当于什么也没做。此方法可能是我们定制SpringApplication所用。
applyInitializers(context);
将SpringApplication中的initializers应用到context中
5.刷新上下文(*挺多的)
详细的refresh()流程:两个重点的一个是obtainFefeshBeanFactory 另一个是 finishBeanFactoryInitialization()
(重点)obtainFefeshBeanFactory() 这里面初始化了ioc并将bean注册到容器中
首先了解下ioc初始化过程:
1.ioc : DefaultListableBeanFactory beanFactory = createBeanFactory();
1.Resource定位过程:指的是beanDefinition的资源定位,由ResourceLoader通过统一的Resource接口完成 > 通过getResourceByPath获取resource的定位
2.BeanDefination的载入:把用户定义好的bean表示成ioc容器内部的数据结构,这个容器内w2部的数据结构就是BeanDefinition > 通过loadBeanDefinitions(beanFactory)实现
3.向ioc容器注册beanDefinition:通过RegisterBeanDefinitions () 中的processBeanDefinition() 将 xml中的bean 转成 beanDefination 并加载容器中
....
....
(重点)finishBeanFactoryInitialization() :负责初始化所有singleton bean
进到getbean继续往下看doGetBean源码
6.刷新后的拓展接口
spring 三级缓存
https://blog.csdn.net/qq_44836294/article/details/107795639
读取spring的配置文件
zk
https://www.cnblogs.com/lsgspace/p/10508180.html