SpringBoot启动 源码深度解析(一)

SpringBoot 版本 : 2.2.1.RELEASE
Spring 版本 : 5.2.1.RELEASE
入口类: SpringApplication;SpringApplicationBuilder
说明 : 由于SpringBoot建立在Spring之上,所以分析SpringBoot的启动过程其实与Spring是交错进行的,分析的时候会顺带将一些Spring的扩展点也提到
注:本文主要讲解一些比较重要的关键步骤,不能面面俱到,若有疑问,随时保持沟通

SpringBoot启动 源码深度解析(二)
SpringBoot启动 源码深度解析(三)
SpringBoot启动 源码深度解析(四)

1. 启动SpringBoot应用

  • 方式一
@SpringBootApplication(scanBasePackages="com.union.autoseller")
public class SpringCloudDockerWebApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(SpringCloudDockerWebApplication.class)
                .main(SpringVersion.class)
                .run(args);
    }
}
  • 方式二
@SpringBootApplication
public class SpringCloudDockerWebApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudDockerWebApplication .class, args);
    }
}
  • 以上两种方式,分别涉及到 SpringApplicationBuilder 类与 SpringApplication类,SpringApplicationBuilder可以构建出 SpringApplication,执行SpringApplicationBuilder 的run方法的时候本质还是调用的 SpringApplication的run方法,另外通过builder的方式可以指定父子上下文。
    image.png

2. SpringApplication.run(String... args) 函数

image.png
  • 看到 getRunListeners(args)行,此行代码的作用是从 /META-INF/spring.factories 文件中反射获取对应的 SpringApplicationRunListener实例:
    image.png
    image.png

    由于boot中内置了一个事件发布监听器 EventPublishingRunListener
    读者只需要仔细留心一点就可发现:SpringApplicationRunListener实现(EventPublishingRunListener)是缓存在SpringApplicationRunListeners类中得一个List集合中( 所以我们就可以模仿EventPublishingRunListener 来实现自己得事件发布器)。发布事件得具体实施者就是这些 SpringApplicationRunListener 实现去做的
    image.png
  • 了解这些之后下面我们看一下内置得 EventPublishingRunListener事件发布器,此类中有几处很重要得地方
    image.png
    ① 首先就是这个成员变量 SimpleApplicationEventMulticaster 应用事件广播器
    ② 其次是构造器,会初始化广播器,然后获取 SpringApplication中得监听器( 下文会提到监听器得由来),然后一个一个添加到广播器中。看到此处我们心里就大概知道boot中得事件发布到底是怎么回事了,发布事件通知 spring.factories文件中得监听器来处理相应得事件(此处我们也可以自定义监听器来处理自己得逻辑)👍👍例如 Dubbo中得扩展:
    image.png

    作者之所以花费这么大篇幅说明这个事件发布是因为有些内置得监听器很重要(自己实现扩展的话能做的事情也很多):比如 ConfigFileApplicationListener, 处理配置文件加载
  • 紧接着发布 starting 通知事件( 后续还有很多事件
  • 然后看 prepareEnvironment(listeners, applicationArguments) 这行代码。点进去发现如下逻辑:
    image.png
    ,首先判断当前是否存在环境,若存在直接返回,否则会根据 this.webApplicationType 字段判断到底应该创建哪种环境,该字段是在哪里赋值的呢? 原因是我们 执行 SpringApplication.run(SpringCloudDemoApplication.class, args)的时候会去 new SpringApplication()对象,而 SpringApplication的构造方法中,会去调用this.webApplicationType = WebApplicationType.deduceFromClasspath() 行代码初始化 webApplicationType的值。
    image.png
    image.png

    此处还有一个重要的初始化动作 setInitializers跟setListeners方法,跟前面说过的监听器的解析步骤是一样的,这些扩展都是在 /META-INF/spring.factories 文件中配置的。最终返回 WebApplicationType.SERVLET,那么此时会创建 StandardServletEnvironment对象,而我们都知道属性配置离不开环境,那么属性配置在哪里存放呢?跟踪到 抽象类 AbstractEnvironment
    image.png
    ,发现是在创建 StandardServletEnvironment对象的无参构造时初始化抽象类的成员变量,此处拿出来单独说的原因是:有时读者阅读源码的时候可能会感觉这个配置到底存哪了呢?没看到有地方可以存放啊? 我们只要稍加留心即可,至于为什么这么做就不展开了。
  • 紧接着调用 configureEnvironment(...)配置环境信息与激活的Profiles,ConfigurationPropertySources.attach(environment)中会在属性资源最后添加一个ConfigurationPropertySourcesPropertySource类型的名字为configurationProperties 的 PropertySource对象。
  • 打印Logo:此处logo可以自定义,只需编写 banner.txt 在resources下即可。
  • 创建上下文:也是根据 webApplicationType字段决定创建哪种上下文对象,由于是SERVLET,所以此处会实例化 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext,发现无参构造下面又藏着两个初始化动作:
    image.png
  • AnnotatedBeanDefinitionReader :会创建ConditionEvaluator对象(用于判断@Conditional注解),然后调用AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)方法去注册实例
// beanFactory中添加比较器实例
AnnotationAwareOrderComparator
// beanFactory中添加AutowireCandidateResolver得实例化对象,用来处理@Lazy注解 和 @qualifier注解
ContextAnnotationAutowireCandidateResolver

注册所有注解配置处理器bean定义:

