百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

十年之重修SpringBoot启动&自动装载&Bean加载过程

wxin55 2025-05-11 19:03 3 浏览 0 评论

总结

Springboot的自动装载,完全是依赖Bean的自动注册,其中默认的规则,是把需要自动装载的bean全名称编辑在spring.factories(2.7之后的版本,还支持.imports文件)中,然后基于@EnableAutoConfiguration注解的@Import({
AutoConfigurationImportSelector.class})注册,通过
AutoConfigurationImportSelector类,扫描当前classpath下的所有spring.factories文件,完成对bean的扫描与注册,然后后续完全依托Spring对Bean的初始化即可。

在完成Bean的定义注册之后,会启动Bean的初始化,优先完成依赖Bean的初始化,如果出现环形依赖会直接报错,然后对Bean进行实例化,在初始化的后置处理器中,针对Bean会进行判定,是否需要进行代理,如果需要会返回一个代理的Bean。

SpringBoot启动

SpringBoot的启动过程,主要是设置服务启动监听、容器启动监、加载当前环境的配置文件,其中调用refreshContext刷新上下文,这里主要是启动Spring容器,以及创建webServer(Tomcat),在上下文启动成功之后,会回调启动webServer。

	// 一切源于这个注解与main函数
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
/*************************** @SpringBootApplication注解 **********************/
// 标明这个是springboot配置类
@SpringBootConfiguration
// 自动装载配置类,springboot自动装载就是基于这个注解实现
// @Import({AutoConfigurationImportSelector.class})
@EnableAutoConfiguration
// 扫描bean路径,未配置则是Application.class所在包
@ComponentScan
public @interface SpringBootApplication {
}

// SpringApplication.run是一个静态方法,后边调用到这个run方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  // 这里先初始化了一个SpringApplication对象,然后再次调用真正干活的run方法
  return new SpringApplication(primarySources).run(args);
}

/************************************ 初始化SpringApplication ************************************/
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  // 目前资源加载器为null
  this.resourceLoader = resourceLoader;
  Assert.notNull(primarySources, "PrimarySources must not be null");
  // 这里保存主源,会用于后边bean装载的扫描范围
  this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
  // 推断服务类型,这里只有两种一个是响应式REACTIVE,一个是SERVLET
  // 推断方式也很简单,就是通过Class.forName()去查找有没有例如MVC的DispatcherServlet等类似web服务的类
  this.webApplicationType = WebApplicationType.deduceFromClasspath();
  // 找到服务启动初始化bootstrapRegistryInitializers列表
  this.bootstrapRegistryInitializers = new ArrayList<>(
    getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
  // 找到应用上下文初始化ApplicationContextInitializer列表
  setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
  // 找到应用监听器ApplicationListener列表
  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  this.mainApplicationClass = deduceMainApplicationClass();
  /**
		1. getSpringFactoriesInstances:
    这个方法会加载当前classpath下的所有META-INF/spring.factories文件
    加载里边配置的自动装载的class全名,然后通过传参class类型进行过滤,
    例如Mybatis的jar包下配置:
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
									org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
    再基于反射对class进行初始化实例。
    2.  BootstrapRegistryInitializer、ApplicationContextInitializer、ApplicationListener 这三个
    都是初始化过程,用户可以实现接口之后自定义,springboot会在启动时一起执行。
  **/
}

/************************************ run方法执行 ************************************/
public ConfigurableApplicationContext run(String... args) {
  long startTime = System.nanoTime();
  // 这里创建默认的BootstrapContext,同时会执行在上边注册的BootstrapRegistryInitializer
  DefaultBootstrapContext bootstrapContext = createBootstrapContext();
  ConfigurableApplicationContext context = null;
  configureHeadlessProperty();
  // 获取注册的SpringApplicationRunListener监听器,同样是基于spring.factores注册,可以自定义
  // 监听SpringApplication的启动、完成、失败等,会根据接口回调
  SpringApplicationRunListeners listeners = getRunListeners(args);
  // 启动监听
  listeners.starting(bootstrapContext, this.mainApplicationClass);
  try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    // 运行环境准备,加载配置文件,广播环境准备事件等
    ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    configureIgnoreBeanInfo(environment);
    // 打印启动时springboot横幅
    Banner printedBanner = printBanner(environment);
    // 创建一个application上线文,基于webApplicationType生成的一个
    // AnnotationConfigServletWebServerApplicationContext 
    context = createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    // 准备上下文环境,调用注册的ApplicationContextInitializer,注册一些bean
    prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    // 更新上下文环境,启动spring容器进行bean装载,启动tomcat,
    refreshContext(context);
    // 后置处理,目前为空,用户可以自定义
    afterRefresh(context, applicationArguments);
    Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
    if (this.logStartupInfo) {
      // 打印启动日志
      new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
    }
    // 容器启动完成广播
    listeners.started(context, timeTakenToStartup);
    // 调用实现ApplicationRunner CommandLineRunner 接口的类
    callRunners(context, applicationArguments);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, listeners);
    throw new IllegalStateException(ex);
  }
  try {
    Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
    // 广播 ready事件
    listeners.ready(context, timeTakenToReady);
  }
  catch (Throwable ex) {
    handleRunFailure(context, ex, null);
    throw new IllegalStateException(ex);
  }
  return context;
}

