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

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

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

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

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

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

@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<?> 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)

公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值