温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

springboot2.0.6中SpringApplication实例初始化

发布时间:2021-06-30 15:24:59 来源:亿速云 阅读:186 作者:chen 栏目:大数据

这篇文章主要介绍“springboot2.0.6中SpringApplication实例初始化”,在日常操作中,相信很多人在springboot2.0.6中SpringApplication实例初始化问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”springboot2.0.6中SpringApplication实例初始化”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

SpringApplication实例初始化

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	// 设置资源加载器属性
	this.resourceLoader = resourceLoader;
	// 校验主要加载资源不能为空,为空抛出异常
	Assert.notNull(primarySources, "PrimarySources must not be null");
	// 设置primarySources(主要来源)属性【后面会交给BeanDefinitionLoader,其分别load这些 primarySources 设置到scanner(扫描仪)中,避免重复扫描这些类进入容器】
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 获取ApplicationContext类型,并设置到webApplicationType属性中
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 获取应用上下文初始化器实例集合,并设置到initializers属性中
	setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
    // 获取应用监听器实例集合,并设置到listeners属性中
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	// 找出main(主入口应用类)类,这里是CoreServerApplication类
	this.mainApplicationClass = deduceMainApplicationClass();
}

   1. webApplicationType 是一个枚举类,用来判断当前的springboot项目是什么类型的。

  1. NONE:既不是运行在 web 容器下的应用并且也不应该启动一个内置的web服务。

  2. SERVLET:是需要运行在基于 servlet 的web应用且需要启动一个内置 servlet-web 服务。

  3. REACTIVE:还是运行在 reactive 的web应用中且需要启动一个 reactive-web 服务。

   2. WebApplicationType.deduceFromClasspath() 获取获取ApplicationContext类型

private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
      "org.springframework.web.context.ConfigurableWebApplicationContext" };
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
      + "web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org."
      + "springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
static WebApplicationType deduceFromClasspath() {
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
   }
   for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
         return WebApplicationType.NONE;
      }
   }
   return WebApplicationType.SERVLET;
}

    根据org.springframework.util.ClassUtils的静态方法 isPresent 判断classpath里面是否有包含 WEBFLUX_INDICATOR_CLASS 并且没有包含 WEBMVC_INDICATOR_CLASS 和 JERSEY_INDICATOR_CLASSERVLET_INDICATOR_CLASSE ,满足条件则表示启动一个 REACTIVE 类型的WEB应用,不满足上述条件且有 SERVLET_INDICATOR_CLASSES 包含的类,则表示启动一个 SERVLET 类型的WEB应用,否则启动一个标准应用。

    是否启动一个WEB应用就是取决于 classpath 下是否满足有 javax.servlet.Servlet 和 org.springframework.web.context.ConfigurableWebApplicationContext 或有 org. springframework.web.reactive.DispatcherHandler 并且没有org.springframework.web.servlet.DispatcherServlet 和 org.glassfish.jersey.servlet.ServletContainer。

3. setInitalizers()

初始化 classpath 下的所有的可用的 ApplicationContextInitializer

    ApplicationContextInitializer 应用程序初始化器,做一些初始化的工作,是 springboot 准备调用 prepareContext 方法准备 spring 容器(此时spring容器已经创建需要refresh)之前调用的用来在这个时间点执行一些操作:比如设置 servletContext 等

    默认情况下,getSpringFActoriesInstances 方法从 spring.factories 文件中找出key为 ApplicationContextInitializer 类型的权限定名称集合有:

  • org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer:

    • 向应用上下文中注册后置处理器(ConfigurationWarningsPostProcessor)

  • org.springframework.boot.context.ContextIdApplicationContextInitializer

    • 设置应用上下文ID,并将上面生成的ContextId(ContextIdApplicationContextInitializer)对象,作为一个单例bean注册到当前应用上下文。

  • org.springframework.boot.context.config.DelegatingApplicationContextInitializer

    • 执行环境属性"context.initializer.classes"指定的初始化器,然后执行这些初始化器。这里实现springboot应用程序自己实现的ApplicationContextInitializer对应用程序做一些初始化工作。

  • org.springframework.boot.context.web.ServerPortInfoApplicationContextInitializer

    • 把自己作为监听器注册到应用上下文中

  • org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer

    • 向应用上下文中注册后置处理器(CachingMetadataReaderFactoryPostProcessor)

  • org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

    • 向应用上下文中注册监听器(ConditionEvaluationReportListener)当应用上下文实现GenercApplicationContext时,则设置“report”属性

springboot2.0.6中SpringApplication实例初始化( 图1-2 spring-boot.jar 中 spring.factories文件中applicationContextInitializer相关)springboot2.0.6中SpringApplication实例初始化