// registry中注册bean定义, 用来处理配置类
ConfigurationClassPostProcessor
// registry中注册bean定义,用来处理@Autowired
AutowiredAnnotationBeanPostProcessor
// registry中注册bean定义,但是前提是否启用jsr250支持():javax.annotation.Resource。用来处理@PostConstruct注解与@PreDestroy注解
CommonAnnotationBeanPostProcessor
// registry中注册bean定义, 检查是否提供JPA support,即有没有javax.persistence.EntityManagerFactory类,有的话并且需要引入spring-orm框架,然后才会注册当前处理器。
PersistenceAnnotationBeanPostProcessor
// registry中注册bean定义,当执行上下文得preInstantiateSingletons()方法之后,会触发SmartInitializingSingleton#afterSingletonsInstantiated()得回调,用来处理带有@EventListener注解得类,然后通过默认实现DefaultEventListenerFactory创建ApplicationListener实例
EventListenerMethodProcessor
// registry中注册bean定义,EventListenerFactory得默认实现,,生成ApplicationListener实例
DefaultEventListenerFactory
  • ClassPathBeanDefinitionScanner : 构造器会执行registerDefaultFilters()方法,默认会添加@Component 注解过滤但是也会隐式得注册包含@Component 元注解得注解。例如:@Repository@Service@Controller@Repositoryjavax.annotation.ManagedBeanjavax.inject.Named 或者一些自定义注解
  • 创建完上下文之后,紧接着调用prepareContext(...) 填充上下文属性,将environment添加到上下文中上下文后置处理postProcessApplicationContext()依次调用所有得ApplicationContextInitializer实例(构造方法中解析得initializers,上下文初始化之后,最先可以访问上下文的是initializers,所以我们可以在Dubbo看到类似的扩展)
  • 执行listeners.contextPrepared(context)通知上下文准备完成,如果有Banner就先注册banner,注册springApplicationArguments 单例 bean;注册springBootBanner 单例bean
  • 然后执行load(context)方法加载上下文,方法里面会创建BeanDefinitionLoader对象,同时构造器中会创建AnnotatedBeanDefinitionReader对象,然后执行BeanDefinitionLoaderload()方法。load()方法会根据传入得参数类型分为class、Resource、Package、CharSequence等类型,分别执行不同得load方法,由于SpringBoot启动时得参数基本都是传入启动类得class,所以这里会找到class类型去执行load()方法。调用 AnnotationUtils.findAnnotation(type, Component.class) != null 判断给定得参数资源有没有被@Component注解修饰,有的话会调用org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register方法去注册当前参数class对应得Bean。register() -> registerBean() -> doRegisterBean(), 在doRegisterBean()方法中执行具体得注册处理。
    image.png

    image.png

    image.png

    image.png
  1. 首先创建注解通用beanDefinetion类AnnotatedGenericBeanDefinition,类主包含一个成员:元数据对象类AnnotationMetadata
    构造器中同时会初始化这个元数据注解类,实例对象为:StandardAnnotationMetadata,这个类会保存参数class对应得所有注解
  2. 然后通过调用ConditionEvaluator类得方法shouldSkip方法判断类是否标注了@Conditional条件注解,没有得话直接false。表示不跳过,执行注册,有的话需要判断注解类得元数据是否包含@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean等注解。此时需要将配置阶段ConfigurationPhase设置为PARSE_CONFIGURATION,否则将配置阶段ConfigurationPhase设置为REGISTER_BEAN,递归当前shouldSkip方法。
  3. 设置完属性之后,由于ConfigurationPhase参数不为空,则下一次递归得时候将会跳过if(phase == null)判断逻辑,直接执行后面得逻辑。获取条件类Condition得实例,循环实例化,并添加到到临时集合中。然后对填充完得集合做一次排序。遍历集合判断类型是否是ConfigurationCondition类型,若是,则获取ConfigurationCondition实例对应配置得ConfigurationPhase,目前只有OnBeanCondition配置得是REGISTER_BEAN,若跟当前设置得相同并且不匹配matches方法,则应该跳过注册,否则可以执行注册流程。
  4. org.springframework.boot.autoconfigure.condition.SpringBootCondition#matches(org.springframework.context.annotation.ConditionContext, org.springframework.core.type.AnnotatedTypeMetadata),进到方法内,会调用org.springframework.boot.autoconfigure.condition.SpringBootCondition#getMatchOutcome()抽象方法,具体得条件解析去实现,执行匹配判断逻辑。
  5. 解析类得@Scope注解,调用org.springframework.context.annotation.AnnotationScopeMetadataResolver#resolveScopeMetadata方法
  6. 最后创建一个BeanDefinitionHolder对象用来保存beanDefinetion,接着调用definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry),若当前注解beanDefinetion@Scope属性为ScopedProxyMode.NO,表示不需要代理,则直接返回;否则判断代理模式是否为ScopedProxyMode.TARGET_CLASSCGLIB代理),调用org.springframework.context.annotation.ScopedProxyCreator#createScopedProxy 再调用org.springframework.aop.scope.ScopedProxyUtils#createScopedProxy实际得创建代理类得方法。

总结:@Conditional -> @Scope -> @common annotation :@Lazy/@DependsOn/@Role/@Description -> qualifier 属性验证 -> 自定义得BeanDefinitionCustomizer处理 -> 根据元数据对应得@Scope注解中具体得scopedProxyMode值判断是否需要启动代理模式 以及具体得代理模式 -> 注册bean定义

  • 执行listeners.contextLoaded(context)通知上下文加载完成
  • 紧接着调用refresh方法重点来了该方法会判断是否是AbstractApplicationContext上下文得子类型,是的话会调用 org.springframework.context.support.AbstractApplicationContext#refresh()方法。此处会进入到抽象父类上下文 AbstractApplicationContext中。
  1. ☛ 文章要是勘误或者知识点说的不正确,欢迎评论,毕竟这也是作者通过阅读源码获得的知识,难免会有疏忽!
  2. 要是感觉文章对你有所帮助,不妨点个关注,或者移驾看一下作者的其他文集,也都是干活多多哦,文章也在全力更新中。
  3. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。