【源码】- SpringBoot启动

SpringBoot启动源码

前言

开始之前,需要先了解下springboot的SPI

SPI 与 API的区别

SPI : 我提供接口,你实现,我来调用你

API: 我提供接口和实现, 你来调我

getSpringFactoriesInstances

一定 牢记 这个方法(Springboot的SPI)

  1. 获取类加载器,默认是应用类加载器
  2. 读取spring.factories 配置的 type类型的 接口实现的全类名,放入set集合
  3. 将这些类实例化
  4. 根据Order排序,升序
java 复制代码
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
}

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 配置的 type类型的 接口实现,放入set集合
  Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
  // 将这些类实例化
  List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
  // 根据order接口排序,如果没有实现Order接口,则根据@Order注解排序
  AnnotationAwareOrderComparator.sort(instances);
  return instances;
}

loadFactoryNames

获取当前类加载器 加载的classpath下的 spring.factories 将文件内容转为Map<String, List>,key为 接口的全类名,value为对应实现类的全类名

java 复制代码
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		// 获取当前类加载器加载的spring.factories
    Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
      // 加载classpath下面的所有META-INF/spring.factories
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
        // 读取文件的配置
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
          // 对应接口类型名
					String factoryTypeName = ((String) entry.getKey()).trim();
          // 接口对应实现的全类名,用,分割
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

      // 去重并收集为 不可修改的列表
			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			// 缓存
      cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

createSpringFactoriesInstances

  1. 根据classLoader 获取 Class对象
  2. 获取无参构造函数
  3. 无参构造实例化
  4. 返回所有实例化对象
java 复制代码
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);
        // parameterTypes 和 args 是上面调用传来的构造参数,都是空
        // 所以这里获取 无参构造函数(必须有无参构造函数)
				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;
	}

AnnotationAwareOrderComparator.sort(instances)

对实例化对象进行Order排序,确定后续的执行顺序

  1. 先获取Order接口返回的值
  2. 如果没有实现Order接口,获取@Order注解返回的值
  3. 默认Order是Integer的最大值
  4. 安装Order升序排列
java 复制代码
public class AnnotationAwareOrderComparator extends OrderComparator {

	/**
	 * Shared default instance of {@code AnnotationAwareOrderComparator}.
	 */
	public static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();



	@Override
	@Nullable
  // 先获取Order接口返回的值
  // 如果没有实现Order接口,获取@Order注解返回的值
  // 默认Order是Integer的最大值
  // 安装Order升序排列
	protected Integer findOrder(Object obj) {
		Integer order = super.findOrder(obj);
		if (order != null) {
			return order;
		}
		return findOrderFromAnnotation(obj);
	}

	@Nullable
	private Integer findOrderFromAnnotation(Object obj) {
		AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
		MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
		Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
		if (order == null && obj instanceof DecoratingProxy) {
			return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
		}
		return order;
	}

  // 排序入口
	public static void sort(List<?> list) {
		if (list.size() > 1) {
			list.sort(INSTANCE);
		}
	}
 .....
}
  

启动入口

java 复制代码
@SpringBootApplication
public class MyAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }
}

内部调用逻辑

  1. 创建 SpringApplication 对象
  2. 调用run方法
java 复制代码
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

创建SpringApplication 对象

  1. 判断是什么web应用(MVC,WebFlux,None)
  2. 设置一些初始化对象
  3. 获取启动类的class对象(main函数所在位置)
java 复制代码
public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

  public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// this.resourceLoader = null 
    this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 判断是什么web类型的应用
    // 有 WebFlux 没有 Spring MVC 没有 Jersey ⇒ 这是一个"纯 WebFlux 应用
    // 缺少 servlet 或 ConfigurableWebApplicationContext ⇒ 这是一个普通应用(非WEB)
    // 否则 是 MVC 的 web应用
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 赋值 bootstrap初始化器
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    // 设置初始化器
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 根据调用栈信息,获取当前启动类的class对象
    this.mainApplicationClass = deduceMainApplicationClass();
	}

启动运行

  1. 构建bootstrap上下文
  2. 准备环境信息
  3. 构建Application上下文
  4. 注册上下文中的bean信息

启动过程中穿插着一些Listeners 的 事件传播

java 复制代码
public ConfigurableApplicationContext run(String... args) {
    // 获取开始时间
    long startTime = System.nanoTime();
    // 创建默认的bootstrap上下文,并执行bootstrap的初始化器
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    
    ConfigurableApplicationContext context = null;
    // 根据系统参数获取 java.awt.headless,是否有图形显示
    configureHeadlessProperty();
    // 获取 SpringApplicationRunListener 监听器
    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);
       // 创建spring上下文
       context = createApplicationContext();
       context.setApplicationStartup(this.applicationStartup);
       // 准备上下文
       prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
       // 刷新上下文
       refreshContext(context);
       // 这是留的一个模板方法,默认是空方法
       // 留给SpringApplication的子类实现
       afterRefresh(context, applicationArguments);
       // 计算启动花费时间
       Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
       if (this.logStartupInfo) {
          new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
       }
       // 发送springboot工程已启动的事件
       listeners.started(context, timeTakenToStartup);
       // 调用根据Order排序后的 ApplicationRunner 和 CommandLineRunner,执行启动后的一些处理
       callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
       handleRunFailure(context, ex, listeners);
       throw new IllegalStateException(ex);
    }
    try {
        // 发送springboot工程已就绪的事件
       Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
       listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
       // 出现异常,执行异常处理
       handleRunFailure(context, ex, null);
       throw new IllegalStateException(ex);
    }
    return context;
}

prepareEnvironment

准备环境配置信息 优先级为

  1. commandLineArgs(优先级永远最高)
  2. systemProperties
  3. systemEnvironment
  4. defaultProperties (优先级永远最低)
java 复制代码
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
			DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 根据web类型创建环境信息
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置环境信息(defaultProperties,commandLineArgs)
		configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 配置环境信息 configurationProperties (包含上面所有的参数)
		ConfigurationPropertySources.attach(environment);
    // 监听器执行环境准备(实际是发送环境准备完成的事件)
		listeners.environmentPrepared(bootstrapContext, environment);
    // 将默认配置移动到最后(优先级最低)
		DefaultPropertiesPropertySource.moveToEnd(environment);
		Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				"Environment prefix cannot be set via properties.");
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
			environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		ConfigurationPropertySources.attach(environment);
		return environment;
	}

prepareContext

根据SpringApplication.run()方法中传入的启动配置类,加载出上下文信息 并将启动类放入Spring容器

java 复制代码
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		// 上下文后置处理
    postProcessApplicationContext(context);
    // 执行 初始化器 ApplicationContextInitializer
		applyInitializers(context);
    // 监听器发送 上下文准备的事件
		listeners.contextPrepared(context);
    // 发送一个bootstrap close事件
		bootstrapContext.close(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 注册springApplicationArguments  bean ,内部存储args内容(会解析 --xxx=aa 、 bb)
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
      // 允许循环引用
			((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
			if (beanFactory instanceof DefaultListableBeanFactory) {
        // 允许bean覆盖
				((DefaultListableBeanFactory) beanFactory)
						.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
			}
		}
    // springboot默认加载全部bean,这里默认为false
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
    // 添加bean工厂的后置处理器(保证defaultProperties 优先级最低)
		context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
		// Load the sources
    // 加载Application启动类配置
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
    // 监听器发送 上下文已加载的事件
		listeners.contextLoaded(context);
	}

refreshContext

这里就是Spring的启动代码

完成一些列bean的注册

这里就不再解释了

java 复制代码
private void refreshContext(ConfigurableApplicationContext context) {
		if (this.registerShutdownHook) {
			shutdownHook.registerApplicationContext(context);
		}
		refresh(context);
	}

protected void refresh(ConfigurableApplicationContext applicationContext) {
		applicationContext.refresh();
}

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

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

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

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				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();
			}
		}
	}
相关推荐
gusijin3 小时前
解决idea启动报错java: OutOfMemoryError: insufficient memory
java·ide·intellij-idea
To Be Clean Coder3 小时前
【Spring源码】createBean如何寻找构造器(二)——单参数构造器的场景
java·后端·spring
吨~吨~吨~3 小时前
解决 IntelliJ IDEA 运行时“命令行过长”问题:使用 JAR
java·ide·intellij-idea
你才是臭弟弟3 小时前
SpringBoot 集成MinIo(根据上传文件.后缀自动归类)
java·spring boot·后端
短剑重铸之日3 小时前
《设计模式》第二篇:单例模式
java·单例模式·设计模式·懒汉式·恶汉式
码农水水3 小时前
得物Java面试被问:消息队列的死信队列和重试机制
java·开发语言·jvm·数据结构·机器学习·面试·职场和发展
summer_du3 小时前
IDEA插件下载缓慢,如何解决?
java·ide·intellij-idea
东东5163 小时前
高校智能排课系统 (ssm+vue)
java·开发语言
余瑜鱼鱼鱼3 小时前
HashTable, HashMap, ConcurrentHashMap 之间的区别
java·开发语言
SunnyDays10113 小时前
使用 Java 自动设置 PDF 文档属性
java·pdf文档属性