( 图1  执行getSpringFactoriesInstances(ApplicationContextInitializer.class)获取 spring.factories文件中applicationContextInitializer相关的工厂类,并进行初始化)

springboot2.0.6中SpringApplication实例初始化 

( 图2 springApplication对象中instances属性值)

4. setListeners()

初使化 classpath 下的所有的可用的 ApplicationListener

    ApplicationListener 应用程序事件(ApplicationEvent)监听器

    这里的应用程序事件(ApplicationEvent)有应用程序启动事件(ApplicationStartedEvent),失败事件(ApplicationFailedEvent),准备事件(ApplicationPreparedEvent)等

    应用程序事件监听器跟监听事件是绑定的。比如 ConfigServerBootstrapApplicationListener 只跟 ApplicationEnvironmentPreparedEvent 事件绑定, LiquibaseServiceLocatorApplicationListener 只跟 ApplicationStartedEvent 事件绑定,LoggingApplicationListener 跟所有事件绑定等

    默认情况下,getSpringFactoriesInstances 方法从 spring.factories 文件中找出key为 ApplicationListener 类型的,类全限定名称集合有:

  • org.springframework.boot.autoconfigure.BackgroundPreinitializer

  • org.springframework.boot.ClearCachesApplicationListener

  • org.springframework.boot.builder.ParentContextCloserApplicationListener

  • org.springframework.boot.context.FileEncodingApplicationListener

  • org.springframework.boot.context.config.AnsiOutputApplicationListener

  • org.springframework.boot.context.config.ConfigFileApplicationListener

  • org.springframework.boot.context.config.DelegatingApplicationListener

  • org.springframework.boot.context.logging.ClasspathLoggingApplicationListener

  • org.springframework.boot.context.logging.LoggingApplicationListener

  • org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

( 图3-1 spring-boot.jar 中 spring.factories文件中applicationlistener相关)

springboot2.0.6中SpringApplication实例初始化 

( 图3-2 spring-boot.jar 中 spring.factories文件中applicationlistener相关)

springboot2.0.6中SpringApplication实例初始化

( 图4  执行getSpringFactoriesInstances(Applicationlistener.class)获取 spring.factories文件中applicationContextInitializer相关的工厂类,并进行初始化)

springboot2.0.6中SpringApplication实例初始化

  1. 由于spring 有一大堆事件比如 ContextRefreshedEvent,ContextStartedEvent 等等 我们有一个 ApplicationListener 就会监听到这些事件进而做相应的处理。

  2. 其实都是 applicationContext 调用其 publishEvent 发送一些 event,该方法会从很多 listener 中找到对该 event 感兴趣的 listener 调用其 onApplicationEvent,而实现这些的就是 SimpleApplicationEventMulticaster,如果有 Executor,其会优先使用Executor来执行这就可能导致我们的回调是异步的。

  3. applicationContext 还会把event传播到其父容器(我们知道我们常用到AnnotationConfigApplicationContext 已经其有很多父容器)

  4. 这边还有一个策略就是如果 earlyApplicationEvents 存在 则先把事件加入到 earlyApplicationEvents,稍后在发送,如果不存在则直接发送。

  5. applicationevent 中包含时间戳,springboot 启动类(source),还有我们的spring容器

5. 自定义ApplicationContextInitializer 和 ApplicationListener

如果我们想自己在代码中写 ApplicationContextInitializer 和 ApplicationListener 的实现类时候可以采用下列方式:

  • 在项目中建立一个 META-INF/spring.factories 文件,里面以 key=value 存放我们的实现类

  • 手动调用 SpringApplication 的对应添加方法也可以

  • 在我们的 application.properties 里面配置 context.initializer.classes = 实现类(通过 DelegatingApplicationContextInitializer 获取到我们配置的 initializer,进而可以保证在 prepareContext 时候调用),context.listener.classes = 实现类(无法监听到 springboot 启动时候的一些事件,因为那个时候该实现类还未加入容器)

  • 虽然我们使用 @Configuration 来讲我们 ApplicationListener 的实现类加入到spring容器,且也能监听到程序正常运行的一些事件(无法监听到 springboot 启动时候的一些事件,因为那个时候该实现类还未加入容器),但是 我们如果想监听全部的事件最好使用上述三种方式配置这样可以在 springboot 启动的时候就监听

6. getSpringFactoriesInstances()

通过系统加载类去指定目录下根据 propties 文件获取对应的 class 的全限定名称

总结

SpringApplication 的构造方法主要就是设置 Initializers 和 Listeners 同时设置 primaryClass 方便后面先去加载 primaryClass,而且也顺便确定了当前的 springboot 的运行环境

到此,关于“springboot2.0.6中SpringApplication实例初始化”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI