首页 > 其他分享 >SpringBoot 启动方法

SpringBoot 启动方法

时间:2024-06-07 15:31:55浏览次数:21  
标签:return SpringBoot 启动 environment listeners context new 方法 class

SpringBoot 启动方法

入口

  • 通常一个简单的 SpringBoot 基础项目我们会有如下代码
@SpringBootApplication
@RestController
@RequestMapping("/")
public class Application {

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

}

  • 值得关注的有SpringApplication.run以及注解@SpringBootApplication

run 方法

	public ConfigurableApplicationContext run(String... args) {
	    // 秒表
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		// 获取监听器
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 监听器启动
		listeners.starting();
		try {
		    // application 启动参数列表
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			// 配置忽略的bean信息
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
			// 创建应用上下文
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
		    // 准备上下文,装配bean
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			// 上下文刷新
			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;
	}

getRunListeners

  • 获取监听器
	private SpringApplicationRunListeners getRunListeners(String[] args) {
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		// 获取  Spring Factory 实例对象
		return new SpringApplicationRunListeners(logger,
				getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
	}


	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// 读取 spring.factories
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 创建SpringFactory实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		/**
		 * 排序 {@link Ordered}
		 */
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

createSpringFactoriesInstances

@SuppressWarnings("unchecked")
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
		ClassLoader classLoader, Object[] args, Set<String> names) {
    // 初始化
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
		    // 通过名字创建类的class对象
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			// 构造器获取
			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			// 创建具体实例
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			// 加入实例表中
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

  • SpringFactoriesLoader.loadFactoryNames(type, classLoader) 是 spring 提供的方法,主要目的是读取spring.factories文件

  • AnnotationAwareOrderComparator.sort(instances)排序

    • 通过 spring 的源码我们知道这个方法是根据order的数字大小进行排序,观察

      SharedMetadataReaderFactoryContextInitializer

    • 同样的再找一个DelegatingApplicationContextInitializer

listeners.starting()

  • SpringApplicationRunListeners : org.springframework.boot.SpringApplicationRunListeners 这个类是org.springframework.boot.SpringApplicationRunListener的集合表现形式

    class SpringApplicationRunListeners {
    
    	private final List<SpringApplicationRunListener> listeners;
    SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
    		this.log = log;
    		this.listeners = new ArrayList<>(listeners);
    	}
    
    	void starting() {
    		for (SpringApplicationRunListener listener : this.listeners) {
    			listener.starting();
    		}
    	}
    
    }
    
    • 这里主要是启动org.springframework.boot.SpringApplicationRunListener#starting方法,只有一个实现org.springframework.boot.context.event.EventPublishingRunListener#starting

prepareEnvironment

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 得到一个环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 配置环境
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		ConfigurationPropertySources.attach(environment);
		listeners.environmentPrepared(environment);
		// 绑定springBoot应用
		bindToSpringApplication(environment);
		// 是否创建自定义环境
		if (!this.isCustomEnvironment) {
			environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
					deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

configureIgnoreBeanInfo

  • 获取spring.beaninfo.ignore并且设置到环境信息中
	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());
		}
	}

printBanner

	private Banner printBanner(ConfigurableEnvironment environment) {
		if (this.bannerMode == Banner.Mode.OFF) {
			return null;
		}
		ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
				: new DefaultResourceLoader(getClassLoader());
		// 创建打印器
		SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
		if (this.bannerMode == Mode.LOG) {
		    // 输出
			return bannerPrinter.print(environment, this.mainApplicationClass, logger);
		}
        // 输出
		return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
	}

	Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
		Banner banner = getBanner(environment);
		banner.printBanner(environment, sourceClass, out);
		return new PrintedBanner(banner, sourceClass);
	}

  • 最终输出内容类:org.springframework.boot.SpringBootBanner

    class SpringBootBanner implements Banner {
    
    	private static final String[] BANNER = { "", "  .   ____          _            __ _ _",
    			" /\\\\ / ___'_ __ _ _(_)_ __  __ _ \\ \\ \\ \\", "( ( )\\___ | '_ | '_| | '_ \\/ _` | \\ \\ \\ \\",
    			" \\\\/  ___)| |_)| | | | | || (_| |  ) ) ) )", "  '  |____| .__|_| |_|_| |_\\__, | / / / /",
    			" =========|_|==============|___/=/_/_/_/" };
    
    	private static final String SPRING_BOOT = " :: Spring Boot :: ";
    
    	private static final int STRAP_LINE_SIZE = 42;
    
    	@Override
    	public void printBanner(Environment environment, Class<?> sourceClass, PrintStream printStream) {
    		for (String line : BANNER) {
    			printStream.println(line);
    		}
    		String version = SpringBootVersion.getVersion();
    		version = (version != null) ? " (v" + version + ")" : "";
    		StringBuilder padding = new StringBuilder();
    		while (padding.length() < STRAP_LINE_SIZE - (version.length() + SPRING_BOOT.length())) {
    			padding.append(" ");
    		}
    
    		printStream.println(AnsiOutput.toString(AnsiColor.GREEN, SPRING_BOOT, AnsiColor.DEFAULT, padding.toString(),
    				AnsiStyle.FAINT, version));
    		printStream.println();
    	}
    
    }
    