SpringBoot自动载入

SpringBoot的自动载入,本质上还是Bean自动初始化的一个过程,其中自动装载,主要是依托Application.class上的@SpringBootApplication注解,里边包含了EnableAutoConfiguration注解,而该注解又引入了@Import({
AutoConfigurationImportSelector.class}),在装载时会扫描classpath下的所有spring.factories和*
.AutoConfiguration.imports文件,对里边申明的类进行装载注入。Bean的扫描、自动装载等会在
invokeBeanFactoryPostProcessors环节完成。在onRefresh环节,会创建tomcat实例,待容器启动完毕之后,基于lifecycle接口触发回调,启动webServer。

/************************** refresh上下文 **********************************/
// springboot在启动时完成配置加载、事件通知等,期间会调用refresh方法开始更新上下文
// 这里会启动spring容器加载bean,自动装载,启动tomcat
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

    // 刷新准备工作,清空scanner缓存,修改上下文状态
    prepareRefresh();

    // 更新beanFactory相关属性
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    // 通过beanFactory的ignoreDependencyInterface忽略部分bean,registerResolvableDependency 手动注入bean,以及注册了一些bean
    prepareBeanFactory(beanFactory);

    try {
      // 当前class是抽象类,这里允许上下文容器实现类设置一下beanFactory相关范围与限制
      postProcessBeanFactory(beanFactory);

      StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
      // 这里会根据上下文配置的扫描路径、配置类、自动装载等,把beanDefinition存入beanFactory
      invokeBeanFactoryPostProcessors(beanFactory);

      // 注册bean后置处理器
      registerBeanPostProcessors(beanFactory);
      beanPostProcess.end();

      // Initialize message source for this context.
      initMessageSource();

      // Initialize event multicaster for this context.
      initApplicationEventMulticaster();

      // 这里由于是web服务,会createWebServer()创建一个tomcat
      onRefresh();

      // 注册监听器
      registerListeners();

      // 在这里对beanFactory里边的bean进行初始化,涉及bean的实例化,依赖注入,代理类生成
      finishBeanFactoryInitialization(beanFactory);

      // 结束刷新
      finishRefresh();
    }

    catch (BeansException ex) {
      if (logger.isWarnEnabled()) {
        logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
      }

      // Destroy already created singletons to avoid dangling resources.
      destroyBeans();

      // Reset 'active' flag.
      cancelRefresh(ex);

      // Propagate exception to caller.
      throw ex;
    }

    finally {
      // Reset common introspection caches in Spring's core, since we
      // might not ever need metadata for singleton beans anymore...
      resetCommonCaches();
      contextRefresh.end();
    }
  }
}

