成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

SpringBoot啟動過程完全解析(一)

之前在排查一個線上問題時,不得不仔細(xì)跑了很多遍Spring Boot的代碼,于是整理一下,我用的是1.4.3.RELEASE。

為鞍山等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及鞍山網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站設(shè)計制作、做網(wǎng)站、鞍山網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

首先,普通的入口,這沒什么好說的,我就隨便貼貼代碼了:

SpringApplication.run(Application.class, args);
-->
  public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
  }
  public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
  }

   也就是一個靜態(tài)方法,調(diào)用了構(gòu)造函數(shù)創(chuàng)建實(shí)例,構(gòu)造的參數(shù)是Object數(shù)組,這里new這個數(shù)組的時候傳入了一個元素就是啟動類的類對象實(shí)例(一般就是“new Object[] { Application.class” }),構(gòu)造函數(shù)里調(diào)用了一個initialize方法。

SpringApplication的initialize方法,首先在Object數(shù)組有值的情況下將數(shù)組放入一個final的類實(shí)例私有Object的Set集合中;然后用deduceWebEnvironment方法判斷當(dāng)前應(yīng)用環(huán)境是否是web環(huán)境,判斷邏輯是看Classpath是否同時存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,缺一就認(rèn)為不是。然后,調(diào)用setInitializers方法,設(shè)置類實(shí)例的私有List<ApplicationContextInitializer<?>>類型變量initializers:

 public void setInitializers(
   Collection<? extends ApplicationContextInitializer<?>> initializers) {
  this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
  this.initializers.addAll(initializers);
 }

設(shè)置的時候會先new,也就是說這方法每次都是整體更換,不會追加。這個方法的參數(shù)都是各個模塊中配置在META-INF/spring.factories中的key為org.springframework.context.ApplicationContextInitializer的值,這些類都是接口ApplicationContextInitializer<C extends ConfigurableApplicationContext>的泛型實(shí)現(xiàn)。

 private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
   Class<?>[] parameterTypes, Object... args) {
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  // Use names and ensure unique to protect against duplicates
  Set<String> names = new LinkedHashSet<String>(
    SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
    classLoader, args, names);
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
 }

使用SpringFactoriesLoader.loadFactoryNames方法去取上面說的被配置的ApplicationContextInitializer的名字放進(jìn)Set<String>中,并用反射創(chuàng)建這些名字的實(shí)例。

Spring Boot啟動過程完全解析(一)

setInitializers方法之后又是setInitializers,參數(shù)同上都是getSpringFactoriesInstances方法獲取,只不過這次參數(shù)Class<T> type泛型類型是org.springframework.context.ApplicationListener。

Spring Boot啟動過程完全解析(一)

   initialize方法的最后一個步是設(shè)置實(shí)例的Class<?>類型私有屬性mainApplicationClass,獲取設(shè)置值的方法deduceMainApplicationClass:

private Class<?> deduceMainApplicationClass() {
  try {
   StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
   for (StackTraceElement stackTraceElement : stackTrace) {
    if ("main".equals(stackTraceElement.getMethodName())) {
     return Class.forName(stackTraceElement.getClassName());
    }
   }
  }
  catch (ClassNotFoundException ex) {
   // Swallow and continue
  }
  return null;
 }

實(shí)例化SpringApplication后調(diào)用了它的run實(shí)例方法(注意不是上面的靜態(tài)方法)。一進(jìn)run方法首先啟動了StopWatch,這個StopWatch的功能在類的注釋寫可,大概意思是這是個簡單的秒表,用于在開發(fā)過程中方便程序員調(diào)試性能等,非線程安全,不建議用于生產(chǎn)。configureHeadlessProperty設(shè)置使用Headless,對于只有遠(yuǎn)程登錄使用的服務(wù)器來說這樣性能要好一些。接著是加載用于這個run方法啟動過程的監(jiān)聽器,依然是getSpringFactoriesInstances方法,這次的類型是org.springframework.boot.SpringApplicationRunListener:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
  

Spring Boot啟動過程完全解析(一)

 SpringApplicationRunListeners(Log log,
   Collection<? extends SpringApplicationRunListener> listeners) {
  this.log = log;
  this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
 }

