当前位置: 首页 > news >正文

辽宁大学网站怎么做/东莞seo技术培训

辽宁大学网站怎么做,东莞seo技术培训,应用镜像 wordpress,那些网站做网批概述今天我们来聊一下Spring的启动流程 以及SpringCloud的父子容器的初始化过程。这个在平时工作中面试中也是出现频率比较高的知识点。特别是SpringCloud的父子容器的概念,如果你的项目中引入了Feign和Ribbon组件 却不知道SpringCloud底层是怎么管理的 在实际开发中…

概述

今天我们来聊一下Spring的启动流程 以及SpringCloud的父子容器的初始化过程。这个在平时工作中面试中也是出现频率比较高的知识点。特别是SpringCloud的父子容器的概念,如果你的项目中引入了Feign和Ribbon组件 却不知道SpringCloud底层是怎么管理的 在实际开发中排查问题是非常困难的。

容器

在微服务的环境中 我们说的IOC容器实际上有三层意思:bootstrap容器,Spring容器,微服务组件容器,如下图所示

049b50e86831b2e5da6ecaa7dc893ac1.png

  1. bootstrapContext 我们常说的SpringCloud容器,由监听器创建 用来初始化SpringCloud上下文。

  2. springbootContext 我们接触最多的容器 平时用的就是这个容器

  3. NamedContextFactory 可以理解为微服务组件的容器工厂,用于管理所有组件容器

Bootstrap容器启动流程

由于启动代码非常多 所以我们文章不可能全部截取 后面贴的代码只是启动容器的主要流程代码

 public static void main(String[] args) {     //进入run方法 会发现最终是new 一个SpringApplication实例 然后调用实例的run方法来启动    SpringApplication.run(SpringcoudApplication.class, args);  }

上面的一行代码实际上做了两件事情

  1. 实例化一个SpringApplication对象

  2. 执行SpringApplication的run方法

实例化一个SpringApplication对象:

public SpringApplication(ResourceLoader resourceLoader, Class>... primarySources) {     this.resourceLoader = resourceLoader;     Assert.notNull(primarySources, "PrimarySources must not be null");     this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));     this.webApplicationType = WebApplicationType.deduceFromClasspath();     //下面两行代码 非常重要 1,使用SPI加载classpath下面所有key为ApplicationContextInitializer      //全限定名的配置。 2,使用SPI加载classpath下面所有key为ApplicationListener的配置     setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));     setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));     this.mainApplicationClass = deduceMainApplicationClass();   }

上面的注释也提到了 实例化SpringApplication最重要的两行代码就是通过SPI的方式加载 ApplicationContextInitializer和ApplicationListener。我们下面跟踪代码看一下加载了哪些ApplicationListener。75cea90659ea652f0c2f8e1585632219.png
看到上图我这里一共加载了13个listener,这里注意 项目依赖的jar包不同这里面可能会有变化。只需要注意第一个listener BootstrapApplicationListener 。这个监听器监听ApplicationEnvironmentPreparedEvent事件

执行SpringApplication的run方法:

StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;Collection exceptionReporters = new ArrayList<>();configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {  ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);  //创建一个Environment实例  重点是在这里   ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);    ...略...

继续跟进prepareEnvironment 这一部分代码我们在 SpringBoot源码系列:Environment机制深入分析有详细描述

//创建一个ConfigurableEnvironment实例  ConfigurableEnvironment environment = getOrCreateEnvironment();  //配置ConfigurableEnvironment   configureEnvironment(environment, applicationArguments.getSourceArgs());  //这里会发一个ApplicationEnvironmentPreparedEvent事件,这样是不是和刚才的BootstrapApplicationListener联系上了。  //BootstrapApplicationListener监听的就是ApplicationEnvironmentPreparedEvent事件啊,所以很显然这里会执行BootstrapApplicationListener的onApplicationEvent方法  listeners.environmentPrepared(environment);  bindToSpringApplication(environment);  if (!this.isCustomEnvironment) {    environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,        deduceEnvironmentClass());  }  ConfigurationPropertySources.attach(environment);  return environment;

通过上面代码分析 spring在初始化环境变量的时候会发送ApplicationEnvironmentPreparedEvent事件,而BootstrapApplicationListener又会监听ApplicationEnvironmentPreparedEvent这个事件,所以在springboot容器没有创建之前 又到了BootstrapApplicationListener这个监听器这里。
接下来我们就看一下onApplicationEvent方法