/************* bean装载invokeBeanFactoryPostProcessors ***************/
public static void invokeBeanFactoryPostProcessors(
  ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
  Set<String> processedBeans = new HashSet<>();
	// 当前的bean工厂为 DefaultListableBeanFactory
  if (beanFactory instanceof BeanDefinitionRegistry) {
    // ... ...
    // 省略查找beanDefinition的后置处理器,这里currentRegistryProcessors里边为ConfigurationClassPostProcessor
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
    // ... ...
    // 省略其他后置处理器场景
}

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
 	// ... ... 这里省略掉,需要处理bean的筛选,把当前阶段beanDefinitionHolder整理出来
  // 这里边包含Application.class这个类的BeanDefinitionHolder
  Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
  Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
  do {
    StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
    // 从candidates里边的beanHolder开始处理,比如applicaiton.class上边还有注解,进一步进行扫描装载
    parser.parse(candidates);
    parser.validate();
  }
}
	/*************************** 进行bean的扫描装载 ***********************************/
  public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
        if (bd instanceof AnnotatedBeanDefinition) {
          // 扫描注解bean,如果当前bean本身也是一个配置类,还会继续扫描,同时会扫描Import、@bean等
          // 由于一个bean本身可能是配置类,触发另外的扫描范围,故里边会不停地递归扫
          parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
          parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        }
        else {
          parse(bd.getBeanClassName(), holder.getBeanName());
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
          "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
    }
		// 这里延迟加载,处理springboot自动装载bean,
    this.deferredImportSelectorHandler.process();
  }
  
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, String declaringClass) {
    // ......
    // 这里省略对scanner扫描器的初始化,比如扫描“*.class”,忽略、包含,省略对basePackages扫描范围的确定
    // 由于未定义,故默认以Application.class包路径以及以下包
  	// doScan进行包扫描,以及bean注册
    return scanner.doScan(StringUtils.toStringArray(basePackages));
  }
  
  /************* deferredImportSelectorHandler.process()自动装载 *************************/
  // process()最后调用到此方法,
  // 因为Application.class上的@EnableAutoConfiguration 通过import引入了 AutoConfigurationImportSelector
  // 执行AutoConfigurationImportSelector.getAutoConfigurationEntry
  protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
    } else {
      AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
      // 这里会扫描classpath下所有spring.factories文件,对配置的进行bean进行加载,针对EnableAutoConfiguration配置的class进行自动装载
      // 在springboot2.7之后,支持META-INF/spring/*.AutoConfiguration.imports 文件进行读取,自动装载里边的class
      List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
      configurations = this.<String>removeDuplicates(configurations);
      Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
      this.checkExcludedClasses(configurations, exclusions);
      configurations.removeAll(exclusions);
      configurations = this.getConfigurationClassFilter().filter(configurations);
      this.fireAutoConfigurationImportEvents(configurations, exclusions);
      return new AutoConfigurationEntry(configurations, exclusions);
    }
  }

Spring Bean初始化

通过
invokeBeanFactoryPostProcessors完成bean的定义之后,在
finishBeanFactoryInitialization环节,对Bean进行初始化,主要是通过getBean方法,如果bean是工厂bean则返回工厂生产的bean对象。getBean时会先去缓存中获取,找不到就会进行bean创建,创建过程先进性属性填充流程,即先初始化依赖对象,然后初始化自身,在初始化的后置处理器流程中,会判定当前bean是否需要代理,如果需要则返回代理bean。

public void preInstantiateSingletons() throws BeansException {
  List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
  for (String beanName : beanNames) {
    RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
    if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
      if (isFactoryBean(beanName)) {
        Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
        if (bean instanceof FactoryBean) {
          // 工厂bean的处理逻辑
        }
      }
      else {
        // 普通bean的处理逻辑
        getBean(beanName);
      }
    }
  }

  for (String beanName : beanNames) {
    Object singletonInstance = getSingleton(beanName);
    if (singletonInstance instanceof SmartInitializingSingleton) {
      // 如果当前bean实现了SmartInitializingSingleton接口,则在所有bean初始化完成之后,
      // 调用afterSingletonsInstantiated方法
    }
  }
}
}

/********************** getbean最后调用doGetBean ******************************/
protected <T> T doGetBean(
  String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
  // 规范名称
  String beanName = transformedBeanName(name);
  Object beanInstance;

  // 这里尝试从三级缓存里获取
  Object sharedInstance = getSingleton(beanName);
  if (sharedInstance != null && args == null) {
    // 如果缓存里不为空
    // 返回bean实例,可以是当前对象,也可以是工厂bean生产的实例
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
  }
  // 缓存为空,需要构建bean
  else {
    // Fail if we're already creating this bean instance:
    // We're assumably within a circular reference.
    if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
    }

    // Check if bean definition exists in this factory.
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // 如果父工厂不为空,则先从父工厂查找
    }
		// ......
    if (!typeCheckOnly) {
      // 将bean添加到已创建或即将创建
      markBeanAsCreated(beanName);
    }
    // ......
    try {
      // 针对当前bean注解dependsOn 进行初始化
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
        for (String dep : dependsOn) {
 					// 省略依赖注册过程,最后会递归调用doGetBean方法
        }
      }

      // Create bean instance.
      if (mbd.isSingleton()) {
        // getSingleton获取到单例之后,会清除掉二、三级缓存
        sharedInstance = getSingleton(beanName, () -> {
          try {
          // 进行单利模式的bean创建
          return createBean(beanName, mbd, args);
      } catch (BeansException ex) {
        destroySingleton(beanName);
        throw ex;
      }
    });
    beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
  }
  // 省略其他模式如Prototype示例的创建流程
} catch (BeansException ex) {
  // ......
} finally {
  beanCreation.end();
}
}
return adaptBeanInstance(name, beanInstance, requiredType);
}

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
 	// ......
  // Initialize the bean instance.
  Object exposedObject = bean;
  try {
    // 这里填充bean,即针对autoware注入的依赖,会进行初始化
    populateBean(beanName, mbd, instanceWrapper);
    // 初始化bean
    exposedObject = initializeBean(beanName, exposedObject, mbd);
  }
  catch (Throwable ex) {
 		// ......
  }
 	// ......
  return exposedObject;
}