先是加載所有可用監(jiān)聽,然后初始化SpringApplicationRunListeners對象,最后循環(huán)啟動所有SpringApplicationRunListener監(jiān)聽。啟動監(jiān)聽的方法:

 @Override
 public void started() {
  this.initialMulticaster
    .multicastEvent(new ApplicationStartedEvent(this.application, this.args));
 }

ApplicationStartedEvent實(shí)例化傳了兩個參數(shù),先看第一個參數(shù)this.application是怎么來的,實(shí)例的SpringApplication的run方法中,用于獲取SpringApplicationRunListener,也就是前面說的getSpringFactoriesInstances被調(diào)用時:

 private SpringApplicationRunListeners getRunListeners(String[] args) {
  Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
    SpringApplicationRunListener.class, types, this, args));
 }

getSpringFactoriesInstances方法的參數(shù)包含SpringApplication.class和this,這兩個參數(shù)被傳入createSpringFactoriesInstances方法:

Spring Boot啟動過程完全解析(一)

可以看到,是通過反射創(chuàng)建實(shí)例的時候,將SpringApplication中的this傳進(jìn)來EventPublishingRunListener構(gòu)造的,然后EventPublishingRunListener構(gòu)造:

public EventPublishingRunListener(SpringApplication application, String[] args) {
  this.application = application;
  this.args = args;
  this.initialMulticaster = new SimpleApplicationEventMulticaster();
  for (ApplicationListener<?> listener : application.getListeners()) {
   this.initialMulticaster.addApplicationListener(listener);
  }
 }

最后在構(gòu)造ApplicationStartedEvent時傳給它的基類EventObject的protected不可序列化屬性source。實(shí)例化ApplicationStartedEvent后instance.getClass()并包裝為ResolvableType類型以保存類型信息,并將它和event作為參數(shù)傳入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先獲取ApplicationListener,使用getApplicationListeners方法,這個方法中拋開對listener做了一些緩存類工作外,主要就是將事件和對應(yīng)的監(jiān)聽器做了下是否支持的驗(yàn)證,返回通過了retrieveApplicationListeners中通過了supportsEvent驗(yàn)證的監(jiān)聽器集合,這里就體現(xiàn)出了ResolvableType的作用,它保存了類型的信息同時對泛型類型也支持。

Spring Boot啟動過程完全解析(一)

   得到了這些匹配的監(jiān)聽器后,判斷當(dāng)前Executor是否被設(shè)置過,如果為null則同步循環(huán)執(zhí)行所有:invokeListener(listener, event);如果不為null則:           

executor.execute(new Runnable() {
     @Override
     public void run() {
      invokeListener(listener, event);
     }
    });

監(jiān)聽器執(zhí)行的時候也會先判斷是否是該由自己處理的事件,例如:

 @Override
 public void onApplicationEvent(ApplicationEvent event) {
  if (event instanceof ApplicationEnvironmentPreparedEvent) {
   onApplicationEnvironmentPreparedEvent(
     (ApplicationEnvironmentPreparedEvent) event);
  }
  if (event instanceof ApplicationPreparedEvent) {
   onApplicationPreparedEvent(event);
  }
 }

監(jiān)聽啟動后,只準(zhǔn)備一些啟動參數(shù),和環(huán)境變量prepareEnvironment方法先是讀取了應(yīng)用的啟動參數(shù)和profile配置,然后用listeners.environmentPrepared(environment)傳給監(jiān)聽器:

 public void environmentPrepared(ConfigurableEnvironment environment) {
  this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
    this.application, this.args, environment));
 }

   接著判斷如果environment是org.springframework.web.context.ConfigurableWebEnvironment的實(shí)例,但webEnvironment不是true,也就是說存在org.springframework.web.context.ConfigurableWebEnvironmen但不存在javax.servlet.Servlet的情況,會多執(zhí)行一步environment = convertToStandardEnvironment(environment)轉(zhuǎn)換。

之后的printBanner就不細(xì)說了,如果你在resource下自定義了一個banner.txt文件,啟動時會輸出內(nèi)容,否則輸出:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

   接著創(chuàng)建ConfigurableApplicationContext實(shí)例,方法也很簡單,如果是web環(huán)境就BeanUtils.instantiate一個org.springframework.boot.context.embedded. AnnotationConfigEmbeddedWebApplicationContext的實(shí)例并強(qiáng)轉(zhuǎn)為ConfigurableApplicationContext,否則用org.springframework.context.annotation. AnnotationConfigApplicationContext的實(shí)例強(qiáng)轉(zhuǎn)。

創(chuàng)建FailureAnalyzers實(shí)例,記錄了ConfigurableApplicationContext實(shí)例中需要關(guān)注的部分,如果啟動出錯了可以據(jù)此分析,可以配置,具體的邏輯依然是老方法spring.factories:

Spring Boot啟動過程完全解析(一)

不同的Analyzer關(guān)注不同的部分,自己可以擴(kuò)展配置,最后prepareFailureAnalyzers方法給所有Analyzer實(shí)例setBeanFactory(context.getBeanFactory()),一旦啟動過程進(jìn)入catch,被注冊的Analyzer實(shí)例的analyze方法就會被觸發(fā)執(zhí)行,分析結(jié)果會被loggedExceptions.add(exception)加入到拋出的異常中:

private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
  for (FailureAnalyzer analyzer : analyzers) {
   FailureAnalysis analysis = analyzer.analyze(failure);
   if (analysis != null) {
    return analysis;
   }
  }
  return null;
 }

例如:NoSuchBeanDefinitionFailureAnalyzer

 @Override
 protected FailureAnalysis analyze(Throwable rootFailure,
   NoSuchBeanDefinitionException cause, String description) {
  if (cause.getNumberOfBeansFound() != 0) {
   return null;
  }
  List<AutoConfigurationResult> autoConfigurationResults = getAutoConfigurationResults(
    cause);
  StringBuilder message = new StringBuilder();
  message.append(String.format("%s required %s that could not be found.%n",
    description == null ? "A component" : description,
    getBeanDescription(cause)));
  if (!autoConfigurationResults.isEmpty()) {
   for (AutoConfigurationResult provider : autoConfigurationResults) {
    message.append(String.format("\t- %s%n", provider));
   }
  }
  String action = String.format("Consider %s %s in your configuration.",
    (!autoConfigurationResults.isEmpty()
      ? "revisiting the conditions above or defining" : "defining"),
    getBeanDescription(cause));
  return new FailureAnalysis(message.toString(), action, cause);
 }

   prepareContext方法中postProcessApplicationContext會在this.beanNameGenerator存在的情況下加載自定義命名策略,然后在this.resourceLoader存在的情況下為context設(shè)置resourceLoader和classLoader。applyInitializers方法調(diào)用之前加載的Initializer的實(shí)例并執(zhí)行其initialize方法,例如加載環(huán)境變量信息、注冊EmbeddedServletContainerInitializedEvent的監(jiān)聽、注冊CachingMetadataReaderFactoryPostProcessor等。listeners.contextPrepared(context)由于EventPublishingRunListener的contextPrepared是空的,先不說了。logStartupInfo部分初始化了logger,然后根據(jù)配置情況打印了啟動或運(yùn)行以及profile是否配置的日志:

protected void logStartupInfo(boolean isRoot) {
  if (isRoot) {
   new StartupInfoLogger(this.mainApplicationClass)
     .logStarting(getApplicationLog());
  }
 }
 protected Log getApplicationLog() {
  if (this.mainApplicationClass == null) {
   return logger;
  }
  return LogFactory.getLog(this.mainApplicationClass);
 }
 public void logStarting(Log log) {
  Assert.notNull(log, "Log must not be null");
  if (log.isInfoEnabled()) {
   log.info(getStartupMessage());
  }
  if (log.isDebugEnabled()) {
   log.debug(getRunningMessage());
  }
 }
 protected void logStartupProfileInfo(ConfigurableApplicationContext context) {
  Log log = getApplicationLog();
  if (log.isInfoEnabled()) {
   String[] activeProfiles = context.getEnvironment().getActiveProfiles();
   if (ObjectUtils.isEmpty(activeProfiles)) {
    String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
    log.info("No active profile set, falling back to default profiles: "
      + StringUtils.arrayToCommaDelimitedString(defaultProfiles));
   }
   else {
    log.info("The following profiles are active: "
      + StringUtils.arrayToCommaDelimitedString(activeProfiles));
   }
  }
 }

   接著prepareContext中注冊啟動參數(shù)(applicationArguments)到bean工廠,包括logger、commandLineArgs等。然后加載bean定義的來源并根據(jù)其中配置加載bean,這里的sources就是初始化啟動類時傳進(jìn)來的那個sources:

 BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
    Assert.notNull(registry, "Registry must not be null");
    Assert.notEmpty(sources, "Sources must not be empty");
    this.sources = sources;
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
    this.xmlReader = new XmlBeanDefinitionReader(registry);
    if (isGroovyPresent()) {
      this.groovyReader = new GroovyBeanDefinitionReader(registry);
    }
    this.scanner = new ClassPathBeanDefinitionScanner(registry);
    this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
  }

注意下面的sources是待加載的,和上面這段不是同一個:

 public int load() {
    int count = 0;
    for (Object source : this.sources) {
      count += load(source);
    }
    return count;
  }
  private int load(Object source) {
    Assert.notNull(source, "Source must not be null");
    if (source instanceof Class<?>) {
      return load((Class<?>) source);
    }
    if (source instanceof Resource) {
      return load((Resource) source);
    }
    if (source instanceof Package) {
      return load((Package) source);
    }
    if (source instanceof CharSequence) {
      return load((CharSequence) source);
    }
    throw new IllegalArgumentException("Invalid source type " + source.getClass());
  }

類型不同加載過程不同,其中Class<?>加載過程大概是通過BeanDefinitionLoader調(diào)用AnnotatedBeanDefinitionReader的registerBean方法:

public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
    }
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName());
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    if (qualifiers != null) {
      for (Class<? extends Annotation> qualifier : qualifiers) {
        if (Primary.class == qualifier) {
          abd.setPrimary(true);
        }
        else if (Lazy.class == qualifier) {
          abd.setLazyInit(true);
        }
        else {
          abd.addQualifier(new AutowireCandidateQualifier(qualifier));
        }
      }
    }
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }

可以看到有生成方法名,設(shè)置默認(rèn)注入的實(shí)例、延遲以及過濾等等,注入的過程包括初始化一些信息,如構(gòu)造、內(nèi)部類、注解等: 

protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {
    setConstructorArgumentValues(cargs);
    setPropertyValues(pvs);
  }
  public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
    super(introspectedClass);
    this.annotations = introspectedClass.getAnnotations();
    this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
  }

   其他三種比如有的有輸入流什么的就不細(xì)總結(jié)了,這部分介紹Spring IOC的相關(guān)文章應(yīng)該不少。

   prepareContext方法最后listeners.contextLoaded(context),加載監(jiān)聽器到context并廣播ApplicationPreparedEvent事件。

咱最近用的github:https://github.com/saaavsaaa

以上所述是小編給大家介紹的Spring Boot啟動過程完全解析(一),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對創(chuàng)新互聯(lián)網(wǎng)站的支持!

標(biāo)題名稱:SpringBoot啟動過程完全解析(一)
轉(zhuǎn)載來于:http://jinyejixie.com/article0/ggigoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、全網(wǎng)營銷推廣、Google、關(guān)鍵詞優(yōu)化、建站公司、網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)
新河县| 敖汉旗| 连州市| 河源市| 乐平市| 东乌珠穆沁旗| 萨迦县| 房山区| 太原市| 赞皇县| 海兴县| 确山县| 乐至县| 墨玉县| 莎车县| 崇文区| 望奎县| 富锦市| 定远县| 白城市| 青田县| 嘉峪关市| 英超| 高阳县| 太康县| 平和县| 铁岭市| 长寿区| 尚义县| 布拖县| 固原市| 资溪县| 舒兰市| 东辽县| 景德镇市| 莲花县| 什邡市| 乌鲁木齐市| 承德县| 磴口县| 女性|