SpringApplication.run(MyApplication.class, args);运行流程源码分析

SpringApplication.run(MyApplication.class, args);如何启动springBoot项目的

在启动时会加载三个jar将其对应的spring.factories工厂文件的接口实现类到MultiValueMap集合当中,并将对应加载器作为key,接口实现类作为value放到缓存当中

  • spring-boot-2.1.3.RELEASE.jar!/META-INF/spring.factories
  • spring-boot-autocinfiggure-2.1.3.RELEASE.jar!/META-INF/spring.factories
  • spring-bean-5.1.5.RELEASE.jar!/META-INF/spring.factories

spring boot框架 如何实例化
首先spring将一些配置文件的信息放到spring.factories工厂文件当中 springboot通过扫描,加载对应的工厂文件获得对应的配置文件的接口和抽象类,遍历获取对应的实现类的class对象 通过反射的方式获取对应构造(constructor)对象,在通过构造对象获取对应的实例化对象(newInstance()),大多数框架才用的也是这种方法去获取对应的实例化对象

run()

一个静态的辅助方法,可以使用默认设置和用户提供的参数从指定源运行SpringApplication

ConfigurableApplicationContext(ApplicationContext增强版)

大多数应用程序上下文(如果不是全部的话)将实现SPI(服务提供者)接口。除了ApplicationContext接口中提供的方法外,还额外提供配置应用程序上下文的功能和特性。

这里**封装了配置和生命周期方法**,以避免它们被ApplicationContext客户端代码发现(避免配置文件被公开给使用者)。目前的方法只能在启动和关闭代码中使用。

//可配置的应用上下文
	public static ConfigurableApplicationContext run(Class<?> primarySource,
			String... args) {
			//重载传入 将要被加载的类放到一个对应的CLASS数组中
		return run(new Class<?>[] { primarySource }, args);
	}

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
			String[] args) {
		//创建一个启动类传入
		return new SpringApplication(primarySources).run(args);
	}

SpringApplication 构造方法

创建一个新的SpringApplication实例。应用程序上下文将从指定的主要源(传过来对象primarySources)加载bean(有关详细信息,请参阅类级文档)。可以在调用run(String…如上)之前定制实例。

	public SpringApplication(Class<?>... primarySources) {
		//调用类的另外一个构造方法
		this(null, primarySources);
	}

参数:
作用同上
resourceLoader -要使用的资源加载器(加载类路径资源)
主源——主要的bean源

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//null
		this.resourceLoader = resourceLoader;
		//断言  PrimarySources 不能为空
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//将传过来对象数组放到集合中 并为primarySources 赋值
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//推断webApplicationType 的枚举类型 一般都是SERVLET 标准webservice
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		//设置初始化器 读取一些控制器 
		//获取实现了ApplicationContextInitializer初始化器的工厂并将其实例化 读取相应的一些控制器
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		//设置监听器 流程同上
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		//存储带有main方法的启动对象(本例MyApplication)
		//deduceMainApplicationClass 获取推断主应用类 获取对应的MyApplication.class
		this.mainApplicationClass = deduceMainApplicationClass();
	}

WebApplicationType 可能的web应用程序类型的枚举。

private WebApplicationType webApplicationType;
public enum WebApplicationType {

	/**
	 	应用程序不应作为web应用程序运行,也不应启*嵌入式web服务器。
	 */
	NONE,

	/**
	 应用程序应作为基于servlet的web应用程序运行,并应启动嵌入式servlet web服务器。
	 */
	SERVLET,

	/**
	 应用程序应作为一个反应式web应用程序运行,并应启动一个嵌入式反应式web服务器。
	 */
	REACTIVE;
	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };
	//webMVC
	private static final String WEBMVC_INDICATOR_CLASS = "org.springframework."
			+ "web.servlet.DispatcherServlet";
	
	private static final String WEBFLUX_INDICATOR_CLASS = "org."
			+ "springframework.web.reactive.DispatcherHandler";
	
	private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";

	private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";

	private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
	
	static WebApplicationType deduceFromClasspath() {
	//如果有WEBFLUX 没有WEBMVC和JERSEY
		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) {
		//如果当前的启动类对象不再当前classpath下
			if (!ClassUtils.isPresent(className, null)) {
			//return NONE
				return WebApplicationType.NONE;
			}
		}
		//当前应用 标准的web应用
		return WebApplicationType.SERVLET;
	}
	static WebApplicationType deduceFromApplicationContext(
			Class<?> applicationContextClass) {
		if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.SERVLET;
		}
		if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
			return WebApplicationType.REACTIVE;
		}
		return WebApplicationType.NONE;
	}
	
	private static boolean isAssignable(String target, Class<?> type) {
		try {
			return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
		}
		catch (Throwable ex) {
			return false;
		}
	}

isPresent

确定由提供的名称标识的类是否存在并可以加载。如果类或其依赖项之一不存在或无法加载,则返回false。

参数:
className——要检查的类的名称
类装入器——要使用的类加载器(可能为null,表示默认的类加载器)

	public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
			//检测是否存在
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// 通常是ClassNotFoundException或NoClassDefFoundError…
			return false;
		}
	}