@Override  public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {    ConfigurableEnvironment environment = event.getEnvironment();    //可以看到还可以通过spring.cloud.bootstrap.enabled=false来关闭bootstrap容器    //关闭了就不会实例化springcloud容器    if (!environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class,true)) {      return;    }//这里相当于是一个判断 容器只初始化一次 因为该监听器会执行多遍。为什么这个监听器会执行多遍?//因为第一次调用SpringApplication.run()会执行到创建环境变量的代码 在prepareEnvironment方法中会发送事件//但是当BootstrapApplicationListener监听到事件的时候又会通过SpringApplicationBuilder//来构建一个SpringApplication对象执行其run方法  所以该监听器 最少执行两次 执行几次根据你引入的包来决定不是绝对的 //如果环境变量中已经加载了bootstrap配置 那么久不会继续执行了    if(environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {      return;    }    ConfigurableApplicationContext context = null;  //找到对应配置的名字   spring.cloud.bootstrap.name:bootstrap  如果有配置  //spring.cloud.bootstrap.name属性 则返回这个属性对应的值 没有返回默认值bootstrap  //注意 这里从environment中取属性  此时的environment还没来得及加载spring的配置文件 所以  //这个属性配置在系统属性或者 环境变量 或者servlet初始参数可以生效  在配置文件中不可能生效    String configName = environment        .resolvePlaceholders("${spring.cloud.bootstrap.name:bootstrap}");    //这里检查我们有没有在ApplicationContextInitializer中定制化容器 这个接口我们在前面提到     //是通过spring.factories加载进SpringApplication对象中去的 我们这里不做进一步的展开 后面再对    //ApplicationContextInitializer做详细描述    for (ApplicationContextInitializer> initializer :event.getSpringApplication()        .getInitializers()) {      if (initializer instanceof ParentContextApplicationContextInitializer) {        context = findBootstrapContext(            (ParentContextApplicationContextInitializer) initializer,            configName);      }    }    if (context == null) {    //这个方法是核心方法 我们继续跟进该方法      context = bootstrapServiceContext(environment, event.getSpringApplication(),          configName);      event.getSpringApplication()          .addListeners(new CloseContextOnFailureApplicationListener(context));    }    apply(context, event.getSpringApplication(), environment);  }

继续跟进bootstrapServiceContext方法

 //重新实例化一个Environment 但这个不是StandardServletEnvironment 我们通过之前的        //Environment机制深入分析 知道environment变量其实是一个StandardServletEnvironment,这也很好理解 bootstrap容器不需要构建在web上面         //所以目前bootstrapEnvironment里面只有两个配置 一个是系统变量 还有一个是系统环境变量        StandardEnvironment bootstrapEnvironment = new StandardEnvironment();    MutablePropertySources bootstrapProperties = bootstrapEnvironment        .getPropertySources();        //这里是将系统变量和系统环境变量移除 因为不需要 最终bootstrapEnvironment是要和        //最开始初始化的那个Environment合并的 系统变量和系统环境变量在Environment中已经存在    for (PropertySource> source : bootstrapProperties) {      bootstrapProperties.remove(source.getName());    }    //可以通过spring.cloud.bootstrap.location系统变量来设置bootstrap配置文件的位置    String configLocation = environment        .resolvePlaceholders("${spring.cloud.bootstrap.location:}");    Map<String, Object> bootstrapMap = new HashMap<>();    bootstrapMap.put("spring.config.name", configName);    bootstrapMap.put("spring.main.web-application-type", "none");    if (StringUtils.hasText(configLocation)) {      bootstrapMap.put("spring.config.location", configLocation);    }    //向bootstrap环境变量 增加一个bootstrap配置    bootstrapProperties.addFirst(        new MapPropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME, bootstrapMap));    //又将environment中的配置放入 创建的配置中 这里需要排除占位配置        for (PropertySource> source : environment.getPropertySources()) {      if (source instanceof StubPropertySource) {        continue;      }      bootstrapProperties.addLast(source);    }    //这里我们启动springboot应用差不多 只不过是没有打印banner 环境变量是自定义的环境变量    //自定义环境变量任然可以参考我们之前关于spring环境变量的文章 非servlet容器     SpringApplicationBuilder builder = new SpringApplicationBuilder()        .profiles(environment.getActiveProfiles()).bannerMode(Mode.OFF)        .environment(bootstrapEnvironment)        // Don't use the default properties in this builder        .registerShutdownHook(false).logStartupInfo(false)        .web(WebApplicationType.NONE);    final SpringApplication builderApplication = builder.application();    if (builderApplication.getMainApplicationClass() == null) {      builder.main(application.getMainApplicationClass());    }    if (environment.getPropertySources().contains("refreshArgs")) {      builderApplication      .setListeners(filterListeners(builderApplication.getListeners()));    }    //这里可以稍稍注意一下 最终builder的sources 值会设置到SpringApplication的primarySources字段中  这里先不过多介绍我们后面文章会对这个展开     builder.sources(BootstrapImportSelectorConfiguration.class);     //这里其实就是容器创建刷新的流程 后面讲springboot容器的时候会涉及这里就不做展开    final ConfigurableApplicationContext context = builder.run();    context.setId("bootstrap");    //为springboot容器添加父容器 这里你可能会问 到这里 springboot容器都还没开始创建呢?    //是的 所以这里只是创建了一个AncestorInitializer加入到SpringApplication 等到springboot容器启动的时候会执行    //SpringApplication中的所有的ApplicationContextInitializer 执行的时候自动就会把当前的容器设置为 父容器    //内部实现为  new ParentContextApplicationContextInitializer(this.parent)    //      .initialize(context);    addAncestorInitializer(application, context);    bootstrapProperties.remove(BOOTSTRAP_PROPERTY_SOURCE_NAME);    //这里实际是把两个environment合并 所以到这bootstrap配置文件内容已经成功的放入    //environment    mergeDefaultProperties(environment.getPropertySources(), bootstrapProperties);    return context;

到这里 bootstrap容器创建完成了。并且调用了SpringApplicationBuilder的run方法启动了容器。

要点回顾

  • SpringApplication run方法会创建环境变量对象 创建完成会发送ApplicationEnvironmentPreparedEvent事件。

  • BootstrapApplicationListener监听器监听了这个事件。

  • spring是怎么加载BootstrapApplicationListener监听器的?通过在SpringApplication的构造方法中通过SPI加载的。所以我们没有引入SpringCloud相关包的时候 是没有这个监听器的。

  • addAncestorInitializer(application, context); 设置父容器。在springboot容器还没有被创建的的时候。具体请看注释

  • 关于Environment的文章参考 SpringBoot源码系列:Environment机制深入分析(一)

关注我

8da80696dbeb755276060bf206f72a6f.png

http://www.lbrq.cn/news/1617157.html

相关文章:

  • 沧州网站建设多少钱/百度竞价代运营
  • 通信公司网站建设/网站首页面设计
  • 专业建设报告/seo发包排名软件
  • php动态网站开发代码/做网站用什么软件
  • 网站建设 天秀网络/友情链接交换网
  • 郴州制作网站设计较好的公司/成都seo工程师
  • 政务网站建设的重要性/网络销售怎么聊客户
  • 地方房产网站APP如何做/百度电话人工服务
  • 网站开发的技术简介/网络营销的应用
  • 做网站电话沧州/简述网站建设的一般流程
  • 网站开发 移民/网易企业邮箱
  • 英文网站群建设/外链查询工具
  • 做b2b网站价格/网站建设与管理属于什么专业
  • magento怎么做b2b网站/seo自学网官网
  • 南京建设监理协会网站/it培训学校
  • wordpress启用特色/商品标题seo是什么意思
  • 如何做网站的源码/南昌seo搜索优化
  • 青岛模板网站建设价格/设计网站模板
  • 安徽 网站制作/企业管理培训公司排行榜
  • 淮南哪里做网站/长沙正规竞价优化推荐
  • 商城网站开发需求/网推怎么做
  • 企业主页设计/网站快速优化排名排名
  • 06628 网页制作与网站开发/什么是网络销售
  • web网站是什么意思/湖南网站优化
  • 西宁做网站的公司/如何联系百度平台客服
  • 网站建设长春/专门做排行榜的软件
  • 飞沐网站建设公司北京/最新搜索关键词
  • 佛山建企业网站/网站友链交换平台
  • 做网站1500全包/知名网络营销推广
  • 济南做html5网站建设/怎么做网络平台
  • [硬件电路-120]:模拟电路 - 信号处理电路 - 在信息系统众多不同的场景,“高速”的含义是不尽相同的。
  • SpringMVC全局异常处理+拦截器使用+参数校验
  • 网络层协议IP
  • LaTeX 复杂图形绘制教程:从基础到进阶
  • 客户服务自动化:如何用CRM减少50%人工工单?
  • 搭建 Mock 服务,实现前端自调