Springboot源码分析 SpringApplication初始化

Springboot源码分析 SpringApplication初始化

描述:众所周知,springboot以轻量级/简化著称,其可通过Main方法直接运行项目

SpringApplication初始化过程如下

@SpringBootApplication
public class SpringBootDemo81Application {

	public static void main(String[] args) {//步骤1
		SpringApplication.run(SpringBootDemo81Application.class, args);
	}
}

    /**
	 * Static helper that can be used to run a {@link SpringApplication} from the
	 * specified source using default settings.
	 * @param primarySource the primary source to load
	 * @param args the application arguments (usually passed from a Java main method)
	 * @return the running {@link ApplicationContext}
	 */
	public static ConfigurableApplicationContext run(Class primarySource,
			String... args) {//步骤2
		return run(new Class[] { primarySource }, args);
	}
	
/**
 * Static helper that can be used to run a {@link SpringApplication} from the
 * specified sources using default settings and user supplied arguments.
 * @param primarySources the primary sources to load
 * @param args the application arguments (usually passed from a Java main method)
 * @return the running {@link ApplicationContext}
 */
public static ConfigurableApplicationContext run(Class[] primarySources,
		String[] args) {//步骤3
	return new SpringApplication(primarySources).run(args);
}

public SpringApplication(Class... primarySources) {
	this(null, primarySources);//步骤4
}

//步骤5
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));//@1
	this.webApplicationType = WebApplicationType.deduceFromClasspath();//@2
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));//@3
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));//@4
	this.mainApplicationClass = deduceMainApplicationClass();//@5
}

如上所需,通过debug源码跟进发现总共执行5个步骤
步骤1 传入SpringBootDemo81Application类,此类是核心应用类,用于构建,args未传入
步骤2 主要是构建SpringApplication 对象,然后调用其run方法
步骤3.4。.5用于构建SpringApplication 对象,详细跟分析步骤5如下
@1设置primarySources 等于传入的Springboot类
@2接着设置当前运行的环境,通过一个枚举判断当前所处的环境状态

/**
 * The application should not run as a web application and should not start an
 * embedded web server.
 */
NONE,

/**
 * The application should run as a servlet-based web application and should start an
 * embedded servlet web server.
 */
SERVLET,

/**
 * The application should run as a reactive web application and should start an
 * embedded reactive web server.
 */
REACTIVE;

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

@3初始化加载一些参数和配置

private  Collection getSpringFactoriesInstances(Class type,
			Class[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();//@3-1
		// Use names and ensure unique to protect against duplicates
		Set names = new LinkedHashSet<>(//@3-2
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		List instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);//@3-3
		AnnotationAwareOrderComparator.sort(instances);//@3-4
		return instances;
	}

//@3-1获取当前线程的ClassLoader
//@3-2加载配置如下

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration 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);
		}
	}

这段代码主要是通过classLoader加载所以spring相关jar里面META-INF/spring.factories的配置,涉及到工程如下(spring-boot、spring-boot-autoconfigure、spring-boot-devtools)
然后把当前线程的ClassLoader对应资源集合存储到cache缓存中,便于后续使用

接着分析上面//@3-3

@SuppressWarnings("unchecked")
	private  List createSpringFactoriesInstances(Class type,
			Class[] parameterTypes, ClassLoader classLoader, Object[] args,
			Set names) {
		List instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				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;
	}

之前步骤已经把相关类加载,此处主要是转换实体,加入到集合中
//@3-4对集合进行排序
//@4同3一样进行赋值并加入集合
//@5 获取到Main函数启动所在类

private Class deduceMainApplicationClass() {
		try {
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

获取当前方法的调用栈,找到main函数所在类,并设置到SpringApplication对象的mainApplicationClass属性

总结:

SpringApplication初始化,主要进行了项目包结构配置加载,并获取当前方法的调用栈,找到main函数所在类所运行

作者简介:张程 技术研究

更多文章请关注微信公众号:zachary分解狮 (frankly0423)

公众号

你可能感兴趣的:(springboot源码,spring源码,Spring,专栏)