Springboot启动流程

Springboot启动流程

@SpringBootApplication
public class WarmApplication {
  /**
   * The main method.
   */
  public static void main(String[] args) {
    SpringApplication.run(WarmApplication.class, args);
  }
}

该代码块主要有两个值得注意的地方,1. SpringApplication 2. SpringApplication.run方法。对于@SpringBootApplication以后再做分析。

1 SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  1. this.webApplicationType = WebApplicationType.deduceFromClasspath();
    WebApplicationType 包含 NONE ,SERVLET,REACTIVE 三种
  2. setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationContextInitializer,作用在容器调用refrence方法的时候,会调用initialize方法
  3. setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    使用 SpringFactoriesLoader 在应用的 classpath 中查找并加载所有可用的 ApplicationListener。作用ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。
  4. this.mainApplicationClass = deduceMainApplicationClass()
    推断出main方法所在类。

run方法6步

  1. 获取监听器SpringApplicationRunListeners 通过SpringApplicationRunListener作为key找到value
		SpringApplicationRunListeners listeners = getRunListeners(args);
  1. 构造应用上下文路径(环境集合,包含系统,jdk,自定义属性信息,如application.yml)加载environment对象中,需要用到通过environment中获取。
			context = createApplicationContext();
 (1)通过应用类型(WebApplicationType),创建不同的应用对象 StandardServletEnvironment
 (2)构造环境对象environment 1)Sytem environment   2)根据配置环境(prod,val,dev)激活相应的配置 ,加载环境
 (3)banner的配置,加载
 (4)构造应用上下文对象ConfigurableApplicationContext。IOC容器也会创建beanFactory
  1. 完成bean的创建,主类
    prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    生成启动类的单列加载到容器中。
    postProcessApplicationContext(context)[setConversionService转换器]
    applyInitializers(context) 对初始化器进行启动,并执行
    listeners.contextPrepared(context) 向监听器发送已准备好的时间
    load(context, sources.toArray(new Object[0]))
    把核心启动类生成单例bean注册到IOC BeanDefinition beanDefinitionMap
    this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
  2. refreshContext(context) 其他单列加载到IOC
    启动bean过程中完成bean的创建存在IOC
    postProcessBeanFactory(beanFactory);bean的后置处理器,可以修改beanDefinition的一些属性
    invokeBeanFactoryPostProcessors(beanFactory);

SpringBean的生命周期

Spring Bean的生命周期只有四个阶段。实例化和属性赋值对应构造方法和setter方法的注入,初始化和销毁是用户能自定义扩展的两个阶段。

  1. 实例化 Instantiation
  2. 属性赋值 Populate
  3. 初始化 Initialization
  4. 销毁 Destruction
if (instanceWrapper == null) {
      instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
try {
			//属性填充
			populateBean(beanName, mbd, instanceWrapper);
			if (exposedObject != null) {
				//初始化Bean(是指根据配置自定义的初始化bean,而不是构造函数创建bean)
				exposedObject = initializeBean(beanName, exposedObject, mbd);
			}
		}

从AbstractAutowireCapableBeanFactory # doCreateBean方法中可以看到bean创建的过程。
bean的销毁,是在容器关闭时调用ConfigurableApplicationContext#close()

细节梳理

第一:在bean生命周期创建前后执行。

实现了这些接口的Bean会切入到多个Bean的生命周期中。正因为如此,这些接口的功能非常强大,Spring内部扩展也经常使用这些接口,例如自动注入以及AOP的实现都和他们有关。

BeanPostProcessor
InstantiationAwareBeanPostProcessor

这两是Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。正好和第一、第三个生命周期阶段对应。通过图能更好理解:
在这里插入图片描述
InstantiationAwareBeanPostProcessor

try {
			// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
			Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
			if (bean != null) {
				return bean;
			}
		}
/**
	 * Apply before-instantiation post-processors, resolving whether there is a
	 * before-instantiation shortcut for the specified bean.
	 * @param beanName the name of the bean
	 * @param mbd the bean definition for the bean
	 * @return the shortcut-determined bean instance, or {@code null} if none
	 */
	@Nullable
	protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
		Object bean = null;
		if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
			// Make sure bean class is actually resolved at this point.
			if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
				Class<?> targetType = determineTargetType(beanName, mbd);
				if (targetType != null) {
					bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
					if (bean != null) {
						bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
					}
				}
			}
			mbd.beforeInstantiationResolved = (bean != null);
		}
		return bean;
	}

spring aop替换对象的时候并不在postProcessBeforeInstantiation替换对象,而是在 postProcessAfterInitialization处理的,也就是applyBeanPostProcessorsAfterInitialization这个方法里面,想了解详细的小朋友可以去看下

/**
	 * Apply InstantiationAwareBeanPostProcessors to the specified bean definition
	 * (by class and name), invoking their {@code postProcessBeforeInstantiation} methods.
	 * <p>Any returned object will be used as the bean instead of actually instantiating
	 * the target bean. A {@code null} return value from the post-processor will
	 * result in the target bean being instantiated.
	 * @param beanClass the class of the bean to be instantiated
	 * @param beanName the name of the bean
	 * @return the bean object to use instead of a default instance of the target bean, or {@code null}
	 * @see InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
	 */
	@Nullable
	protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof InstantiationAwareBeanPostProcessor) {
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
				Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
				if (result != null) {
					return result;
				}
			}
		}
		return null;
	}

