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

PHP教程|ASP.NET教程|JAVA教程|ASP教程|编程技术|正则表达式|

服务器之家 - 编程语言 - JAVA教程 - Spring Boot启动过程完全解析(二)

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

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

这篇文章主要介绍了Spring Boot启动过程完全解析(二),需要的朋友可以参考下

上篇给大家介绍了spring boot启动过程完全解析(一),大家可以点击参考下

  该说refreshcontext(context)了,首先是判断context是否是abstractapplicationcontext派生类的实例,之后调用了强转为abstractapplicationcontext类型并调用它的refresh方法。由于annotationconfigembeddedwebapplicationcontext继承自embeddedwebapplicationcontext,所以会执行embeddedwebapplicationcontext的refresh方法,继而执行其中的super.refresh。这个refresh也就是abstractapplicationcontext的refresh方法了,它内部是一个synchronized锁全局的代码块,同样的加锁方法还有这个类里的close和registershutdownhook方法。

  同步代码块中第一个方法preparerefresh,首先会执行annotationconfigembeddedwebapplicationcontext的preparerefresh方法:

?
1
2
3
4
protected void preparerefresh() {
 this.scanner.clearcache();
 super.preparerefresh();
}

  这个super也就是abstractapplicationcontext,它的preparerefresh方法逻辑是:生成启动时间;设置closed状态为false;active状态为true;initpropertysources方法主要是调用了abstractenvironment的getpropertysources方法获取了之前springapplication的prepareenvironment方法中getorcreateenvironment方法准备的各种环境变量及配置并用于初始化servletpropertysources。具体的servletcontextinitparams这些是在环境对象初始化时由各集成级别environment的customizepropertysources方法中初始化的。

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

   接着的getenvironment().validaterequiredproperties()方法实际执行了abstractenvironment中的this.propertyresolver.validaterequiredproperties(),主要是验证了被占位的key如果是required的值不能为null。preparerefresh的最后是初始化this.earlyapplicationevents = new linkedhashset<applicationevent>()。*****

  只够是获取beanfactory实例的方法obtainfreshbeanfactory(),首先在refreshbeanfactory方法中用原子布尔类型判断是否刷新过,beanfactory实例是在createapplicationcontext创建context实例时被创建的,如果没有刷新则设置一个用于序列化的id,id是contextidapplicationcontextinitializer初始化设置的(如未配置该初始化器,是有一个默认objectutils.identitytostring(this)生成的),这个id的生成规则是spring.config.name截取的+":"+server.port的占位截取。设置序列化id时,同时保存了一个id和弱引用defaultlistablebeanfactory实例映射。

  得到了beanfactory后就是preparebeanfactory(beanfactory)了,逻辑是注册了beanclassloader用于注入的bean实例的创建;standardbeanexpressionresolver用于el表达式,比如配置文件或者@value("#{...}")等使用;用resourceeditorregistrar注册属性转换器,比如xml配置的bean属性都是用的字符串配置的要转成真正的属性类型;addbeanpostprocessor(new applicationcontextawareprocessor(this))注册applicationcontextawareprocessor,它的invokeawareinterfaces方法会对实现指定接口的bean调用指定的set方法;ignoredependencyinterface忽略对这些接口的自动装配,比如aware这些是要做独立处理的,不适合通用的方法;然后是有几个类型直接手动注册,比如beanfactory,这个很好理解;接着注册一个后置处理器applicationlistenerdetector的实例,addbeanpostprocessor注册的会按照注册先后顺序执行;这个方法的最后判断了特定的4个bean名字,如果存在会做相应注册,包括loadtimeweaver、environment、systemproperties和systemenvironment。补充一点,在最开始创建实例的时候还执行过ignoredependencyinterface(beannameaware.class);ignoredependencyinterface(beanfactoryaware.class);ignoredependencyinterface(beanclassloaderaware.class)。

