springboot的SpringApplication的创建过程

本文详细解析了SpringApplication的创建过程,包括初始化参数、决定容器类型、加载初始化器和监听器,以及确定启动类。深入探讨了如何通过类路径判断环境,加载spring.factories配置,实现了自动化配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringApplication的创建过程

运行main方法
new SpringApplication(primarySources).run(args)
new SpringApplication(primarySources)
	primarySources就是自己的启动类调用如下构造方法
	/**
	 * resourceLoader,此时传的是null
	 * primarySources 一般的是我们的启动类
	 */
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //1.resourceLoader资源加载的,这里为空,按照spring的尿性一般的是**Context,当然这里暂时还没有context
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
    //启动类不能为空,不然报错
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //2.决定容器的类型,NONE、servlet、REACTIVE①
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //3.初始化器的装载②
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //4.容器监听器的加载③
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    //5.返回启动类的class对象④
		this.mainApplicationClass = deduceMainApplicationClass();
	}

注释:

①容器类型的决定

//存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet并且不存在org.glassfish.jersey.servlet.ServletContainer就是REACTIVE

//javax.servlet.Servlet",
			"org.springframework.web.context.ConfigurableWebApplicationContext都没有就是NONO
//默认就是SERVLET               
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;
	}

/**
 * 判断哪些类在不在,上面classLoader传过来就是null
 *
 */
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
			throws ClassNotFoundException, LinkageError {

		Assert.notNull(name, "Name must not be null");
		//方法1.1,首先判断名字不为空,并且名字长度小于8
		Class<?> clazz = resolvePrimitiveClassName(name);
		if (clazz == null) {
            //1.2commonClassCache主要是java.lang包里面的
			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) {
            //默认当前线程的classLoader
            //Thread.currentThread().getContextClassLoader()
			clToUse = getDefaultClassLoader();
		}
		try {
            //直接调用Class.forName
			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 {
                    //上面获取不到判断是不是一个内部类的名字继续Class.forName
					return Class.forName(innerClassName, false, clToUse);
				}
				catch (ClassNotFoundException ex2) {
					// Swallow - let original exception get through
				}
			}
			throw ex;
		}
	}
上面一整套下来解决了一个问题,在这个环境里面有没有某一个类

1.1 primitiveTypeNameMap的数据,resolvePrimitiveClassName方法里面的(m代码未贴出来)

在这里插入图片描述

1.2commonClassCache,commonClassCache.get(name);方法的数据,都是java.lang包下面的,这里就直接debug打印

{java.lang.Throwable=class java.lang.Throwable, java.lang.Float=class java.lang.Float, [Ljava.lang.Integer;=class [Ljava.lang.Integer;, [Ljava.lang.Boolean;=class [Ljava.lang.Boolean;, java.lang.Double=class java.lang.Double, java.util.Enumeration=interface java.util.Enumeration, java.lang.Error=class java.lang.Error, [Ljava.lang.Character;=class [Ljava.lang.Character;, [Ljava.lang.Double;=class [Ljava.lang.Double;, java.lang.Integer=class java.lang.Integer, java.lang.AutoCloseable=interface java.lang.AutoCloseable, java.lang.Iterable=interface java.lang.Iterable, java.lang.Character=class java.lang.Character, java.lang.Enum=class java.lang.Enum, java.lang.Long=class java.lang.Long, java.lang.Short=class java.lang.Short, java.util.Map=interface java.util.Map, java.lang.Boolean=class java.lang.Boolean, [Ljava.lang.Number;=class [Ljava.lang.Number;, java.lang.StackTraceElement=class java.lang.StackTraceElement, java.lang.String=class java.lang.String, java.lang.Byte=class java.lang.Byte, java.lang.Number=class java.lang.Number, [Ljava.lang.Float;=class [Ljava.lang.Float;, java.util.Map$Entry=interface java.util.Map$Entry, java.lang.Cloneable=interface java.lang.Cloneable, [Ljava.lang.StackTraceElement;=class [Ljava.lang.StackTraceElement;, java.util.Iterator=interface java.util.Iterator, java.io.Externalizable=interface java.io.Externalizable, java.util.Collection=interface java.util.Collection, [Ljava.lang.Byte;=class [Ljava.lang.Byte;, [Ljava.lang.Class;=class [Ljava.lang.Class;, java.util.Set=interface java.util.Set, java.lang.RuntimeException=class java.lang.RuntimeException, [Ljava.lang.String;=class [Ljava.lang.String;, java.lang.Exception=class java.lang.Exception, java.io.Serializable=interface java.io.Serializable, [Ljava.lang.Object;=class [Ljava.lang.Object;, java.util.List=interface java.util.List, [Ljava.lang.Long;=class [Ljava.lang.Long;, java.io.Closeable=interface java.io.Closeable, [Ljava.lang.Short;=class [Ljava.lang.Short;, java.util.Optional=class java.util.Optional, java.lang.Class=class java.lang.Class, java.lang.Object=class java.lang.Object, java.lang.Comparable=interface java.lang.Comparable}

③公用一个方法

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
		//从META-INF/spring.factories里面读取配置,注意这一步,这一步很重要2.1
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//实例化,常规反射
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		//排序,怎么排序这次没看,按照以往经验,一般都是实现Order或者PriorityOrdered接口的,或者有@Order的在前面,具体细节本次没看(好吧,其实是看不懂)
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

2.1步骤详解

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

	//这一步返回的是一个map
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		
        //核心步骤,classLoader.getResources,ClassLoader.getSystemResources
        //这俩方法是java基础里面的获取资源的方法,最后进行遍历封装到map里面
		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);
		}
	}

loadSpringFactories里面的内容
在这里插入图片描述

上面这些功能都不大相同,我说一下本人用过的,

1.org.springframework.boot.autoconfigure.EnableAutoConfiguration,这个很简单,你把你的配置类放在META-INF/spring.factories里面,就相当于自己加了@Component注解(意义:别人写的jar包你总不能去里面加注解吧,这个就是实现springboot自动化配置的一个重要步骤)

2.其他的讲道理都没有直接用过,暂时不去了解各个类是干什么的

//StackTraceElement在平常debug的时候挺有用的
private Class<?> deduceMainApplicationClass() {
		try {
            //返回方法的调用栈
			StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
			for (StackTraceElement stackTraceElement : stackTrace) {
				if ("main".equals(stackTraceElement.getMethodName())) {
                    //找到main方法的类
					return Class.forName(stackTraceElement.getClassName());
				}
			}
		}
		catch (ClassNotFoundException ex) {
			// Swallow and continue
		}
		return null;
	}

总结
1.SpringApplication的创建到此结束,其中一些loadSpringFactories(META-INF/spring.factories里面加载的还未弄清楚)
2.spring上下文的创建过程参考
3.spring容器的refresh()方法,springbbot里面的refresh还是这个方法执行原理一样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值