SpringBoot启动过程(底层源码深度剖析)

目录

SpringBoot启动过程

概述:

1.入口定义

1.1 SpringApplication静态方法run

2.SpringApplication对象初始化

2.1 确定应用程序类型   webApplicationType类型

 2.2 初始化 初始化器  ApplicationContextInitializer

 2.3 初始化 监听器 ApplicationListener

 2.4  推测程序运行的主类

3.SpringApplication的run方法

附源码注释:

3.1 定义时间Watch,并记录当前时间

3.2 设置系统属性java.awt.headless,不用显示器

3.3 获取应用程序运行监听器监听器,并监听器启动

springboot初始化【事件】表

3.4 获取参数持有类

3.5 准备环境信息

3.6 配置忽略Bean信息

3.7 打印Banner信息 

3.8 创建应用上下文容器

3.9 加载异常处理器   

3.10 准备上下文 prepareContext

3.11 刷新上下文 refreshContext

3.12 刷新后操作

3.13 记录启动时间,广播事件

3.14 执行后置runner

3.15 最后:应用程序就绪事件:ApplicationReadyEvent

4.总结

5.作者感言


SpringBoot启动过程

本文所使用的springboot版本:2.1.8.RELEASE

开发环境:JDK8

概述:

        Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》。Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但是Spring不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦合性等方面从Spring中获益。

        下面我们从源头梳理下springboot在启动过程中做了那些事~

ps:作者建议大家跟随本文,DEBUG多走几遍源码,方便各位理解!

1.入口定义

        下面是最常见的SpringBoot项目启动入口,由此分析启动过程中都发生了什么。

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

1.1 SpringApplication静态方法run

        org.springframework.boot.SpringApplication#run(java.lang.Class<?>, java.lang.String...)内部调用重载接口,调用下述代码。

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

结论1:启动类内部逻辑核心做了两件事

  • new 一个SpringApplication(启动类.class)对象
  • 调用他的run方法,传入启动类上的args

接下来分别对这两个过程进行展开探究~。


2.SpringApplication对象初始化

先附上结论

  1. 确认程序的应用类型
  2. SPI初始化,解析META-INF/spring.factories 文件
  3. 初始化初始化器 ApplicationContextInitializer
  4. 初始化监听器 ApplicationListener
  5. 设置程序运行的主类

        进入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();
		// 初始化初始化器  META-INF/spring.factories 文件下的 ApplicationContextInitializer
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 初始化监听器  META-INF/spring.factories 文件下的 ApplicationListener
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 设置程序运行的主类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.1 确定应用程序类型   webApplicationType类型

        WebApplicationType.deduceFromClasspath()方法,根据是否存在对应文件推断当前应用程序的容器。默认使用的是Servlet 容器,除了servlet之外,还有NONE  和 REACTIVE (响应式编程)

 2.2 初始化 初始化器  ApplicationContextInitializer

        这里我们首次看到了getSpringFactoriesInstances(ApplicationContextInitializer.class)方法。这个方法在SpringBoot的自动装配中很重要,下面我们来看下它的执行过程。

	/**
	 * desc : 通过spi获取指定类型的实例对象
	 * @param : 想要获取的对象类型
	 */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
/**
	 * desc : 获取实例方法
	 */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 拿到指定类型的类全路径名Set
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));

		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 如果缓存中存在,不再加载
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
            // 获取指定路径的文件 META-INF/spring.factories
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

        由上面的执行逻辑得出结论,SpringBoot初始化时会将下述位置的文件/META-INF/spring.factories  内容初始化到本地缓存中  

        key:启动类

        value:自动装配的Map

	private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();

         MultiValueMap<String, String>代表有重复值的Map,可以理解为  Map<K, List<V>>,维护了spring.factories中的内容。

        key是接口路径

        value:是实现类全路径名List

         拿到ApplicationContextInitializer所有的实现类后,通过反射将其初始化,保存到启动类的成员变量中

 2.3 初始化 监听器 ApplicationListener

        初始化方式和上面初始化器一样,SPI过程不再赘述,从spring.factories配置文件中加载对应的配置,通过反射 进行初始化,再加载到SpringApplication的成员变量中。

        ps:区别是spring.factories初始化后就从cache中读取了

    // 初始化器
	private List<ApplicationContextInitializer<?>> initializers;
	// 监听器
	private List<ApplicationListener<?>> listeners;

 2.4  推测程序运行的主类

        推测程序运行的主类,其实现逻辑是基于程序运行堆栈中main方法所属的class,也就是我们程序的入口。

private Class<?> deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		return null;
	}

        经过上述操作后,SpringApplication的构造函数就结束了,总结初始化期间,做了如下几件事

SpringApplication构造函数操作结论

  1. 确认程序的应用类型
  2. SPI初始化,解析META-INF/spring.factories 文件
  3. 初始化初始化器 ApplicationContextInitializer
  4. 初始化监听器 ApplicationListener
  5. 设置程序运行的主类

3.SpringApplication的run方法

先附上结论

  1. 定义时间Watch,并记录当前时间
  2. 设置系统属性java.awt.headless,不用显示器
  3. 获取应用程序运行监听器监听器,监听器启动
  4. 获取参数持有类
  5. 准备环境信息
  6. 配置忽略Bean信息
  7. 打印Banner信息
  8. 创建应用上下文容器
  9. 加载异常处理器
  10. 刷新容器前准备操作
  11. 刷新上下文,容器初始化,自动装配 - spring 初始化那一坨
  12. 刷新后操作
  13. 记录启动时间,广播事件
  14. 执行后置runner

附源码注释:

public ConfigurableApplicationContext run(String... args) {
		// 定义时间Watch
		StopWatch stopWatch = new StopWatch();
		// 记录当前时间
		stopWatch.start();
		// Spring容器上下文对象
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 设置系统属性java.awt.headless,不用显示器
		configureHeadlessProperty();
		// 获取应用程序运行监听器监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 监听器启动
		listeners.starting();
		try {
			// 参数持有类
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			// 准备环境信息
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 配置忽略Bean信息
			configureIgnoreBeanInfo(environment);
			// Banner信息
			Banner printedBanner = printBanner(environment);
			// 创建应用上下文容器
			context = createApplicationContext();
			// 加载异常处理器
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 刷新容器前准备操作
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 刷新上下文,容器初始化,自动装配 - spring 初始化那一坨
			refreshContext(context);
			// 刷新后操作
			afterRefresh(context, applicationArguments);
			// 记录启动时间
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			// 事件广播 启动完成了
			listeners.started(context);
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			// 事件广播启动出错了
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			// 事件广播 容器运行了
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

3.1 定义时间Watch,并记录当前时间

        run方法第一步,初始化计时器对象,用来统计程序启动过程中的花费时间。具体实现是以:维护开始截止时间戳,做差值。

        // 定义时间Watch
		StopWatch stopWatch = new StopWatch();
		// 记录当前时间
		stopWatch.start();

3.2 设置系统属性java.awt.headless,不用显示器

        配置系统属性【java.awt.headless】无显示模式运行,当系统无指定java.awt.headless属性时,默认为true。

/**
	 * desc : 设置系统属性java.awt.headless,代表不用显示器 
	 */
	private void configureHeadlessProperty() {
		System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
				System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
	}

3.3 获取程序运行时监听器,并启动监听器

        初始化过程中第三次用到SPI,初始化方式和上述 「2.2通过SPI初始化初始化器」一样,不过加载类型为。SpringApplicationRunListener.class。

	/**
	 * desc : 初始化 SpringApplicationRunListener.class
	 */
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}

        拿到全部监听器后,过滤出监听ApplicationStartingEvent事件的监听器,并进行启动

/**
	 * desc : 遍历全部监听器,调用start方法
	 */
	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

        监听器启动实现方式,springboot是由事件广播机制进行实现,底层由SimpleApplicationEventMulticaster发送ApplicationStartingEvent事件【事件1】

	/**
	 * desc : 应用程序启动事件
	 */
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
	}

        拓展:spring的事件结构,及常用事件类型。基于事件的发布及订阅实现不同节点的广播。具体执行节点下文进行详细阐述。

springboot初始化【事件】表

顶层类

  • jdk的EventObject

spring应用事件实现类

  • SpringApplicationEvent

springBoot具体事件及执行顺序

  1. 应用程序启动事件:ApplicationStartingEvent
  2. 应用环境准备事件:ApplicationEnvironmentPreparedEvent
  3. 应用程序上下文初始化事件:ApplicationContextInitializedEvent
  4. 应用程序准备事件:ApplicationPreparedEvent
  5. 应用程序启动完成事件:ApplicationStartedEvent
  6. 应用程序就绪事件:ApplicationReadyEvent
  7. 应用程序失败事件:ApplicationFailedEvent

3.4 获取参数持有类

        获取默认应用程序参数,赋值DefaultApplicationArguments对象

// 参数持有类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

3.5 准备环境信息

        准备环境信息,创建配置环境对象,内部存储了systemProperties,systemEnvironment两类系统属性信息,获取系统profile,将配置文件与启动类绑定,准备完成后,发布【事件2】

    /**
	 * desc : 准备环境信息
	 */
	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {

		// 创建配置环境对象,内部存储了systemProperties,systemEnvironment两类系统属性信息
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置系统环境,profile信息
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 配置加载好的属性源propertySource
		ConfigurationPropertySources.attach(environment);
		// 发布应用环境准备完成事件
		listeners.environmentPrepared(environment);
		// 绑定环境信息于SpringApplication对象
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

3.6 配置忽略Bean信息

        此步逻辑围绕spring.beaninfo.ignore,这个配置用来忽略所有自定义的BeanInfo类的搜索。简单来说程序启动过程中不去检查所有Bean的ClassLoader,提升效率.

	/**
	 * desc : 配置忽略Bean信息
	 * 程序启动过程中不去检查classNotFound的Bean,提升效率.
	 */
	private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
		if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
			Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
			System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
		}
	}

3.7 打印Banner信息 

        打印控制台Banner(spring的大logo)

// Banner信息
Banner printedBanner = printBanner(environment);

banner共有三种模式。

  • OFF:Disable printing of the banner.
  • CONSOLE:Print the banner to System.out.  -- 默认
  • LOG:Print the banner to the log file

支持自定义banner,仅需要在resource目录下增加banner.txt 文件,内部维护想要的图案就好了。

banner生成:Text to ASCII Art Generator (TAAG)http://patorjk.com/software/taag/#p=display&h=3&v=3&f=Blocks&t=TRADE

3.8 创建应用上下文容器

        通过【2.1 推测webApplicationType类型】属性,通过类路径名拿到class,通过反射构造函数初始化。以此创建对应的应用上下文容器。

// 确定上下文容器类型				
switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				case REACTIVE:
					contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
					break;
				default:
					contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
    		}
// 反射初始化
return BeanUtils.instantiateClass(contextClass);

3.9 加载异常处理器   

        初始化过程中第四次用到SPI,初始化方式和上述 「2.2通过SPI初始化初始化器」一样,不过类型换为。SpringBootExceptionReporter.class。在应用程序启动失败时,广播【事件7】

// 加载异常处理器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);

3.10 准备上下文 prepareContext

        prepareContext方法作为容器刷新前的准备操作,主要做了如下几件事

  1. 初始化BeanName生成器
  2. spi获取的初始化器开始执行
  3. 发送容器上下文初始化事件【事件3】
  4. 注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
  5. 加载启动指定类,将启动类注入容器
  6. 发送容器准备完成事件【事件4】

/**
* desc : 准备上下文
*/
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		// 初始化BeanName生成器
		postProcessApplicationContext(context);
		// spi获取的初始化器开始执行
		applyInitializers(context);
		// 发送容器上下文初始化事件
		listeners.contextPrepared(context);
		。。。。。。
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		//注册启动参数bean,这里将容器指定的参数封装成bean,注入容器
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		。。。。。。
		// **加载启动指定类,将启动类注入容器**
		load(context, sources.toArray(new Object[0]));
		// 发送容器准备完成事件
		listeners.contextLoaded(context);
	}

3.11 刷新上下文 refreshContext

        这里看过spring源码的同学应该就很熟悉,spring的refresh那一套逻辑。核心做了容器初始化,自动装配。内容过多,有兴许可见此文章,这里就不单独展开描述了。

Spring之refresh的12个步骤_每一个不曾起舞的日子,都是对生命的辜负的博客-CSDN博客根据B站黑马视频总结1. refresh的12个步骤prepareRefreshobtainFreshBeanFactoryprepareBeanFactorypostProcessBeanFactoryinvokeBeanFactoryPostProcessorsregisterBeanPostProcessorsinitMessageSourceinitApplicationEventMulticasteronRefreshregisterListe...https://blog.csdn.net/weixin_44390164/article/details/120695989?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-0.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.1&utm_relevant_index=3

3.12 刷新后操作

        springboot预留扩展点,目前无业务逻辑,可通过重写方法进行后置扩展


	/**
	 * Called after the context has been refreshed.
	 * @param context the application context
	 * @param args the application arguments
	 */
	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
	}

3.13 记录启动时间,广播事件

        到此节点springboot启动过程基本已经结束了,记录并打印启动耗时,并发布应用程序启动完成事件【事件5】

// 记录启动时间
stopWatch.stop();
if (this.logStartupInfo) {
    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 事件广播 启动完成了
listeners.started(context);

3.14 执行后置runner

        这里也是一个拓展点,用户可以自定义ApplicationRunner  或者  CommandLineRunner类型的实现类,并交由spring托管,这样spring容器启动后就会在此节点调用。

/**
* desc : 执行后置runner
*/
private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		// 获取ApplicationRunner类型的实现类
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		// CommandLineRunner
		runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
		AnnotationAwareOrderComparator.sort(runners);
		for (Object runner : new LinkedHashSet<>(runners)) {
			if (runner instanceof ApplicationRunner) {
				callRunner((ApplicationRunner) runner, args);
			}
			if (runner instanceof CommandLineRunner) {
				callRunner((CommandLineRunner) runner, args);
			}
		}
	}

3.15 最后:应用程序就绪事件:ApplicationReadyEvent

        事件发布应用程序就绪事件:ApplicationReadyEvent 【事件6】

try {
    // 事件广播 容器运行了
    listeners.running(context);
}

4.总结

         最后总结下springboot启动过程。

结论1:启动类内部逻辑核心做了两件事

  • new 一个SpringApplication(启动类.class)对象
  • 调用他的run方法,传入启动类上的args

其中new 一个SpringApplication(启动类.class)对象:

结论2:new 一个SpringApplication(启动类.class)对象的核心操作

  1. 确认程序的应用类型
  2. SPI初始化,解析META-INF/spring.factories 文件
  3. 初始化初始化器 ApplicationContextInitializer
  4. 初始化监听器 ApplicationListener
  5. 设置程序运行的主类

run方法如下:

结论3:SpringApplication的run方法核心操作

  1. 定义时间Watch,并记录当前时间
  2. 设置系统属性java.awt.headless,不用显示器
  3. 获取应用程序运行监听器监听器,监听器启动
  4. 获取参数持有类
  5. 准备环境信息
  6. 配置忽略Bean信息
  7. 打印Banner信息
  8. 创建应用上下文容器
  9. 加载异常处理器
  10. 刷新容器前准备操作
  11. 刷新上下文,容器初始化,自动装配 - spring 初始化那一坨
  12. 刷新后操作
  13. 记录启动时间,广播事件
  14. 执行后置runner

全览:

5.作者感言

        完结撒花~
        
能看到这里的小伙伴真的是很有耐心。身边有的朋友和我说学习源码的过程十分枯燥和无趣,不过每个技术人其实都需要一定的自我驱动能力。并且在枯燥的的学习过程中。找到自己一个又一个”满足点“。当你积累一定的痛苦后,后头看看,可能已经比别人多走了很远。

        其实每一个人写代码都有自己的风格与习惯。多去看看开源优秀的源码,从源码中学到他们的设计和想法,然后再对比一下自己的代码,这样就会找到前进的方向。

        最后综上所述,springboot的启动过程源码剖析就结束了。笔者再次建议大家打开源码,debug多走几遍,加深对springboot的理解。

                                                    「点赞,收藏,评论 三连走一走,谢谢各位Thanks♪(・ω・)ノ」

        需要注释源码的可以留下邮箱,我私聊发你~~~~

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SpringBoot2的启动流程是通过@SpringBootApplication注解自动化配置来实现的。该注解包含了多个注解的组合,其中包括@ComponentScan、@EnableAutoConfiguration和@Configuration等。通过这些注解,Spring Boot会自动扫描并加载配置类,并根据自动配置规则来配置应用程序。 具体而言,当应用程序启动时,Spring Boot会创建一个Spring应用程序上下文。在创建上下文的过程中,会先加载主配置类(通常是包含main方法的类),然后根据@ComponentScan注解扫描指定包下的所有组件。 接下来,Spring Boot会根据@EnableAutoConfiguration注解自动配置应用程序。这个注解会根据classpath和条件匹配的规则,加载配置类,并将它们注册到应用程序上下文中。这些配置类使用了@Configuration注解,会声明一些Bean,并根据条件来决定是否生效。 最后,Spring Boot会启动应用程序,并执行相应的事件处理器。这些事件处理器可以通过自定义ApplicationListener来实现。在应用程序运行期间,Spring Boot会触发不同的事件,并调用相应的事件处理器。 参考文献: 引用:SpringBoot2 | @SpringBootApplication注解 自动化配置流程源码分析(三) [2] 引用:SpringBoot2 | SpringBoot监听器源码分析 | 自定义ApplicationListener(六) 引用:该系列主要还是Spring的核心源码,不过目前Springboot大行其道,所以就从Springboot开始分析。最新版本是Springboot2.0.4,Spring5,所以新特性本系列后面也会着重分析。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值