一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術|正則表達式|

服務器之家 - 編程語言 - JAVA教程 - Spring Boot啟動過程完全解析(一)

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

2020-09-16 14:15draculav JAVA教程

這篇文章主要介紹了Spring Boot啟動過程完全解析(一),需要的朋友可以參考下

之前在排查一個線上問題時,不得不仔細跑了很多遍spring boot的代碼,于是整理一下,我用的是1.4.3.release。

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

?
1
2
3
4
5
6
7
8
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)方法,調用了構造函數(shù)創(chuàng)建實例,構造的參數(shù)是object數(shù)組,這里new這個數(shù)組的時候傳入了一個元素就是啟動類的類對象實例(一般就是“new object[] { application.class” }),構造函數(shù)里調用了一個initialize方法。

  springapplication的initialize方法,首先在object數(shù)組有值的情況下將數(shù)組放入一個final的類實例私有object的set集合中;然后用deducewebenvironment方法判斷當前應用環(huán)境是否是web環(huán)境,判斷邏輯是看classpath是否同時存在javax.servlet.servlet和org.springframework.web.context.configurablewebapplicationcontext,缺一就認為不是。然后,調用setinitializers方法,設置類實例的私有l(wèi)ist<applicationcontextinitializer<?>>類型變量initializers:

?
1
2
3
4
5
public void setinitializers(
  collection<? extends applicationcontextinitializer<?>> initializers) {
 this.initializers = new arraylist<applicationcontextinitializer<?>>();
 this.initializers.addall(initializers);
}

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

?
1
2
3
4
5
6
7
8
9
10
11
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的名字放進set<string>中,并用反射創(chuàng)建這些名字的實例。

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

  setinitializers方法之后又是setinitializers,參數(shù)同上都是getspringfactoriesinstances方法獲取,只不過這次參數(shù)class<t> type泛型類型是org.springframework.context.applicationlistener。

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

   initialize方法的最后一個步是設置實例的class<?>類型私有屬性mainapplicationclass,獲取設置值的方法deducemainapplicationclass:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
 }

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

?
1
2
3
4
# run listeners
org.springframework.boot.springapplicationrunlistener=\
org.springframework.boot.context.event.eventpublishingrunlistener
  

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

?
1
2
3
4
5
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)聽的方法:

?
1
2
3
4
5
@override
public void started() {
 this.initialmulticaster
   .multicastevent(new applicationstartedevent(this.application, this.args));
}

  applicationstartedevent實例化傳了兩個參數(shù),先看第一個參數(shù)this.application是怎么來的,實例的springapplication的run方法中,用于獲取springapplicationrunlistener,也就是前面說的getspringfactoriesinstances被調用時:

?
1
2
3
4
5
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)建實例的時候,將springapplication中的this傳進來eventpublishingrunlistener構造的,然后eventpublishingrunlistener構造:

?
1
2
3
4
5
6
7
8
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);
  }
 }

  最后在構造applicationstartedevent時傳給它的基類eventobject的protected不可序列化屬性source。實例化applicationstartedevent后instance.getclass()并包裝為resolvabletype類型以保存類型信息,并將它和event作為參數(shù)傳入simpleapplicationeventmulticaster的multicastevent方法。multicastevent首先獲取applicationlistener,使用getapplicationlisteners方法,這個方法中拋開對listener做了一些緩存類工作外,主要就是將事件和對應的監(jiān)聽器做了下是否支持的驗證,返回通過了retrieveapplicationlisteners中通過了supportsevent驗證的監(jiān)聽器集合,這里就體現(xiàn)出了resolvabletype的作用,它保存了類型的信息同時對泛型類型也支持。

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

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

?
1
2
3
4
5
6
executor.execute(new runnable() {
     @override
     public void run() {
      invokelistener(listener, event);
     }
    });

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

?
1
2
3
4
5
6
7
8
9
10
@override
public void onapplicationevent(applicationevent event) {
 if (event instanceof applicationenvironmentpreparedevent) {
  onapplicationenvironmentpreparedevent(
    (applicationenvironmentpreparedevent) event);
 }
 if (event instanceof applicationpreparedevent) {
  onapplicationpreparedevent(event);
 }
}

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

?
1
2
3
4
public void environmentprepared(configurableenvironment environment) {
 this.initialmulticaster.multicastevent(new applicationenvironmentpreparedevent(
   this.application, this.args, environment));
}

   接著判斷如果environment是org.springframework.web.context.configurablewebenvironment的實例,但webenvironment不是true,也就是說存在org.springframework.web.context.configurablewebenvironmen但不存在javax.servlet.servlet的情況,會多執(zhí)行一步environment = converttostandardenvironment(environment)轉換。

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

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: spring boot ::        (v1.4.3.release)

   接著創(chuàng)建configurableapplicationcontext實例,方法也很簡單,如果是web環(huán)境就beanutils.instantiate一個org.springframework.boot.context.embedded. annotationconfigembeddedwebapplicationcontext的實例并強轉為configurableapplicationcontext,否則用org.springframework.context.annotation. annotationconfigapplicationcontext的實例強轉。

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

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

  不同的analyzer關注不同的部分,自己可以擴展配置,最后preparefailureanalyzers方法給所有analyzer實例setbeanfactory(context.getbeanfactory()),一旦啟動過程進入catch,被注冊的analyzer實例的analyze方法就會被觸發(fā)執(zhí)行,分析結果會被loggedexceptions.add(exception)加入到拋出的異常中:

?
1
2
3
4
5
6
7
8
9
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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@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設置resourceloader和classloader。applyinitializers方法調用之前加載的initializer的實例并執(zhí)行其initialize方法,例如加載環(huán)境變量信息、注冊embeddedservletcontainerinitializedevent的監(jiān)聽、注冊cachingmetadatareaderfactorypostprocessor等。listeners.contextprepared(context)由于eventpublishingrunlistener的contextprepared是空的,先不說了。logstartupinfo部分初始化了logger,然后根據(jù)配置情況打印了啟動或運行以及profile是否配置的日志:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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就是初始化啟動類時傳進來的那個sources:

?
1
2
3
4
5
6
7
8
9
10
11
12
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是待加載的,和上面這段不是同一個:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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調用annotatedbeandefinitionreader的registerbean方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
  }

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

?
1
2
3
4
5
6
7
8
9
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;
  }

   其他三種比如有的有輸入流什么的就不細總結了,這部分介紹spring ioc的相關文章應該不少。

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

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

以上所述是小編給大家介紹的spring boot啟動過程完全解析(一),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網(wǎng)站的支持!

原文鏈接:http://www.cnblogs.com/saaav/p/6259405.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美18-19 | 精品一区二区三区在线成人 | 手机亚洲第一页 | 久久嫩草影院网站 | 国产亚洲精品美女久久久 | 九九热这里只有精品2 | 日韩在线免费播放 | www.尤物在线 | 激情婷婷成人亚洲综合 | 视频国产精品 | 不良研究所地址一 | 特级一级全黄毛片免费 | 欧美在线视频一区 | 4444亚洲国产成人精品 | 亚州综合网 | 男人j进女屁股视频在线观看 | 网友自拍咪咪爱 | 青草园网站在线观看 | 久久综合色超碰人人 | 国产成人精品777 | 俄罗斯bbbbbbxxxxxx | 扒开老师挠尿口到崩溃刑罚 | 无人区乱码区1卡2卡三卡在线 | 操穴勤 | 国产日韩欧美在线一区二区三区 | 国产成人无精品久久久久国语 | 国产一区国产二区国产三区 | 九九成人免费视频 | 亚洲精品一区波多野结衣 | 国产v日韩v欧美v精品专区 | 国产第一页无线好源 | 国产日韩欧美成人 | 精品一区二区三区在线播放 | 黑人群性xxx | 日本大片在线 | 国产激情一区二区三区成人91 | 色老板美国在线观看 | 嫩草影院永久一二三入口 | 国产亚洲精品一区久久 | 国语自产自拍秒拍在线视频 | 小sao货水好多真紧h的视频 |