服务器之家:专注于服务器技术及软件下载分享
分类导航

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);
  }

   也就是一个静态方法,调用了构造函数创建实例,构造的参数是object数组,这里new这个数组的时候传入了一个元素就是启动类的类对象实例(一般就是“new object[] { application.class” }),构造函数里调用了一个initialize方法。

  springapplication的initialize方法,首先在object数组有值的情况下将数组放入一个final的类实例私有object的set集合中;然后用deducewebenvironment方法判断当前应用环境是否是web环境,判断逻辑是看classpath是否同时存在javax.servlet.servlet和org.springframework.web.context.configurablewebapplicationcontext,缺一就认为不是。然后,调用setinitializers方法,设置类实例的私有list<applicationcontextinitializer<?>>类型变量initializers:

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

  设置的时候会先new,也就是说这方法每次都是整体更换,不会追加。这个方法的参数都是各个模块中配置在meta-inf/spring.factories中的key为org.springframework.context.applicationcontextinitializer的值,这些类都是接口applicationcontextinitializer<c extends configurableapplicationcontext>的泛型实现。

?
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>中,并用反射创建这些名字的实例。

Spring Boot启动过程完全解析(一)

  setinitializers方法之后又是setinitializers,参数同上都是getspringfactoriesinstances方法获取,只不过这次参数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实例方法(注意不是上面的静态方法)。一进run方法首先启动了stopwatch,这个stopwatch的功能在类的注释写可,大概意思是这是个简单的秒表,用于在开发过程中方便程序员调试性能等,非线程安全,不建议用于生产。configureheadlessproperty设置使用headless,对于只有远程登录使用的服务器来说这样性能要好一些。接着是加载用于这个run方法启动过程的监听器,依然是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);
}

  先是加载所有可用监听,然后初始化springapplicationrunlisteners对象,最后循环启动所有springapplicationrunlistener监听。启动监听的方法:

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

  applicationstartedevent实例化传了两个参数,先看第一个参数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方法的参数包含springapplication.class和this,这两个参数被传入createspringfactoriesinstances方法:

Spring Boot启动过程完全解析(一)

  可以看到,是通过反射创建实例的时候,将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作为参数传入simpleapplicationeventmulticaster的multicastevent方法。multicastevent首先获取applicationlistener,使用getapplicationlisteners方法,这个方法中抛开对listener做了一些缓存类工作外,主要就是将事件和对应的监听器做了下是否支持的验证,返回通过了retrieveapplicationlisteners中通过了supportsevent验证的监听器集合,这里就体现出了resolvabletype的作用,它保存了类型的信息同时对泛型类型也支持。

Spring Boot启动过程完全解析(一)

   得到了这些匹配的监听器后,判断当前executor是否被设置过,如果为null则同步循环执行所有:invokelistener(listener, event);如果不为null则:           

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

  监听器执行的时候也会先判断是否是该由自己处理的事件,例如:

?
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);
 }
}

  监听启动后,只准备一些启动参数,和环境变量prepareenvironment方法先是读取了应用的启动参数和profile配置,然后用listeners.environmentprepared(environment)传给监听器:

?
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的情况,会多执行一步environment = converttostandardenvironment(environment)转换。

  之后的printbanner就不细说了,如果你在resource下自定义了一个banner.txt文件,启动时会输出内容,否则输出:

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

   接着创建configurableapplicationcontext实例,方法也很简单,如果是web环境就beanutils.instantiate一个org.springframework.boot.context.embedded. annotationconfigembeddedwebapplicationcontext的实例并强转为configurableapplicationcontext,否则用org.springframework.context.annotation. annotationconfigapplicationcontext的实例强转。

  创建failureanalyzers实例,记录了configurableapplicationcontext实例中需要关注的部分,如果启动出错了可以据此分析,可以配置,具体的逻辑依然是老方法spring.factories:

Spring Boot启动过程完全解析(一)

  不同的analyzer关注不同的部分,自己可以扩展配置,最后preparefailureanalyzers方法给所有analyzer实例setbeanfactory(context.getbeanfactory()),一旦启动过程进入catch,被注册的analyzer实例的analyze方法就会被触发执行,分析结果会被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的实例并执行其initialize方法,例如加载环境变量信息、注册embeddedservletcontainerinitializedevent的监听、注册cachingmetadatareaderfactorypostprocessor等。listeners.contextprepared(context)由于eventpublishingrunlistener的contextprepared是空的,先不说了。logstartupinfo部分初始化了logger,然后根据配置情况打印了启动或运行以及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中注册启动参数(applicationarguments)到bean工厂,包括logger、commandlineargs等。然后加载bean定义的来源并根据其中配置加载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),加载监听器到context并广播applicationpreparedevent事件。

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

以上所述是小编给大家介绍的spring boot启动过程完全解析(一),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!

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

延伸 · 阅读

精彩推荐