?
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
37
38
protected void preparebeanfactory(configurablelistablebeanfactory beanfactory) {
 // tell the internal bean factory to use the context's class loader etc.
 beanfactory.setbeanclassloader(getclassloader());
 beanfactory.setbeanexpressionresolver(new standardbeanexpressionresolver(beanfactory.getbeanclassloader()));
 beanfactory.addpropertyeditorregistrar(new resourceeditorregistrar(this, getenvironment()));
 // configure the bean factory with context callbacks.
 beanfactory.addbeanpostprocessor(new applicationcontextawareprocessor(this));
 beanfactory.ignoredependencyinterface(environmentaware.class);
 beanfactory.ignoredependencyinterface(embeddedvalueresolveraware.class);
 beanfactory.ignoredependencyinterface(resourceloaderaware.class);
 beanfactory.ignoredependencyinterface(applicationeventpublisheraware.class);
 beanfactory.ignoredependencyinterface(messagesourceaware.class);
 beanfactory.ignoredependencyinterface(applicationcontextaware.class);
 // beanfactory interface not registered as resolvable type in a plain factory.
 // messagesource registered (and found for autowiring) as a bean.
 beanfactory.registerresolvabledependency(beanfactory.class, beanfactory);
 beanfactory.registerresolvabledependency(resourceloader.class, this);
 beanfactory.registerresolvabledependency(applicationeventpublisher.class, this);
 beanfactory.registerresolvabledependency(applicationcontext.class, this);
 // register early post-processor for detecting inner beans as applicationlisteners.
 beanfactory.addbeanpostprocessor(new applicationlistenerdetector(this));
 // detect a loadtimeweaver and prepare for weaving, if found.
 if (beanfactory.containsbean(load_time_weaver_bean_name)) {
  beanfactory.addbeanpostprocessor(new loadtimeweaverawareprocessor(beanfactory));
  // set a temporary classloader for type matching.
  beanfactory.settempclassloader(new contexttypematchclassloader(beanfactory.getbeanclassloader()));
 }
 // register default environment beans.
 if (!beanfactory.containslocalbean(environment_bean_name)) {
  beanfactory.registersingleton(environment_bean_name, getenvironment());
 }
 if (!beanfactory.containslocalbean(system_properties_bean_name)) {
  beanfactory.registersingleton(system_properties_bean_name, getenvironment().getsystemproperties());
 }
 if (!beanfactory.containslocalbean(system_environment_bean_name)) {
  beanfactory.registersingleton(system_environment_bean_name, getenvironment().getsystemenvironment());
 }
}

   之后到了refresh的postprocessbeanfactory方法,首先是会走到annotationconfigembeddedwebapplicationcontext的override,需要注意的一点是,这是web环境,如果不是是不会加载这个上下文的,也就不会这么走。它重写的第一步是先走super也就是embeddedwebapplicationcontext的postprocessbeanfactory,这里又注册了个后置处理器webapplicationcontextservletcontextawareprocessor的实例,构造参数是this,也就是当前上下文,同时忽略servletcontextaware接口,这个接口是用于获取servletcontext的,为什么要忽略呢,我猜应该是因为我们既然有了web应用并且内嵌servlet的上下文实例,还要servletcontext的实现就没什么用了,还有可能出现冲突的问题,有空我再确认下。然后是配置的basepackages和annotatedclasses:

?
1
2
3
4
5
6
7
8
9
10
@override
 protected void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) {
  super.postprocessbeanfactory(beanfactory);
  if (this.basepackages != null && this.basepackages.length > 0) {
   this.scanner.scan(this.basepackages);
  }
  if (this.annotatedclasses != null && this.annotatedclasses.length > 0) {
   this.reader.register(this.annotatedclasses);
  }
 }

  到了invokebeanfactorypostprocessors方法,这个方法就是执行之前注册的beanfactory后置处理器的地方。代码一目了然,postprocessorregistrationdelegate的invokebeanfactorypostprocessors中只是有些排序的逻辑,我就不说了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * instantiate and invoke all registered beanfactorypostprocessor beans,
 * respecting explicit order if given.
 * <p>must be called before singleton instantiation.
 */
protected void invokebeanfactorypostprocessors(configurablelistablebeanfactory beanfactory) {
 postprocessorregistrationdelegate.invokebeanfactorypostprocessors(beanfactory, getbeanfactorypostprocessors());
 // detect a loadtimeweaver and prepare for weaving, if found in the meantime
 // (e.g. through an @bean method registered by configurationclasspostprocessor)
 if (beanfactory.gettempclassloader() == null && beanfactory.containsbean(load_time_weaver_bean_name)) {
  beanfactory.addbeanpostprocessor(new loadtimeweaverawareprocessor(beanfactory));
  beanfactory.settempclassloader(new contexttypematchclassloader(beanfactory.getbeanclassloader()));
 }
}

   beanfactory后置处理器执行之后是注册bean的后置处理器方法registerbeanpostprocessors。例如new beanpostprocessorchecker(beanfactory, beanprocessortargetcount)会在bean没有合适的后置处理器时记条info级日志。applicationlistenerdetector也注册了一个。

  initmessagesource这个方法在我这没什么用,都说是国际化的,随便百度一下一堆一堆的,而且其实严格来说这篇多数不属于spring boot的部分,这方法我就不细写了。

  initapplicationeventmulticaster方法主要也就是初始化并注册applicationeventmulticaster的这两句代码:          

?
1
2
this.applicationeventmulticaster = new simpleapplicationeventmulticaster(beanfactory);
   beanfactory.registersingleton(application_event_multicaster_bean_name, this.applicationeventmulticaster);

   onrefresh也是根据环境不同加载的上下文不同而不同的,用于支持子类扩展出来的上下文特定的逻辑的。embeddedwebapplicationcontext的onrefresh首先依然是super.onrefresh,逻辑就是初始化了主题;

createembeddedservletcontainer方法名我就不翻译了,一般情况下是使用getbeanfactory .getbeannamesfortype方法找到embeddedservletcontainerfactory类型的实例,这也就是我之前那个问题解决过程中,为什么只要排除掉tomcat引用,引入jetty引用就可以自动换成jetty的原因。创建容器的过程中初始化方法selfinitialize注册了filter和mappingforurlpatterns等,代码在abstractfilterregistrationbean等onstartup,这里就不细说了,如果能抽出时间说说之前查问题的时候查的容器代码再说。然后初始化propertysources,servletcontextinitparams和servletconfiginitparams:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void initservletpropertysources(
  mutablepropertysources propertysources, servletcontext servletcontext, servletconfig servletconfig) {
 assert.notnull(propertysources, "'propertysources' must not be null");
 if (servletcontext != null && propertysources.contains(standardservletenvironment.servlet_context_property_source_name) &&
   propertysources.get(standardservletenvironment.servlet_context_property_source_name) instanceof stubpropertysource) {
  propertysources.replace(standardservletenvironment.servlet_context_property_source_name,
    new servletcontextpropertysource(standardservletenvironment.servlet_context_property_source_name, servletcontext));
 }
 if (servletconfig != null && propertysources.contains(standardservletenvironment.servlet_config_property_source_name) &&
   propertysources.get(standardservletenvironment.servlet_config_property_source_name) instanceof stubpropertysource) {
  propertysources.replace(standardservletenvironment.servlet_config_property_source_name,
    new servletconfigpropertysource(standardservletenvironment.servlet_config_property_source_name, servletconfig));
 }
}

  registerlisteners首先注册静态监听:

?
1
2
3
4
5
6
7
@override
public void addapplicationlistener(applicationlistener<?> listener) {
 synchronized (this.retrievalmutex) {
  this.defaultretriever.applicationlisteners.add(listener);
  this.retrievercache.clear();
 }
}

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

  接着是:

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

 registerlisteners的最后,初始化过的earlyapplicationevents如果有事件,这时候会被发布。

  finishbeanfactoryinitialization结束beanfactory的初始化并初始化所有非延迟加载的单例。事实上我们自定义的单例bean都是在这里getbean方法初始化的,所以如果注册的bean特别多的话,这个过程就是启动过程中最慢的。初始化开始前先设置configurationfrozen为true,并this.frozenbeandefinitionnames = stringutils.tostringarray ( this. beandefinitionnames )。如果有bean实例实现了smartinitializingsingleton会有后置处理触发,不包括延迟加载的。例如:org.springframework.context.event. internaleventlistenerprocessor会触发eventlistenermethodprocessor的aftersingletonsinstantiated方法对所有对象(object的子类)处理。

  finishrefresh:refresh的最后一步,发布相应事件。同样先执行embeddedwebapplicationcontext中对应方法的super(embeddedwebapplicationcontext)的对应方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * finish the refresh of this context, invoking the lifecycleprocessor's
 * onrefresh() method and publishing the
 * {@link org.springframework.context.event.contextrefreshedevent}.
 */
protected void finishrefresh() {
 // initialize lifecycle processor for this context.
 initlifecycleprocessor();
 // propagate refresh to lifecycle processor first.
 getlifecycleprocessor().onrefresh();
 // publish the final event.
 publishevent(new contextrefreshedevent(this));
 // participate in livebeansview mbean, if active.
 livebeansview.registerapplicationcontext(this);
}

   初始化生命周期处理器,逻辑是判断beanfactory中是否已经注册了lifecycleprocessor,没有就new一个defaultlifecycleprocessor并setbeanfactory(beanfactory),然后将它赋值给私有lifecycleprocessor类型的this变量。然后执行生命周期处理器的onrefresh,其中先startbeans,被start的beans是通过getbeannamesfortype(lifecycle.class, false, false)从beanfactory中取出来的,例如endpointmbeanexporter和lifecycleprocessor,会去调用bean的start方法,endpointmbeanexporter的start中执行 locateandregisterendpoints方法并设置running属性为true,这个过程加了reentrantlock锁。bean都启动完会设置处理器的running为true。刷新完会发布contextrefreshedevent事件,这个事件除了都有的记录时间还执行了configurationpropertiesbindingpostprocessor的freelocalvalidator方法,我这的逻辑是实际上执行了validatorfactoryimpl的close方法。这个逻辑的最后会检查一个配置spring.livebeansview.mbeandomain是否存在,有就会创建一个mbeanserver:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void registerapplicationcontext(configurableapplicationcontext applicationcontext) {
  string mbeandomain = applicationcontext.getenvironment().getproperty(mbean_domain_property_name);
  if (mbeandomain != null) {
   synchronized (applicationcontexts) {
    if (applicationcontexts.isempty()) {
     try {
      mbeanserver server = managementfactory.getplatformmbeanserver();
      applicationname = applicationcontext.getapplicationname();
      server.registermbean(new livebeansview(),
        new objectname(mbeandomain, mbean_application_key, applicationname));
     }
     catch (throwable ex) {
      throw new applicationcontextexception("failed to register livebeansview mbean", ex);
     }
    }
    applicationcontexts.add(applicationcontext);
   }
  }
 }

  finishrefresh最后会启动前面创建的内嵌容器,并发布embeddedservletcontainerinitializedevent事件,启动这一部分算是容器的逻辑了,有机会整理容器逻辑再细写,我这里是tomcat的:

?
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
@override
 public void start() throws embeddedservletcontainerexception {
  try {
   addpreviouslyremovedconnectors();
   connector connector = this.tomcat.getconnector();
   if (connector != null && this.autostart) {
    startconnector(connector);
   }
   checkthatconnectorshavestarted();
   tomcatembeddedservletcontainer.logger
     .info("tomcat started on port(s): " + getportsdescription(true));
  }
  catch (connectorstartfailedexception ex) {
   stopsilently();
   throw ex;
  }
  catch (exception ex) {
   throw new embeddedservletcontainerexception(
     "unable to start embedded tomcat servlet container", ex);
  }
  finally {
   context context = findcontext();
   contextbindings.unbindclassloader(context, getnamingtoken(context),
     getclass().getclassloader());
  }
 }

  然后是resetcommoncaches:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * reset spring's common core caches, in particular the {@link reflectionutils},
 * {@link resolvabletype} and {@link cachedintrospectionresults} caches.
 * @since 4.2
 * @see reflectionutils#clearcache()
 * @see resolvabletype#clearcache()
 * @see cachedintrospectionresults#clearclassloader(classloader)
 */
protected void resetcommoncaches() {
 reflectionutils.clearcache();
 resolvabletype.clearcache();
 cachedintrospectionresults.clearclassloader(getclassloader());
}

  refreshcontext的最后是注册shutdown的钩子:

?
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
if (this.registershutdownhook) {
  try {
   context.registershutdownhook();
  }
  catch (accesscontrolexception ex) {
   // not allowed in some environments.
  }
 }
 
/**
 * register a shutdown hook with the jvm runtime, closing this context
 * on jvm shutdown unless it has already been closed at that time.
 * <p>delegates to {@code doclose()} for the actual closing procedure.
 * @see runtime#addshutdownhook
 * @see #close()
 * @see #doclose()
 */
@override
public void registershutdownhook() {
 if (this.shutdownhook == null) {
  // no shutdown hook registered yet.
  this.shutdownhook = new thread() {
   @override
   public void run() {
    synchronized (startupshutdownmonitor) {
     doclose();
    }
   }
  };
  runtime.getruntime().addshutdownhook(this.shutdownhook);
 }
}

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

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

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

延伸 · 阅读

精彩推荐
  • JAVA教程解析Java设计模式编程中命令模式的使用

    解析Java设计模式编程中命令模式的使用

    这篇文章主要介绍了Java设计模式编程中命令模式的使用,在一些处理请求响应的场合经常可以用到命令模式的编程思路,需要的朋友可以参考下 ...

    卡奴达摩4992020-03-31
  • JAVA教程Java设计模式之Strategy模式

    Java设计模式之Strategy模式

    Strategy模式即策略模式,就是将一个算法的不同实现封装成一个个单独的类,这些类实现同一个接口,使用者直接使用该接口来访问具体的算法。这个样子...

    java教程网3652020-05-29
  • JAVA教程浅谈java Collection中的排序问题

    浅谈java Collection中的排序问题

    下面小编就为大家带来一篇浅谈java Collection中的排序问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...

    jingxian2462020-07-12
  • JAVA教程java调用回调机制详解

    java调用回调机制详解

    这篇文章主要介绍了java调用回调机制详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小...

    带妳心菲4182020-08-01
  • JAVA教程Windows下Java+MyBatis框架+MySQL的开发环境搭建教程

    Windows下Java+MyBatis框架+MySQL的开发环境搭建教程

    这篇文章主要介绍了Windows下Java+MyBatis框架+MySQL的开发环境搭建教程,Mybatis对普通SQL语句的支持非常好,需要的朋友可以参考下 ...

    红烧狮子头5162020-04-15
  • JAVA教程Springboot web项目打包实现过程解析

    Springboot web项目打包实现过程解析

    这篇文章主要介绍了Springboot web项目打包实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以...

    Auler4712020-08-31
  • JAVA教程Java基础之如何学好Java

    Java基础之如何学好Java

    这篇文章已经是有数年“网龄”的老文,不过在今天看来仍然经典。如何学习java?本篇文章可以说也是面对编程初学者的一篇指导文章,其中对于如何学习...

    hebedich4962019-12-03
  • JAVA教程java书店系统毕业设计 用户模块(3)

    java书店系统毕业设计 用户模块(3)

    这篇文章主要介绍了java书店系统毕业设计,第三步系统总体设计,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 ...

    xanlv3372020-06-22