从0开始深入理解Spring(1)--SpringApplication构造

从0开始深入理解Spring-启动类构造

引言:
从本篇开始,打算深入理解Spring执行过程及原理,个人理解极有可能有偏差,评论区欢迎指正错误,下面开始进入正片内容。
ps: springboot版本使用2.4.8

Springboot项目启动时,是通过main方法中编写启动类的形式启动的,按照下面格式编写启动类

@SpringBootApplication
public class SpringFrameStudyApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringFrameStudyApplication.class);
    }
}

其核心为SpringApplication.run(SpringFrameStudyApplication.class)方法,下面深入探讨该方法的执行

SpringApplication.java位于org.springframework.boot包下,是Springboot项目启动的核心类。点击run()方法。进入该方法

这里run方法不做过多解析,下篇会详解run方法的具体执行过程

SpringApplication.java

	public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
		return run(new Class<?>[] { primarySource }, args);
	}
	
	public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		// 在这里创建了SpringApplication对象,并执行run方法。执行完毕后返回配置好的上下文对象ConfigurableApplicationContext
		return new SpringApplication(primarySources).run(args);
	}
	// 构造方法
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}
	
	// SpringApplication核心构造方法
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 资源加载器: 如果按照上述方法进行构造时,为null
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// webApplicationType属性用于判断运行容器: 为Servlet还是Reactive。Reactive为响应式的架构: SpringWebFlex
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 这里重点介绍下。从SpringFactories中获得Boot的注册器及初始化器等相关信息并将springfatory中的信息初始化至缓存中的
		this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
		// 设置Application上下文初始化器 相关属性(赋值操作)
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 设置应用监听初始化器 相关属性(赋值操作)
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 赋值 jvm线程栈中 main方法栈所在类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

这里避免类代码过长过重,拆分两部分进行代码讲解。下面这一部分介绍getBootstrapRegistryInitializersFromSpringFactories()是如何从MATE-INFO/spring.factory中获取相关属性

SpringApplication.java


	private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
		ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList<>();
		// 核心方法,获得Bootstrapper类有关的SpringFactory实例
		getSpringFactoriesInstances(Bootstrapper.class).stream()
				.map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
				.forEach(initializers::add);
		// 获得BootstrapRegistryInitializer类有关的SpringFactory实例
		initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		return initializers;
	}
	
	public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
		this.initializers = new ArrayList<>(initializers);
	}
	
	public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
		this.listeners = new ArrayList<>(listeners);
	}
	
	/**
     * 根据类型获得SpringFacotries实例
     * @param type
     */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}
	
	/**
     * 根据类型获得SpringFacotries实例
     * @param type 类类型
     * @param parameterTypes 参数类型
     * @param args 构造方法的参数
     */
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// 这里getClassLoader()为内部的一个方法
		ClassLoader classLoader = getClassLoader();
		// 根据传入的类获得SpringFactories中的限定名称
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 根据名称集合创建实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		// 按照order进行排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

	public ClassLoader getClassLoader() {
		if (this.resourceLoader != null) {
		    // 正常默认启动时,resourceLoader为空
			return this.resourceLoader.getClassLoader();
		}
		// 默认返回当前主线程
		return ClassUtils.getDefaultClassLoader();
	}

SpringFactoriesLoader.java

	// 获取spring.factories中的所在位置
	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
	
	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		// 加载所有springfactories的实现类并 根据factoryTypeName获得该实现类的名称
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
	    // 从缓存中获取已经加载的类 名称
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		// 如果没有
		result = new HashMap<>();
		try {
		    // 类加载器中获得spring.factories的所在url地址
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				// 加载resource资源
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						// 添加实现类名称
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// 将其列表去重并修改为不可修改的集合
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			// 加入缓存中 key为classLoader
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

构造SpringApplication实例的过程实际上是完成了spring.factories文件的扫描,并将扫描好的factories配置放入缓存中(key: className, values: List names)。按照需要去获取设置 上下文、监听等 实现类。

相关推荐

  1. 0开始深入理解Spring(1)--SpringApplication构造

    2024-04-22 14:38:06       17 阅读
  2. 开始精通RTSP之深入理解RTCP协议

    2024-04-22 14:38:06       18 阅读
  3. 0~1开发财务软件

    2024-04-22 14:38:06       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-22 14:38:06       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-22 14:38:06       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-22 14:38:06       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-22 14:38:06       20 阅读

热门阅读

  1. kubeadm 升级 k8s集群 1.17到1.20

    2024-04-22 14:38:06       15 阅读
  2. SpringCloudAlibaba+RocketMQ实现对消息中间件的整合

    2024-04-22 14:38:06       17 阅读
  3. Docker中Kafka容器创建/更新Topic支持多分区

    2024-04-22 14:38:06       12 阅读
  4. WPF-关于动画Animation(及其常见问题)

    2024-04-22 14:38:06       31 阅读
  5. Flask RESTful视图使用(蓝图、装饰器、渲染模板)

    2024-04-22 14:38:06       54 阅读
  6. 三个目前主流的计算机视觉软件

    2024-04-22 14:38:06       54 阅读
  7. 类和对象(下)

    2024-04-22 14:38:06       18 阅读
  8. C++静态变量

    2024-04-22 14:38:06       18 阅读
  9. 【python】一文读懂python函数

    2024-04-22 14:38:06       43 阅读
  10. ADC通道检测功能-单片机通用模板

    2024-04-22 14:38:06       42 阅读
  11. AI原生技术分享活动:引领智能科技新浪潮

    2024-04-22 14:38:06       24 阅读