postProcessAfterInstantiation调用点,忽略无关代码:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {

   // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
   // state of the bean before properties are set. This can be used, for example,
   // to support styles of field injection.
   boolean continueWithPropertyPopulation = true;
    // InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation()
    // 方法作为属性赋值的前置检查条件,在属性赋值之前执行,能够影响是否进行属性赋值!
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
               continueWithPropertyPopulation = false;
               break;
            }
         }
      }
   }

   // 忽略后续的属性赋值操作代码
}
第二大类:只调用一次的接口

Aware类型的接口
生命周期接口

无所不知的Aware
Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!
Aware接口众多,这里同样通过分类的方式帮助大家记忆。
Aware接口具体可以分为两组,至于为什么这么分,详见下面的源码分析。如下排列顺序同样也是Aware接口的执行顺序,能够见名知意的接口不再解释。
Group1

  • BeanNameAware
  • BeanClassLoaderAware
  • BeanFactoryAware

Group2

  • EnvironmentAware
  • EmbeddedValueResolverAware 这个知道的人可能不多,实现该接口能够获取Spring EL解析器,用户的自定义注解需要支持spel表达式的时候可以使用,非常方便。
  • ApplicationContextAware(ResourceLoaderAware\ApplicationEventPublisher- Aware\MessageSourceAware) 这几个接口可能让人有点懵,实际上这几个接口可以一起记,其返回值实质上都是当前的ApplicationContext对象,因为ApplicationContext是一个复合接口,如下
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
        MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

Aware调用时机

    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {

        // 这里调用的是Group1中的三个Bean开头的Aware
        invokeAwareMethods(beanName, bean);

        Object wrappedBean = bean;
        
        // 这里调用的是Group2中的几个Aware,
        // 而实质上这里就是前面所说的BeanPostProcessor的调用点!
        // 也就是说与Group1中的Aware不同,这里是通过BeanPostProcessor(ApplicationContextAwareProcessor)实现的。
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 下文即将介绍的InitializingBean调用点
        invokeInitMethods(beanName, wrappedBean, mbd);
        // BeanPostProcessor的另一个调用点
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

        return wrappedBean;
    }

可以看到并不是所有的Aware接口都使用同样的方式调用。Bean××Aware都是在代码中直接调用的,而ApplicationContext相关的Aware都是通过BeanPostProcessor#postProcessBeforeInitialization()实现的。感兴趣的可以自己看一下ApplicationContextAwareProcessor这个类的源码,就是判断当前创建的Bean是否实现了相关的Aware方法,如果实现了会调用回调方法将资源传递给Bean。

BeanPostProcessor的调用时机也能在这里体现,包围住invokeInitMethods方法,也就说明了在初始化阶段的前后执行。

关于Aware接口的执行顺序,其实只需要记住第一组在第二组执行之前就行了。

InitializingBean

对应生命周期的初始化阶段,在上面源码的invokeInitMethods(beanName, wrappedBean, mbd);方法中调用。
有一点需要注意,因为Aware方法都是执行在初始化方法之前,所以可以在初始化方法中放心大胆的使用Aware接口获取的资源,这也是我们自定义扩展Spring的常用方式。
除了实现InitializingBean接口之外还能通过注解或者xml配置的方式指定初始化方法,至于这几种定义方式的调用顺序其实没有必要记。因为这几个方法对应的都是同一个生命周期,只是实现方式不同,我们一般只采用其中一种方式。

DisposableBean

类似于InitializingBean,对应生命周期的销毁阶段,以ConfigurableApplicationContext#close()方法作为入口,实现是通过循环取所有实现了DisposableBean接口的Bean然后调用其destroy()方法 。

SpringBoot启动流程可以分为以下几个步骤: 1. 确定应用程序类型。在启动SpringBoot时,首先需要确定应用程序的类型。这可以通过设置启动类的注解来实现,比如使用@SpringBootApplication注解。 2. 创建SpringBoot应用程序上下文。在确定应用程序类型后,SpringBoot会创建一个应用程序上下文(ApplicationContext)对象。这个上下文对象是整个应用程序的核心,包含了所有的配置信息和Bean定义。 3. 加载配置文件。SpringBoot会自动加载并解析应用程序的配置文件,包括application.properties或application.yml等。这些配置文件可以用来配置应用程序的各种属性,如数据库连接、端口号等。 4. 扫描和注册Bean。SpringBoot会扫描应用程序中的所有类,并将符合条件的类注册为Bean。这可以通过@ComponentScan注解来实现,它会扫描指定包及其子包中的所有类。 5. 执行Bean的初始化和依赖注入。在注册Bean后,SpringBoot会执行Bean的初始化操作,并将其依赖的其他Bean注入到其中。这可以通过使用@Autowired注解来实现。 6. 启动应用程序。在完成上述步骤后,SpringBoot启动应用程序。这将导致应用程序开始监听指定的端口,并处理来自客户端的请求。 总而言之,SpringBoot启动流程包括确定应用程序类型、创建应用程序上下文、加载配置文件、扫描和注册Bean、执行Bean的初始化和依赖注入,最后启动应用程序。 <span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [9千字长文带你了解SpringBoot启动过程--史上最详细 SpringBoot启动流程-图文并茂](https://blog.csdn.net/weixin_44947701/article/details/124055713)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值