createApplicationContext

	protected ConfigurableApplicationContext createApplicationContext() {
	    // 获取上下文类
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				// 根据不同类型选择创建的实例
				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);
				}
			}
			catch (ClassNotFoundException ex) {
				throw new IllegalStateException(
						"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
			}
		}
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

  • this.applicationContextClass 初始化方法
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 设置 web应用类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}
  • org.springframework.boot.WebApplicationType#deduceFromClasspath
	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

prepareContext

	private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
			SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
	    // 上下文中设置环境
		context.setEnvironment(environment);
		// 上下文处理
		postProcessApplicationContext(context);
		// 初始化
		applyInitializers(context);
		// 监听器中放入上下文
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		// 单例对象注册
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 加载上下文
		load(context, sources.toArray(new Object[0]));
		// 监听器做加载上下文操作
		listeners.contextLoaded(context);
	}

  • set方法就不说了

postProcessApplicationContext

	protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
		if (this.beanNameGenerator != null) {
		    // 注册 beanName 的生成器
			context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
					this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
			if (context instanceof GenericApplicationContext) {
			    // 设置资源加载器
				((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
			}
			if (context instanceof DefaultResourceLoader) {
			    // 设置类加载器
				((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
			}
		}
		if (this.addConversionService) {
		    // 转换服务
			context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
		}
	}

  • 看一下最终设置完成后的 context

    context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    

applyInitializers

  • 初始化应用上下文
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected void applyInitializers(ConfigurableApplicationContext context) {
		for (ApplicationContextInitializer initializer : getInitializers()) {
			Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
					ApplicationContextInitializer.class);
			Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
			initializer.initialize(context);
		}
	}

  • 初始化 List<ApplicationListener<?>> listeners: setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

  • 获取 List<ApplicationListener<?>> listeners: public Set<ApplicationListener<?>> getListeners() { return asUnmodifiableOrderedSet(this.listeners);}

  • 子类的具体实现不展开了

getAllSources

	public Set<Object> getAllSources() {
		Set<Object> allSources = new LinkedHashSet<>();
		if (!CollectionUtils.isEmpty(this.primarySources)) {
			allSources.addAll(this.primarySources);
		}
		if (!CollectionUtils.isEmpty(this.sources)) {
			allSources.addAll(this.sources);
		}
		return Collections.unmodifiableSet(allSources);
	}

  • primarySources 就是我们的项目启动类,在SpringApplication的构造器中有this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))

load

  • 加载 bean 到应用上下文
	protected void load(ApplicationContext context, Object[] sources) {
		if (logger.isDebugEnabled()) {
			logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
		}
		// bean定义加载器
		BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
		if (this.beanNameGenerator != null) {
		    // 设置 beanName生成器
			loader.setBeanNameGenerator(this.beanNameGenerator);
		}
		if (this.resourceLoader != null) {
		    // 设置 资源加载器
			loader.setResourceLoader(this.resourceLoader);
		}
		if (this.environment != null) {
		    // 设置环境
			loader.setEnvironment(this.environment);
		}
		// 加载
		loader.load();
	}

	int load() {
		int count = 0;
		for (Object source : this.sources) {
			count += load(source);
		}
		return count;
	}

private int load(Object source) {
		Assert.notNull(source, "Source must not be null");
		if (source instanceof Class<?>) {
			return load((Class<?>) source);
		}
		if (source instanceof Resource) {
			return load((Resource) source);
		}
		if (source instanceof Package) {
			return load((Package) source);
		}
		if (source instanceof CharSequence) {
			return load((CharSequence) source);
		}
		throw new IllegalArgumentException("Invalid source type " + source.getClass());
	}

  • 通过前文我们已经知道 source就是一个 class
	private int load(Class<?> source) {
		if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
			// Any GroovyLoaders added in beans{} DSL can contribute beans here
			GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
			load(loader);
		}
		// 是否为组件
		if (isComponent(source)) {
			this.annotatedReader.register(source);
			return 1;
		}
		return 0;
	}

  • 我们的启动类是一个组件,直接注册完成返回 1

listeners.contextLoaded(context)

  • 监听器行为: 在上下文资源加载后做一些事情

refreshContext

  • 上下文刷新
	private void refreshContext(ConfigurableApplicationContext context) {
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}

	/**
	 * Refresh the underlying {@link ApplicationContext}.
	 * @param applicationContext the application context to refresh
	 */
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
  • 最终来到了org.springframework.context.support.AbstractApplicationContext#refresh方法,此方法是 spring 的一个方法,此处不在阐述

afterRefresh

  • 刷新上下文之后做的事情,空的没有实现

    	protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
    	}
    
    

stopWatch.stop()

  • 秒表结束

listeners.started(context)

  • 各类监听器启动

callRunners

  • 两种 runner 启动ApplicationRunnerCommandLineRunner
	private void callRunners(ApplicationContext context, ApplicationArguments args) {
		List<Object> runners = new ArrayList<>();
		runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
		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);
			}
		}
	}

private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args);
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
		}
	}

listeners.running(context)

  • 监听器正式开始工作

标签:return,SpringBoot,启动,environment,listeners,context,new,方法,class
From: https://blog.csdn.net/u014349086/article/details/139505668

相关文章

  • springboot-异步使用
    创建配置类,开启异步和创建线程packagecom.chulx.demo1.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.EnableAsync;importorg.springframe......
  • 技术洞察的关键步骤和方法
    什么是技术洞察?    《从技术洞察到技术规划赋能》公开课即将开启!     最近,共创力许多客户在进行技术规划时遇到了同一个问题:如何进行技术洞察?技术需求如何进行收集和获取?如何保证技术洞察的准确性?带着这些问题,共创力资深顾问George结合多年的实战经验对技术洞察......
  • 解决DNF游戏启动故障:缺少ClientBase.dll文件的高效应对策略
    《地下城与勇士》(DNF)是一款风靡全球的2D横版卷轴式大型多人在线角色扮演游戏,以其街机风格的爽快打击感、丰富多样的职业体系、深渊副本挑战、PVP竞技场以及装备打造系统著称,引领玩家在阿拉德大陆展开惊心动魄的冒险之旅。当遇到DNF(地下城与勇士)游戏在启动时提示缺少ClientBase.......
  • python获取多只股票价格信息的方法
    python代码如下:importefinanceasefimporttimefromdatetimeimportdatetimeimportcsvfreq=1defprocess_row(row):#在这里处理每一行数据print('股票:'+row[0],'日期:'+row[2],'现价:'+row[3],'最高:'+row[5],'最低:�......
  • 重复运行同一个 junit 测试的简便方法?
    正如标题所说,我正在寻找一些简单的方法,以便使用Eclipse自动连续多次运行JUnit4.x测试。一个例子是连续运行10次相同的测试并报告结果。我们已经有了一种复杂的方法,但我仍在寻找一种简单的方法,以便我可以确定我一直在尝试修复的不稳定测试将保持不变。......
  • 在虚拟机上搭建 Docker Kafka 宿主机器程序无法访问解决方法
    1、问题描述在虚拟机CentOS-7上搭建的DockerKafka,docker内部可以创建Topic、可以生产者数据、可以消费数据,而在宿主机开发程序无法消费Docker Kafka的数据。1.1、运行情况[docker@localhost~]$dockerps-aCONTAINERIDIMAGECOMMAND......
  • 怎么避免电脑磁盘数据泄露?磁盘数据保护方法介绍
    电脑磁盘是电脑存储数据的基础,而为了避免磁盘数据泄露,我们需要保护电脑磁盘。下面我们就来了解一下磁盘数据保护的方法。磁盘加密磁盘加密可以通过专业的加密算法来加密保护磁盘数据,避免电脑磁盘数据泄露。在这里小编推荐使用文件夹只读加密专家来加密保护电脑磁盘。文件......
  • 电池SOC估计方法
      全球经济快速增长使得能源需求与日俱增,由于传统化石能源的不可再生性以及其带来的环境污染问题日益突出,世界各国正将目光转向新能源的开发与利用上,以谋求能源的可持续发展。  燃油车的尾气排放是全球碳排放的主要来源之一,减少燃油汽车尾气的排放对降低全球碳排放具......
  • 【S082】Springboot+Vue电子商城购物系统 购物 商务 前后端分离 含文档
    运行截图:用户登陆商城首页商品分类商品详情页我的购物车下单页面我的订单后台首页用户管理头像管理商品分类管理轮播图管理商品管理订单管理图表分析收入排行榜项目组成:项目源码:项目文档:源码获取⬇⬇⬇......
  • 【S081】基于SpringBoot实现健身房管理系统 JavaWeb健身房管理系统
    运行截图:登录后台首页会员卡查询会员管理添加会员员工管理添加员工器材管理编辑器材课程管理报名信息项目组成:项目源码:源码获取⬇⬇⬇......