Spring源码解读(十三)Boot启动类——SpringApplication构造方法

Spring源码解读(十三)Boot启动类——SpringApplication构造方法,第1张

前言

springBoot是一个基于spring开发的一个快速开发框架,用来简化Spring项目的初始搭建和开发的过程。使用其特定的方式简化复杂的xml配置工作,通俗的讲就是“约定优于配置”,这篇博文将分析一下SpringApplication的构造方法。源码版本:SpringBoot-2.2.11-RESEALE

程序入口

在spring项目中,需要创建启动类,调用SpringApplication.run方法启动项目,所以以此处作为分析入口。

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
构造方法
	public SpringApplication(Class... primarySources) {
		this(null, primarySources);
	}

	@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));
		// Web 应用程序类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 设置应用上下文初始化器
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 设置应用监听器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 推导出当前启动的main方法所在的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}
获取web应用程序类型

方法内通过判断webflux相关的类是否存在,存在这是RESCTIVE类型,不存在继续判断servlet是否存在,存在则是SERVLET,不存在则是NONE。


	private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext" };

	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() {
		// ClassUtils.isPresent 通过这个方法来判断常量中的类是否存在
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			// 响应式web项目,对应 spring-webflux
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				// 非web项目,不需要创建web容器
				return WebApplicationType.NONE;
			}
		}
		// servlet项目,对应spring mvc项目
		return WebApplicationType.SERVLET;
	}
设置应用上下文初始化器和设置应用监听器

这两步用到都是同一个SpringApplication#getSpringFactoriesInstances(java.lang.Class)方法,只是传入的不同的类型。

getSpringFactoriesInstances

一般是用来解析META-INF/spring.factories文件中部分接口的实现类,实现可拔插的效果。

	private  Collection getSpringFactoriesInstances(Class type) {
		return getSpringFactoriesInstances(type, new Class[] {});
	}

	private  Collection getSpringFactoriesInstances(Class type, Class[] parameterTypes, Object... args) {
		ClassLoader classLoader = getClassLoader();
		// 从META-INF/spring.factories文件中加载和实例化给定类型的工厂。
		// Use names and ensure unique to protect against duplicates
		Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 根据names反射创建对应的实例
		List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
使用给定的类加载器从“META-INFspring.factories”加载给定类型的工厂实现的完全限定类名。

SpringFactoriesLoader这个类是spring的,不是boot专有的,源码在org.springframework.core.io.support中。

	public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
		// 获取spring.factories中key对应的接口名字
		String factoryTypeName = factoryType.getName();
		// 将spring.factories配置转为map后,通过接口全名获取对应的配置项list
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	/**
	 * 将spring.factories中的键值对转换为map对象
	 * @param classLoader
	 * @return
	 */
	private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			Enumeration urls = (classLoader != null ?
					// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
					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 factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}
通过反射创建对应的实例
	@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;
	}
推导出当前启动的main方法所在的类
	/**
	 * 推导出当前启动的main方法所在的类
	 * @return
	 */
	private Class deduceMainApplicationClass() {
		try {
			// 新建一个RuntimeException异常,获取当前堆栈信息
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				// 遍历堆栈信息,找到main所在的类
				if ("main".equals(stackTraceElement.getMethodName())) {
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/728818.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存