ApplicationContextInitializer.class 初始化器(完成初始化工作)

  • 回调接口,用于在刷新之前初始化Spring ConfigurableApplicationContext。
  • 通常用于需要对应用程序上下文进行一些程序化初始化的web应用程序中。例如,根据上下文环境注册属性源或激活配置文件
  • 我们鼓励ApplicationContextInitializer处理器检测是否已经实现了Spring的有序接口,或者是否存在@Order注释,如果存在,则在调用之前对实例进行相应的排序。(对一些配置信息初始化谁先初始化 谁后初始化进行排序)

ApplicationListener.class 监听器

由应用程序事件侦听器实现的接口。基于标准java.util。观察者设计模式的EventListener接口。

从Spring 3.0开始,ApplicationListener通常可以声明它感兴趣的事件类型。当向Spring ApplicationContext注册时,事件将被相应地过滤,侦听器仅被调用来匹配事件对象。

getSpringFactoriesInstances 获取spring工厂实例

//具化方法
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		//重载
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	//泛化方法
	//type 传过来的初始化器
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		/*
		返回要使用的默认类加载器:通常是线程上下文类加载器(如果可用);
		不可用将加载ClassUtils(当前类加载器)类的类加载器将用作回退。
		如果您打算在显然更喜欢非空类加载器引用的场景中使用线程上下文类加载器,请调用此方法:
		例如,用于类路径资源加载(但不一定用于类)。forName,它也接受空类加载器引用)。
		
		该方法是 springBoot自定义的ClassLoader
		*/
		ClassLoader classLoader = getClassLoader();
		// 使用名称并确保惟一以防止重复 
		//返回实现了type(ApplicationContextInitializer.class)加载文件工厂对应的接口实现类
		Set<String> names = new LinkedHashSet<>(
				//(初始化)将上下文初始化器和classLoader 传进去
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
				//创建工厂实例,根据获取的names去创建对象
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
				//进行排序
		AnnotationAwareOrderComparator.sort(instances);
		//返回
		return instances;
	}

在这里插入图片描述

getClassLoader();

	public ClassLoader getClassLoader() {
		if (this.resourceLoader != null) {//由上分析 resourceLoader  = null
			return this.resourceLoader.getClassLoader();
		}
		//返回改类加载器
		return ClassUtils.getDefaultClassLoader();
	}

ClassUtils.getDefaultClassLoader();

返回要使用的默认类加载器:通常是线程上下文类加载器(如果可用);
不可用将加载ClassUtils类的类加载器将用作回退。
如果您打算在显然更喜欢非空类加载器引用的场景中使用线程上下文类加载器,请调用此方法:
例如,用于类路径资源加载(但不一定用于类)。forName,它也接受空类加载器引用)。

该方法是 springBoot自定义的ClassLoader

@Nullable
	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			//无法访问线程上下文类加载器-后退…
		}
		if (cl == null) {
			// 没有线程上下文类装入器——>使用这个类ClassUtils.class的类装入器。
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader()返回null表示引导类加载器
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// 不能访问系统类加载器-哦,好吧,也许调用者可以与空…
				}
			}
		}
		return cl;
	}

SpringFactoriesLoader spring工厂加载器(重要)

通用的工厂加载机制(加载工厂的类加载器)用于框架内部使用。
SpringFactoriesLoader从“META-INF/spring.factories”中加载并实例化给定类型的工厂(spring提供的一种约定)。类路径中的多个JAR文件中可能存在这个工厂文件(spring.factories)。**spring.factories文件必须采用属性(.factories)格式,其中键(key)是接口或抽象类的完全限定名(二进制名),值是用逗号分隔的实现类名列表。例如:

example.MyService = example.MyServiceImpl1,example.MyServiceImpl2
在这里插入图片描述
在例子。MyService是接口的名称MyServiceImpl1和MyServiceImpl2是两个实现。

loadFactoryNames

从“META-INF/spring”加载给定类型的工厂实现的完全限定类名。,使用给定的类加载器。
参数:
factoryClass——表示工厂的接口或抽象类
类加载器——用于加载资源的类加载器;可以为空来使用默认值吗

	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	  //获取工厂类的名字
		String factoryClassName = factoryClass.getName();
		//getOrDefault 返回对应map的key(加载进来的接口) 
		//有就返回实现factoryClassName(对应的初始化器ApplicationContextInitializer.class)接口de Value
		//没有返回对应数据 否返回Collections.emptyList()默认值(空集合)
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        //可以存储多个值 一般第一次加载时没有缓存 result = null
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
		 //classLoader != null
			Enumeration<URL> urls = (classLoader != null ?
				//FACTORIES_RESOURCE_LOCATION 加载当前路径下的spring.factories(工厂文件)
				//spring.factories(工厂文件)可能会存在多个jar包当中
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			//储存多值 当spring.factories里的配置器有多值时对应同个key 扫描时会直接添加到同一key
			//spring.factories(工厂文件)存在多个jar包当中其 当对应的key相同时 
			//其不同jar下的value值都会被组合到LinkedMultiValueMap当中
			result = new LinkedMultiValueMap<>();
			while (urls.hasMoreElements()) {
			    //获取当前的url路径
				URL url = urls.nextElement();
				//根据给定的URL对象创建一个新的UrlResource。
				UrlResource resource = new UrlResource(url);
				//将属性文件转换成Properties文件并加载到Properties文件中
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				//遍历文件的value值 将其放到map当中
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					//将当前的接口或抽象类的全类名取出
					String factoryClassName = ((String) entry.getKey()).trim();
					for (String factoryName : 
					//commaDelimitedListToStringArray 将逗号分隔的实现类转换成数组 方便遍历
					StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						//将接口或抽象类的全类名(不同jar) 和对应的value 存入result中
						result.add(factoryClassName, factoryName.trim());
					}
				}
			}
			//将classLoader 作为key result加载的文件作为值 缓存
			cache.put(classLoader, result);
			//返回最终加载的数据 一个13个接口 除了spring.factories 的文件(还加载了一些其他文件)
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

LinkedMultiValueMap

储存多值 当spring.factories里的配置器有多值时对应同个key 扫描时会直接添加到同一key
spring.factories(工厂文件)存在多个jar包当中其 当对应的key相同时
其不同jar下的value值都会被组合到LinkedMultiValueMap当中

在这里插入图片描述
合并后返回的结果合并后返回的结果
储存多值 当spring.factories里的配置器有多值时对应同个key 扫描时会直接添加到同一key
spring.factories(工厂文件)存在多个jar包当中其 当对应的key相同时
其不同jar下的value值都会被组合到LinkedMultiValueMap当中

在这里插入图片描述
很多jar包都存在这有的全路径接口
在这里插入图片描述

getName()

公共字符串getName ()

以字符串的形式返回这个类对象(使用getName方法的那个对象)表示的实体(类、接口、数组类、基本类型或void)的名称。
如果这个Class对象表示的引用类型不是数组类型,那么就会返回类的二进制名,这是Java™语言规范指定的。

如果这个类对象表示一个基本类型或void,那么返回的名称就是一个字符串,它等于与基本类型或void对应的Java语言关键字。(byte.class.getName() return byte)

如果这个类对象表示数组的一个类,那么名称的内部形式由元素类型的名称前面一个或多个表示数组嵌套深度的’[’([的个数取决于数组是几位数组)字符组成。元素类型名称的编码如下:
在这里插入图片描述在这里插入图片描述

getResources

查找具有给定名称的所有资源。资源是一些可以被类代码(自己写的)以独立于代码位置的方式访问的数据(图像、音频、文本等)。

资源的名称是标识资源的/分隔的路径名。
在getResource(String)文档中描述了搜索顺序。
参数:
名称——资源名称
返回:
资源的URL对象的枚举。如果找不到任何资源,枚举将为空。类加载器无法访问的资源并不会存在枚举中。
抛出:
IOException——如果发生I/O错误
apiNote
在重写此方法时,建议使用实现确保任何委托与getResource(String)方法一致。这应该确保枚举的nextElement方法返回的第一个元素与getResource(String)方法返回的资源相同。

    public Enumeration<URL> getResources(String name) throws IOException {
        @SuppressWarnings("unchecked")
        Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
        if (parent != null) {
            tmp[0] = parent.getResources(name);
        } else {
            tmp[0] = getBootstrapResources(name);
        }
        tmp[1] = findResources(name);
		//包含枚举的枚举对象
        return new CompoundEnumeration<>(tmp);
    }

在这里插入图片描述
将下图对应的value值加载到内存当中
在这里插入图片描述
在这里插入图片描述

createSpringFactoriesInstances

根据当前加载的接口实现类去创建对应对象,放到list当中
createSpringFactoriesInstances(工厂实现的类,Class数组,类加载器,jvm参数[])

	private <T> List<T> createSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set<String> names) {
			//创建对应names长度
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				//获取对应工厂的class对象并不初始化
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				//断言父类型isassignablefrom(子类型)为真 
				//断言instanceClass的父类型是否为type(如初始化器)
				Assert.isAssignable(type, instanceClass);
				//反射 获取该遍历name的 Constructor 构造对象 
				Constructor<?> constructor = instanceClass
						.getDeclaredConstructor(parameterTypes);
				//实例化该构造对象 
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				//将实例化的对象放到List集合当中
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException(
						"Cannot instantiate " + type + " : " + name, ex);
			}
		}
		// 返回实现ApplicationContextInitializer 接口的6个实例
				return instances;
	}

	

返回的的实例
在这里插入图片描述

ClassUtils.forName(name, classLoader);

	public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
			throws ClassNotFoundException, LinkageError {
		//断言
		Assert.notNull(name, "Name must not be null");
		//解析原生的class名字
		Class<?> clazz = resolvePrimitiveClassName(name);
		if (clazz == null) {
		//获取缓存对应name值
			clazz = commonClassCache.get(name);
		}
		if (clazz != null) {
			return clazz;
		}

		// "java.lang.String[]" style arrays
		if (name.endsWith(ARRAY_SUFFIX)) {
			String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
			Class<?> elementClass = forName(elementClassName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[Ljava.lang.String;" style arrays
		if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
			String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[[I" or "[[Ljava.lang.String;" style arrays
		if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
			String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		ClassLoader clToUse = classLoader;
		if (clToUse == null) {
			clToUse = getDefaultClassLoader();
		}
		try {
		//通过clToUse这个类加载器去加载name,并不初始化false
			return Class.forName(name, false, clToUse);
		}
		catch (ClassNotFoundException ex) {
			int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
			if (lastDotIndex != -1) {
				String innerClassName =
						name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
				try {
					return Class.forName(innerClassName, false, clToUse);
				}
				catch (ClassNotFoundException ex2) {
					// Swallow - let original exception get through
				}
			}
			throw ex;
		}
	}
	/**
	 *映射常用Java语言类名作为键,对应的类作为值。
	 *主要用于远程调用的高效反序列化。
	 */
	private static final Map<String, Class<?>> commonClassCache = new HashMap<>(64);

isAssignable

断言父类型isassignablefrom(子类型)为真 就是当前的类型是不是可以赋值给父类型。
Assert.isAssignable(父类型,子类型);

	public static void isAssignable(Class<?> superType, Class<?> subType) {
		isAssignable(superType, subType, "");
	}
	
	public static void isAssignable(Class<?> superType, @Nullable Class<?> subType, String message) {
		notNull(superType, "Super type to check against must not be null");
		if (subType == null || !superType.isAssignableFrom(subType)) {
			assignableCheckFailed(superType, subType, message);
		}
	}

instantiateClass

使用给定构造函数实例化类的便利方法。
注意,如果给定不可访问的构造函数(即非公共的),此方法将尝试设置可访问的构造函数,并支持带有可选参数和默认值的Kotlin类。

参数:
ctor——要实例化的构造函数
args——要应用的构造函数参数(如果Kotlin类需要使用可选参数和默认值,则对未指定的参数使用null)

返回:
新实例

public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
		//断言
		Assert.notNull(ctor, "Constructor must not be null");
		try {
			//如果一个类是私有的不可访问 将打破改规则 让其可被访问
			//	ctor.setAccessible(true);  反射 让其可被访问
			ReflectionUtils.makeAccessible(ctor);
			//是不是KotlinDetector的父类 和该类型 如果是用KotlinDelegate方法实例化
			//否则使用ctor.newInstance(args) 方法实例化
			return (KotlinDetector.isKotlinReflectPresent() && KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ?
					KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args));
		}
		catch (InstantiationException ex) {
			throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
		}
		catch (IllegalAccessException ex) {
			throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
		}
		catch (IllegalArgumentException ex) {
			throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
		}
		catch (InvocationTargetException ex) {
			throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
		}
	}

deduceMainApplicationClass

获取推断主应用类

	private Class<?> deduceMainApplicationClass() {
		try {
		//生成一个运行期异常 获取堆栈信息
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				//获取堆栈信息获取方法名为main的这个方法
				if ("main".equals(stackTraceElement.getMethodName())) {
					//返回这个main方法所在类的Class对象
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

run

运行Spring应用程序,创建并刷新一个新的ApplicationContext。

参数: 应用程序参数(通常从Java主方法传递)

返回: 一个运行的ApplicationContext

public ConfigurableApplicationContext run(String... args) {
  //StopWatch 简单的秒表,允许为许多任务计时,显示总运行时间和每个指定任务的运行时间。
  //启动一个秒表不启动任何任务
		StopWatch stopWatch = new StopWatch();
		//启动一个未命名的任务。如果调用stop()或计时方法而不调用此方法,则结果是未定义的。
		//里面定义了启动时间
		stopWatch.start();
		//定义一个上下文
		ConfigurableApplicationContext context = null;
		//设置声明异常报告器集合
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//配置系统属性
		configureHeadlessProperty();
		//得到运行监听器实例的集合
		//getRunListeners 获取实现SpringApplicationRunListener工厂实例的
		//此时springboot的运行监听器构造完成
		SpringApplicationRunListeners listeners = getRunListeners(args);
		//第一次启动run方法时立即调用。
		listeners.starting();
		try {
		  //提供对用于运行SpringApplication的参数的访问。
		  //就是传过来的命令行参数sring[] arg 参数会封装成ApplicationArguments  对象 
		  //使用Default(默认的实现类)
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			/*
			环境对象的配置必须通过ConfigurableEnvironment接口完成,该接口由所有
			AbstractApplicationContext子类getEnvironment()方法返回。
				*/
			//此时准备环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			//忽略environment里的配置信息
			configureIgnoreBeanInfo(environment);
			//日志 打印启动时的spring横幅和版本号 输出到控制台
			Banner printedBanner = printBanner(environment);
			//获取webApplicationType对应的ApplicationContext的对象实例
			context = createApplicationContext();
			//获取实现了SpringBootExceptionReporter.class的工厂实例
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 将上下文 配置环境 监听器 应用参数 横幅传过去 
			//此时控制台打印Starting MyApplication on HJ01280A with PID 776424 表示
			//准备上下文(将一些属性添加到对应的上下文当中) 此时profile 的设置日志被加载
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			//刷新上下文  刷新完成说明改上下文准备就绪了
			refreshContext(context);
			//刷新后  将上下文和JVM参数传入 什么都没做 主要是给子类留一个占位的方法
			afterRefresh(context, applicationArguments);
			//上下文已经刷新了 关闭秒表
			stopWatch.stop();
			//如果日志启动了
			if (this.logStartupInfo) {
				//将启动完成的日志信息(JVM用时)打印在控制台
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			//上下文已经刷新了 应用已经启动了 触发该事件 但
			//ApplicationRunner,CommandLineRunner还没被运行
			listeners.started(context);
			//调用ApplicationRunner,CommandLineRunner
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
		//上下文已经刷新了 应用已经启动了ApplicationRunner,CommandLineRunner 被调用了
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		//返回ConfigurableApplicationContext  
		return context;
	}

StopWatch

简单的秒表,为许多任务计时,显示总运行时间和每个指定任务的运行时间

隐藏了System.currentTimeMillis()的使用,提高了应用程序代码的可读性,减少了计算错误的可能性。

请注意,此对象不是设计为线程安全的,也不使用同步。

此类通常用于在概念验证和开发期间验证性能,而不是作为生产应用程序的一部分。

ConfigurableApplicationContext

在这里插入图片描述

ApplicationContext是springboot和spring中【特别重要(中心)】的应用程序上下文

在这里插入图片描述
【为应用程序提供配置的中央接口。在应用程序运行时】,这是只读的,但如果实现支持这一点,则可以重新加载。
加粗样式
一个ApplicationContext提供(这些功能来自继承的这些借口):

  • 用于访问应用程序组件的Bean工厂方法。从ListableBeanFactory继承

-以通用方式加载文件资源的能力。继承自org.springframe .core.io.ResourceLoader接口。
(如读取spring.xml)

-将事件发布到注册侦听器的能力。继承自ApplicationEventPublisher接口。【重要】

-解析消息的能力,支持国际化。继承自MessageSource接口。

从父上下文继承。后代上下文中的定义总是优先级(子优先级大于父类)。例如,这意味着单个父上下文可以被整个web应用程序使用,而每个servlet都有自己独立于任何其他servlet的子上下文
(子上下文是完全独立的)

除了标准的org.springframe .bean .factory。BeanFactory生命周期功能、ApplicationContext实现检测和调用ApplicationContext bean以及ResourceLoaderAware、ApplicationEventPublisherAware和MessageSourceAware bean。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {

	/**
	 * Return the unique id of this application context.
	 * @return the unique id of the context, or {@code null} if none
	 */
	@Nullable
	String getId();

	/**
	 * Return a name for the deployed application that this context belongs to.
	 * @return a name for the deployed application, or the empty String by default
	 */
	String getApplicationName();

	/**
	 * Return a friendly name for this context.
	 * @return a display name for this context (never {@code null})
	 */
	String getDisplayName();

	/**
	 * Return the timestamp when this context was first loaded.
	 * @return the timestamp (ms) when this context was first loaded
	 */
	long getStartupDate();

	/**
	 * Return the parent context, or {@code null} if there is no parent
	 * and this is the root of the context hierarchy.
	 * @return the parent context, or {@code null} if there is no parent
	 */
	@Nullable
	ApplicationContext getParent();

	/**
	 * Expose AutowireCapableBeanFactory functionality for this context.
	 * <p>This is not typically used by application code, except for the purpose of
	 * initializing bean instances that live outside of the application context,
	 * applying the Spring bean lifecycle (fully or partly) to them.
	 * <p>Alternatively, the internal BeanFactory exposed by the
	 * {@link ConfigurableApplicationContext} interface offers access to the
	 * {@link AutowireCapableBeanFactory} interface too. The present method mainly
	 * serves as a convenient, specific facility on the ApplicationContext interface.
	 * <p><b>NOTE: As of 4.2, this method will consistently throw IllegalStateException
	 * after the application context has been closed.</b> In current Spring Framework
	 * versions, only refreshable application contexts behave that way; as of 4.2,
	 * all application context implementations will be required to comply.
	 * @return the AutowireCapableBeanFactory for this context
	 * @throws IllegalStateException if the context does not support the
	 * {@link AutowireCapableBeanFactory} interface, or does not hold an
	 * autowire-capable bean factory yet (e.g. if {@code refresh()} has
	 * never been called), or if the context has been closed already
	 * @see ConfigurableApplicationContext#refresh()
	 * @see ConfigurableApplicationContext#getBeanFactory()
	 */
	AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;

SpringBootExceptionReporter

SpringBootExceptionReporter回调(函数式)接口用于支持自定义报告SpringApplication启动错误。报告器是通过SpringFactoriesLoader加载的,并且改接口实现类必须声明一个带有单个ConfigurableApplicationContext参数的公共构造函数。

在这里插入图片描述

@FunctionalInterface
public interface SpringBootExceptionReporter {

	/**
	*向用户报告启动失败。
	* @param失败源失败
	*如果报告失败,则返回{@code true};如果默认情况,则返回{@code false}
	*应进行报告。
	*/
	boolean reportException(Throwable failure);

}

configureHeadlessProperty();

private void configureHeadlessProperty() {
// 取出系统属性的值 如果有值 将SYSTEM_PROPERTY_JAVA_AWT_HEADLESS 做key 属性值做value
//如果没值默认为 Boolean.toString(this.headless) 为true
/*
这行代码的意思 springBoot服务主要运行在服务器端,是一个没有页面的应用没有鼠标键盘 在java称为
HEADLESS模式 这个HEADLESS模式系统属性的值就叫做“java.awt.headless” 表示该应用是服务器端应用
*/

	System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
				SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

系统属性
在这里插入图片描述
在这里插入图片描述

setProperty()

设置由指定键指示的系统属性。

首先,如果存在安全管理器,则使用它的SecurityManager。使用PropertyPermission(键“写”)权限调用checkPermission方法。这可能会导致抛出SecurityException。如果没有抛出异常,则将指定的属性设置为给定的值。
参数:
键——系统属性的名称。
值——系统属性的值。
返回:

系统属性的前一个值,如果没有则为空。

   public static String setProperty(String key, String value) {
        checkKey(key);
        SecurityManager sm = getSecurityManager();
        if (sm != null) {
            sm.checkPermission(new PropertyPermission(key,
                SecurityConstants.PROPERTY_WRITE_ACTION));
        }

        return (String) props.setProperty(key, value);
    }

getRunListeners

	private SpringApplicationRunListeners getRunListeners(String[] args) {
	/*ApplicationRunListener
	它应该声明一个public接受SpringApplication实例 和一个参数 String[] 的 公共构造函数
	*/
	//将其构造方法的两个参数放到Class数组中
		Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
		/*
		通过下面的构造方法
		传入日志和List<SpringApplicationRunListener> 集合 通过getSpringFactoriesInstances
		获取 实现了运行监听器的实现类实例
		
		*/
		return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
		// 运行监听器,types,当前对象(springApplication.class),jvm参数String[] args
		/*
		底层分析 types 就是获取指定带其参数的构造方法   args是用于其构造方法的实例的
		*/
				SpringApplicationRunListener.class, types, this, args));
	}

SpringApplicationRunListener.class, types, this, args)); 传过去的参数的作用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

SpringApplicationRunListeners 构造方法

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;
/*
调用该构造方法将 得到的实例 放到listeners变量当中
*/
	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

在这里插入图片描述

SpringApplicationRunListeners

SpringApplicationRunListeners与SpringApplicationRunListener之间的关系
SpringApplicationRunListeners里面的方法跟SpringApplicationRunListener是一一对应的
SpringApplicationRunListeners 是一个监听器容器,里面容纳了很多SpringApplicationRunListener

class SpringApplicationRunListeners {

	private final Log log;
	//运行监听器集合
	private final List<SpringApplicationRunListener> listeners;
	//当容器在启动的时候他会遍历对应的监听器 然后一一启动 其他方法类似这种
	SpringApplicationRunListeners(Log log,
			Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

	public void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	public void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	public void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	public void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	public void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	public void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}

	private void callFailedListener(SpringApplicationRunListener listener,
			ConfigurableApplicationContext context, Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

SpringApplicationRunListener 运行监听器

用于SpringApplication run方法的监听器springapplicationrunlistener是通过SpringFactoriesLoader加载的,它应该声明一个public接受SpringApplication实例 和一个参数 String[]公共构造函数。将为每次运行创建一个新的SpringApplicationRunListener实例

一种观察者模式的设计体现 用来观察SpringApplication 运行的生命周期各个阶段状态
监听器是监听某一个主题对象当这个主题对象发生变化或者某一个事件发生了变化,监听器就会对其产生回应,这里的回应就是这些方法被调用

其实现类会去调用对应的event 事件

public interface SpringApplicationRunListener {

	/**
	 * 第一次启动run方法时立即调用。可用于初始化。
	 *
	 */
	void starting();

	/**
	 *在准备好环境之后调用,但是在创建{@link ApplicationContext}之前调用。
	 @param环境
	 */
	void environmentPrepared(ConfigurableEnvironment environment);

	/**
	 * 在创建和准备{@link ApplicationContext}(上下文)之后调用,但在加载源之前调用。
	 * @param上下文应用程序上下文
	 */
	void contextPrepared(ConfigurableApplicationContext context);

	/**
	 *在加载应用程序上下文后但在刷新它之前调用。
	 * @param上下文应用程序上下文
	 */
	void contextLoaded(ConfigurableApplicationContext context);

	/**
	 *上下文已经刷新,应用程序已经启动,但是{@link CommandLineRunner CommandLineRunner}和
	 {@linkApplicationRunnerApplicationRunners}还没有被调用。
	 * @param context the application context.
	 * @since 2.0.0
	 */
	void started(ConfigurableApplicationContext context);

	/**
	 * 当应用程序上下文被刷新并且所有{@link CommandLineRunner CommandLineRunner}和{@link ApplicationRunner ApplicationRunners}都被调用时,在运行方法结束之前立即调用。@param上下文应用程序上下文。
	 * @since 2.0.0
	 */
	void running(ConfigurableApplicationContext context);

	/**
	 * 在运行应用程序时发生故障时调用。如果在创建上下文之前发生了故障,则@param上下文是应用程序上下文,
	 * 或者{@code null}
	 * @param exception the failure
	 * @since 2.0.0
	 */
	void failed(ConfigurableApplicationContext context, Throwable exception);

EventPublishingRunListener 事件 发布 运行 监听器

SpringApplicationRunListener发布SpringApplicationEvents。
是在实际刷新上下文之前触发的事件,并使用该内部ApplicationEventMulticaster。

在这里插入图片描述

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	private final SpringApplication application;

	private final String[] args;
	//简单的应用事件广播器
	private final SimpleApplicationEventMulticaster initialMulticaster;

public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public void starting() {
		//调用广播器的广播事件方法
		this.initialMulticaster.multicastEvent(
		/*
		官方定义SpringApplicationRunListener实现类存在一个构造方法 
		必须传Springapplication.class和String[] 数组
		
		创建了一个应用正在启动的事件 把源和参数本身传进去 此时创建完就相当于把事件本身发布出去了
		所有定义这个事件的监听者就会接收这个事件 同时接收 事件源和参数 就可以使用他们
		 */
				new ApplicationStartingEvent(this.application, this.args));
	}
	...其他同理 只不过事件不同

SimpleApplicationEventMulticaster 简单的 应用 事件 广播

该实现类是简单实现了ApplicationEventMulticaster接口。
将所有事件广播给所有已注册的侦听器,让侦听器忽略它们不感兴趣的事件。侦听器通常会对传入的事件对象执行相应的instanceof检查。

默认情况下,在调用线程中调用所有侦听器。可能会出现某个侦听器阻塞整个应用程序的危险但成本低。可以指定一个可选的任务执行器,以便在不同的线程中(例如从线程池中)执行侦听器

在这里插入图片描述

ApplicationStartingEvent 应用 启动 事件

只要启动了SpringApplication就会尽可能早地发布事件,在环境Environment或应用上下问ApplicationContext可用之前,但在applicationlistener注册之后。事件的来源是SpringApplication本身,但是要注意在早期阶段不要过多地使用它的内部状态,因为它可能在生命周期的后期被修改。

Spring本身定义了很多事件概念,在不同的阶段会有不同的事件产生 在这些事件中 所有的的来源都来自SpringApplicationEvent

public class ApplicationStartingEvent extends SpringApplicationEvent {

	/**
	 * 创建一个新的{@link ApplicationStartingEvent}实例。
	 * @param application the current application
	 * @param args the arguments the application is running with
	 */
	public ApplicationStartingEvent(SpringApplication application, String[] args) {
		super(application, args);
	}

}


SpringApplicationEvent

与SpringApplication相关的ApplicationEvent的基类。底层实现了很多事件 如刚启动事件,初始化事件,启动完事件,启动失败事件等等
当一个springboot应用在启动的时候,在某一个时间点,springboot就会产生一个事件通过广播的形式将该事件发送给所有的Listener(监听器)对应的监听器就会收到这个事件,而事件当中描述了当事件发生时的一些信息,监听器就会收到这些信息,由监听器本身来决定是否对这个事件进行响应。如果响应的话那么在他的监听方法代码当中进行一些操作 观察者模式的一个经典的应用
在这里插入图片描述

ConfigurableEnvironment

在这里插入图片描述
配置接口将由大多数(如果不是所有)环境类型实现。提供设置活动配置文件和默认配置文件以及操作底层数据源的工具。允许客户端通过ConfigurablePropertyResolver父接口设置和验证所需的属性、定制转换服务等。

如何操作属性源(属性都是在哪定义的)?
属性源可以被删除、重新排序或替换;可以使用从getPropertySources()返回的MutablePropertySources实例添加其他属性源。下面的例子是针对ConfigurableEnvironment的标准环境实现的,但是通常适用于任何实现,尽管特定的默认属性源可能不同。

示例:添加具有最高搜索优先级的新属性源


   ConfigurableEnvironment environment = new StandardEnvironment();
   MutablePropertySources propertySources = environment.getPropertySources();
   Map<String, String> myMap = new HashMap<>();
   myMap.put("xyz", "myValue");
   propertySources.addFirst(new MapPropertySource("MY_MAP", myMap));

示例:删除默认的系统属性属性源

   MutablePropertySources propertySources = environment.getPropertySources();
   propertySources.remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME)

示例:出于测试目的模拟系统环境

  MutablePropertySources propertySources = environment.getPropertySources();
   MockPropertySource mockEnvVars = new MockPropertySource().withProperty("xyz", "myValue");
   propertySources.replace(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, mockEnvVars);

当环境被ApplicationContext使用时,在调用上下文的refresh()方法之前执行任何这样的PropertySource操作是很重要的。这确保了在容器引导过程中所有的属性源都是可用的,包括属性占位符配置器的使用。

==Environment(环境)接口(重要) ==

改接口被sping内部所使用 开发人员大多都是通过${…}属性占位符使用他
【重要】两个关键组成部分(profiles and properties)概要文件和属性。
表示当前应用程序正在运行的环境的接口对应用程序环境的两个关键组成部分:(profiles and properties)概要文件和属性。与属性访问相关的方法通过PropertyResolver超接口公开。
profiles 概念如下

profiles是一组命名的逻辑bean定义,只有在给定的配置文件处于活悦状态时才向容器注册。可以将bean分配给一个配置文件,无论是在XML中定义的还是通过注释定义的; (如果未设置profiles会有默认的profiles去发挥这作用)
启动时 没有找到profiles 使用默认的 profiles: default(默认)
在这里插入图片描述
有关语法细节,请参阅spring-beans 3.1模式或@Profile注释。与概要文件相关的环境对象的作用是确定哪些概要文件(如果有的话)当前是活动的,以及哪些概要文件(如果有的话)在默认情况下应该是活动的。
properties概念如下
属性在几乎所有的应用程序中都扮演着重要的角色,它们可能来自各种各样的来源:属性文件、JVM系统属性、系统环境变量、JNDI、servlet上下文参数、特定属性对象、映射等等。与属性相关的环境对象的作用是**为用户提供一个方便的服务接口,用于配置属性源并从中解析属性。**

在ApplicationContext中管理的bean可以注册为EnvironmentalAware或@Inject环境,以便直接查询配置文件状态或解析属性

但是,在大多数情况下,应用程序级bean不需要直接与环境交互,而是必须使用${…}属性值被属性占位符(如PropertySourcesPlaceholderConfigurer (@Value(“¥小写{java…}”))替换,属性占位符本身是环境敏感的,从Spring 3.1开始,在使用时默认注册< context:property-placeholder/>.

环境对象的配置必须通过ConfigurableEnvironment接口完成,该接口由所有AbstractApplicationContext子类getEnvironment()方法返回。有关在应用程序上下文refresh()之前操作属性源的使用示例,请参阅ConfigurableEnvironment Javadoc。

prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		//获取并创建环境
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		//配置该环境 将其环境 和对应的源参数传入
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		listeners.environmentPrepared(environment);
		//将环境绑定到spring应用
		bindToSpringApplication(environment);
		if (!this.webEnvironment) {
		//通过环境转换器转换成标准环境
			environment = new EnvironmentConverter(getClassLoader())
			/*
			将给定环境转换为标准环境。如果环境已经是一个标准环境,
			而不是ConfigurableWebEnvironment(可配置的web环境),
			则不执行转换,以不变的方式返回。
			*/
					.convertToStandardEnvironmentIfNecessary(environment);
		}
		/*
		将ConfigurationPropertySource支持附加到指定的环境。将环境管理的每个PropertySource调整为
		ConfigurationPropertySource,并允许使用配置属性名解析经典的
		PropertySourcesPropertyResolver调用。
		附加的解析器将动态跟踪任何来自基础环境属性源的添加或删除。
		*/
			ConfigurationPropertySources.attach(environment);
		//返回标准环境
		return environment;
	}

configureEnvironment 配置参数

模板方法按照这个顺序委托给configurePropertySources(ConfigurableEnvironment, String[])和configureProfiles(ConfigurableEnvironment, String[])分别覆盖此方法以对环境自定义进行完全控制,或覆盖上述方法之一以对属性源或概要文件进行细粒度控制

	protected void configureEnvironment(ConfigurableEnvironment environment,
			String[] args) {
			//配置属性源
		configurePropertySources(environment, args);
		//配置属性文件
		configureProfiles(environment, args);
	}

属性源PropertySources和属性文件Profiles区别

Banner 用于以编程方式编写横幅(spring横幅)的接口类。

在这里插入图片描述

@FunctionalInterface
public interface Banner {

	/**
	 * 将横幅打印到指定的打印流。
	 * @param environment spring环境
	 * @param sourceClass 应用程序的源类
	 * @param 输出打印流
	 */
	void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);

	/**
	 * 用于配置Banner 的可能值的枚举。
	 */
	enum Mode {

		/**
		 * 禁止打印 banner.
		 */
		OFF,

		/**
		 * 将banner打印到System.out。
		 */
		CONSOLE,

		/**
		 * 将banner打印到日志文件
		 */
		LOG

	}

}

private Banner getTextBanner(Environment environment) {
		String location = environment.getProperty(BANNER_LOCATION_PROPERTY,
				DEFAULT_BANNER_LOCATION);
				//获取文件资源
		Resource resource = this.resourceLoader.getResource(location);
		if (resource.exists()) {
			//如果resources存在banner.txt 直接返回
			return new ResourceBanner(resource);
		}
		//不存在返回null 加载默认的
		return null;
	}

在这里插入图片描述
在这里插入图片描述

createApplicationContext(重要)

该方法只要是根据不同webApplicationType 去创建不同的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);
			}
		}
		//通过反射获取到该class的实例
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

prepareContext

private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		//set环境
		context.setEnvironment(environment);
		//后处理对应上下文
		postProcessApplicationContext(context);
		//应用初始化设置
		applyInitializers(context);
		//调用上下文配置完毕事件 此时源没被加载
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			//控制台输出profiles对应设置信息
			logStartupProfileInfo(context);
		}
		// 添加一些boot的单例bean
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		// 加载源
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		//调用源已经被加载但是还没被刷新的事件
		listeners.contextLoaded(context);
	}

callRunners

	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);
		}
	}

	private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
		try {
			(runner).run(args.getSourceArgs());
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
		}
	}
原创文章 39 获赞 6 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42261668/article/details/103029333
今日推荐