// 填充依赖属性
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
 	// ......
    for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
      PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
 			// 根据属性后置处理器,进行递归初始化依赖bean处理
    }
 // ......
}

	protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	 	// ......
		try {
      // 初始化bean回调
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
 			// ......
		}
		if (mbd == null || !mbd.isSynthetic()) {
      // 基于后置处理器,对bean进行代理处理
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}
		return wrappedBean;
	}

不积小流,无以成江海!

相关推荐

Java框架 —— Spring简介

简介一般来说,Spring指的是SpringFramework,它提供了很多功能,例如:控制反转(IOC)、依赖注入(DI)、切面编程(AOP)、事务管理(TX)主要jar包org.sprin...

Monkey自动化测试

Monkey1.通过Monkey程序模拟用户触摸屏幕、滑动Trackball、按键等操作来对设备上的程序进行压力测试,检测程序多久的时间会发生异常;2.Monkey主要用于Android的压力...

十年之重修SpringBoot启动&amp;自动装载&amp;Bean加载过程

总结Springboot的自动装载,完全是依赖Bean的自动注册,其中默认的规则,是把需要自动装载的bean全名称编辑在spring.factories(2.7之后的版本,还支持.imports文件)...

一些可以显著提高大型 Java 项目启动速度的尝试

我们线上的业务jar包基本上普遍比较庞大,动不动一个jar包上百M,启动时间在分钟级,拖慢了我们在故障时快速扩容的响应。于是做了一些分析,看看Java程序启动慢到底慢在哪里,如何去优化,...

class 增量发包改造为 jar 包方式发布

大纲class增量发包介绍项目目录结构介绍jar包方式发布落地方案class增量发包介绍当前项目的迭代修复都是通过class增量包来发版本的将改动的代码class增量打包,如下图cla...

Flink架构及其工作原理(很详细)

原文链接:https://www.cnblogs.com/code2one/p/10123112.html关键词:Flink架构、面试杀手锏!更多大数据架构、实战经验,欢迎关注【大数据与机器学习】,...

大促系统优化之应用启动速度优化实践

作者:京东零售宋维飞一、前言本文记录了在大促前针对SpringBoot应用启动速度过慢而采取的优化方案,主要介绍了如何定位启动速度慢的阻塞点,以及如何解决这些问题。希望可以帮助大家了解如何定位该类问...

Maven工程如何使用非Maven仓库jar包

使用Maven之前,一直都是自己手工在网上搜索需要的jar包,然后添加到工程中。以这样的方式开发,工作了好多年,曾经以为以后也会一直这样下去。直到碰上Maven,用了第一次,就抛弃老方法了。Maven...

【推荐】一款开源免费、功能强大的短链接生成平台

项目介绍reduce是一款开源免费、功能强大的短链接生成平台。部署在服务器,使用短域名解析即可提供服务。CoodyFramework首秀,自写IOC、MVC、ORM、TASK、JSON、DB连接池、...

K8S官方java客户端之七:patch操作

欢迎访问我的GitHubhttps://github.com/zq2599/blog_demos内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;...

Java 的业务逻辑验证框架 之-fluent-validator

开发人员在维护核心业务逻辑的同时,还需要为输入做严格的校验。当输入不合法时,能够给caller一个明确的反馈,最常见的反馈就是返回封装了result的对象或者抛出exception。一些常见...

互联网大厂后端必看!手把手教你替换 Spring Boot 中的日志框架

在互联网大厂的后端开发工作中,SpringBoot框架是搭建项目的“得力助手”,使用十分普遍。但不少开发者都遇到过这样的困扰:SpringBoot默认集成的Logback日志框架,在实际...

测试经理教你如何用monkey进行压力测试!

一、monkey是什么1、monkey程序由android系统自带,使用Java语言写成,在Android文件系统中的存放路径是:/system/framework/monkey.jar2、Mo...

Java-Maven详解

一、什么是Maven?ApacheMaven是一个软件项目管理的综合工具。基于项目对象模型(POM)的概念,提供了帮助管理构建、文档、报告、依赖、发布等方法,Maven简化和标准化项目建设过程。处理...

SpringBoot打包部署最佳实践

springboot介绍SpringBoot目前流行的javaweb应用开发框架,相比传统的spring开发,springboot极大简化了配置,并且遵守约定优于配置的原则即使0配置也能正常运...

取消回复欢迎 发表评论: