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方法:

 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方法中初始化的。

  接着的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)。

 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:

@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中只是有些排序的逻辑,我就不说了:

 /**
  * 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的这两句代码:

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:

 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首先注册静态监听:

 @Override
 public void addApplicationListener(ApplicationListener<?> listener) {
  synchronized (this.retrievalMutex) {
   this.defaultRetriever.applicationListeners.add(listener);
   this.retrieverCache.clear();
  }
 }

  接着是:

 

 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)的对应方法:

 /**
  * 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:

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的:

@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:

 /**
  * 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的钩子:

 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启动过程完全解析(二),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Spring Boot启动过程全面解析(三)

    我已经很精简了,两篇(Spring Boot启动过程(一).spring Boot启动过程(二))依然没写完,接着来. refreshContext之后的方法是afterRefresh,这名字起的真...好.afterRefresh方法内只调用了callRunners一个方法,这个方法从上下文中获取了所有的ApplicationRunner和CommandLineRunner接口的实现类,并执行这些实现类的run方法.例如Spring Batch的JobLauncherCommandLineRu

  • Spring Boot启动流程分析

    引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始,深入了解一下spring boot的工作原理. 为什么用spring boot 在使用一个东西或者一个工具之前,我们总是会问自己,我为什么要用?用他能给我带来什么好处? * 最大的好处就是spring boot遵从了java**约定大于配置**不用面对一大堆的配置文件,spring boot是根据

  • spring boot在启动项目之后执行的实现方法

    前言 我们在web项目启动之后有时候还会做点其它的东西(比如,导入数据脚本),下面就说说spring-boot里怎么在程序启动后加入自己要执行的东西 方法如下: 新建一个类:BeforeStartup.java @Configuration public class BeforeStartup implements ApplicationListener<ContextRefreshedEvent> { @Autowired private InitDB initDB; @Override p

  • SpringBoot Tomcat启动实例代码详解

    废话不多了,具体内容如下所示: Application configuration class: @SpringBootApplication public class ServletInitializer extends SpringBootServletInitializer { @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return appli

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

    之前在排查一个线上问题时,不得不仔细跑了很多遍Spring Boot的代码,于是整理一下,我用的是1.4.3.RELEASE. 首先,普通的入口,这没什么好说的,我就随便贴贴代码了: SpringApplication.run(Application.class, args); --> public static ConfigurableApplicationContext run(Object source, String... args) { return run(new Object[]

  • spring boot启动加载数据原理分析

    实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求. 为了解决这样的问题,spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现. 创建实现接口 CommandLineRunner 的类,通过@Component注解,就可以实现启动时加载数据项.使用@Order 注解来定义执行顺序. IndexStartupRunner.Java类: import org.springframework.boot.CommandLine

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

    上篇给大家介绍了Spring Boot启动过程完全解析(一),大家可以点击参考下 该说refreshContext(context)了,首先是判断context是否是AbstractApplicationContext派生类的实例,之后调用了强转为AbstractApplicationContext类型并调用它的refresh方法.由于AnnotationConfigEmbeddedWebApplicationContext继承自EmbeddedWebApplicationContext,所以会

  • Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动

    之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServ

  • Spring Boot启动过程(六)之内嵌Tomcat中StandardHost、StandardContext和StandardWrapper的启动教程详解

    StandardEngine[Tomcat].StandardHost[localhost]的启动与StandardEngine不在同一个线程中,它的start: // Start our child containers, if any Container children[] = findChildren(); List<Future<Void>> results = new ArrayList<>(); for (int i = 0; i < childre

  • Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    标题和Spring Boot启动过程(四)之Spring Boot内嵌Tomcat启动很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用LifecycleBase对这一套容器(engine,host,context及wrapper)进行启动并发布诸如configure_start.before_init.after_start的lifecycleEvent事件给相应的监听器(如

  • Spring Boot启动流程断点过程解析

    这篇文章主要介绍了Spring Boot启动流程断点过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 启动入口 跟进run方法 : 一个用来使用默认的配置从特定的源运行SpringApplication的静态帮助类. 这个类有两个重载方法,另一个用来传入多个源.通常,单个参数方法是数组方法的一个特例 创建一个新的SpringApplication实例.这个应用程序上下文会从特定的源加载Beans,这个实例会在调用run方法之前被定制化.

  • 如何在Spring Boot启动时运行定制的代码

    Spring Boot会自动为我们做很多配置,但迟早你需要做一些自定义工作.在本文中,您将学习如何挂钩应用程序引导程序生命周期并在Spring Boot启动时执行代码. 1.执行bean初始化的方法 Spring启动应用程序后运行某些逻辑的最简单方法是将代码作为所选bean引导过程的一部分来执行. 只需创建一个类,将其标记为Spring组件,并将应用程序初始化代码放在带有@PostConstruct注释的方法中.理论上,您可以使用构造函数而不是单独的方法,但将对象的构造与其实际责任分开是一种很好

  • Spring Boot启动时调用自己的非web逻辑

    在spring Boot中,有些代码是WEB功能,例如API等,但是有些逻辑是非WEB,启动时就要调用并持续运行的,该如何加载自己的非WEB逻辑呢? SpringBootApplication类实现CommandLineRunner并覆盖run()方法 @SpringBootApplication public class ZjkApplication implements CommandLineRunner{ /** * @param args */ public static void ma

随机推荐