SpringApplication对象的构建及spring.factories的加载时机

这篇具有很好参考价值的文章主要介绍了SpringApplication对象的构建及spring.factories的加载时机。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

构建SpringApplication对象源码:
1、调用启动类的main()方法,该方法中调用SpringApplication的run方法。

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

2、调用SpringApplication的run()方法的重载方法,在发方法内构建了SpringApplication对象

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
		return new SpringApplication(primarySources).run(args);
	}

3、构建SpringApplication对象。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		//resourceLoader为null
		this.resourceLoader = resourceLoader;
		//PrimarySources(即启动类)一定不能为null
		Assert.notNull(primarySources, "PrimarySources must not be null");
		//初始化primarySources属性
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		//从Classpath中推断Web应用类型。
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

构建过程:

接下来我们来详细研究一下上述过程。在构建SpringApplication对象过程中,此时resourceLoader
为null,primarySources一定不为空且需要初始化为启动类SpringbootdemoApplication
。从Classpath中推断出Web应用类型并初始化webApplicationType,通过getSpringFactoriesInstances()获取Spring工厂实例来初始化bootstrapRegistryInitializers,initializers(List<ApplicationContextInitializer<?>>应用程序上下文初始化器列表), listeners(List

4、从Classpath推断Web应用类型,调用的是WebApplicationType类的deduceFromClasspath()方法。如果Classpath中包含DispatcherHandler,则表明当前WebApplicationType为REACTIVE;如果既不包含javax.servlet.Servlet也不包含Spring中的ConfigurableWebApplicationContext,则表明当前WebApplicationType为NONE; 上述两种都不是则表明当前WebApplicationType是SERVLET

static WebApplicationType deduceFromClasspath() {
        // 如果org.springframework.web.reactive.DispatcherHandler的class文件存在且可以加载
        //不存在org.springframework.web.servlet.DispatcherServlet
        //不存在org.glassfish.jersey.servlet.ServletContainer
        //则返回REACTIVE表明 该应用程序应作为响应式Web应用程序运行,并应启动嵌入式servlet Web 服务器
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		//遍历SERVLET 指标类
		//如果不存在javax.servlet.Servlet也不存在org.springframework.web.context.ConfigurableWebApplicationContext
		//则返回NONE表明该应用程序不应作为 Web 应用程序运行,也不应启动嵌入式 Web 服务器
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		//返回SERVLET表明该应用程序应作为基于servlet的 Web 应用程序运行,并应启动嵌入式 servlet Web 服务器。
		return WebApplicationType.SERVLET;
	}

spring.factories的加载时机
1、在构建SpringApplication中,初始化其属性bootstrapRegistryInitializers属性时进行加载 /META-INF/spring.factories。
2、构建SpringApplication对象时,通过调用getSpringFactoriesInstances(Class type)获取工厂实例。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		......
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	    ......
	}

3、我们以初始化bootstrapRegistryInitializers为例讲解,getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args)中首先获取ClassLoader ,通过SpringFactoriesLoader机制,根据ClassLoader从类路径jar包中加载META-INF/spring.factories下的所有工厂类及其实现类 。

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 classLoader = getClassLoader();
		// 使用SpringFactoriesLoader机制加载出工厂名,并放入Set集合中确保其唯一性。但是在META-INF/spring.factories中并无BootstrapRegistryInitializer所以此处的names的size为0。
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//创建Spring工厂实例(但因为上述names的size为0,所以对于BootstrapRegistryInitializer来说它并不会创建SpringFactory实例)
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		//通过AnnotationAwareOrderComparator对Spring工厂实例排序
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

4、SpringFactoriesLoader中的loadFactoryNames来加载META-INF/spring.factories下的所有工厂类及其实现类

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		//获取当前使用的ClassLoader
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
		    //为空,则获取SpringFactoriesLoader的类加载器
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		//org.springframework.boot.BootstrapRegistryInitializer
		String factoryTypeName = factoryType.getName();
		//加载META-INF/spring.factories下的扩展类,没有值的返回一个空列表。
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

5、重点关注SpringFactoriesLoader中的loadSpringFactories(ClassLoader classLoader),该方法具体实现了从META-INF/spring.factories下加载扩展类。

//工厂类所在位置,在多个jar文件中都有。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        //检查缓存中是否有工厂类
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}
		//缓存中没有,初始化result
		result = new HashMap<>();
		try {
		    //加载META-INF/spring.factories下的一系列元素
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			//迭代遍历
			while (urls.hasMoreElements()) {
			    //spring-boot-版本号.jar文件中Spring.factories所在的绝对路径
				URL url = urls.nextElement();
				//构建UrlResource
				UrlResource resource = new UrlResource(url);
				//加载该UrlResource中的属性(即Spring.factories中的键值对)
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
				    //父类工厂类型名
					String factoryTypeName = ((String) entry.getKey()).trim();
					//子类工厂实现类名数组(在Spring.factories中多个用逗号分隔)
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					//将工厂类型名,工厂实现类列表放入名为result的 Map<String, List<String>>中,key为工厂类型名,value为工厂实现类列表。
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			//将result中的所有list都置为包含唯一元素的不可修改的list
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			//放入缓存。		
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}
bootstrapRegistryInitializers 的初始化中实现了加载META-INF/spring.factories中工厂扩展类(但是在META-INF/spring.factories并无bootstrapRegistryInitializers ),并将其放入缓存Map<String, List>,其中key为父类工厂名,value为其对应扩展类列表。之后initializers(ApplicationContextInitializer列表)以及listeners(ApplicationListener列表)的初始化都是从该缓存中获取值。

6、接下里我们看一下第三步中的createSpringFactoriesInstances()方法。由于bootstrapRegistryInitializers 在META-INF/spring.factories中并不存在。所以我们只有它返回的instances是空的即它不会创建SpringFactoriesInstances。但是初始化initializers,listeners时,却会。我们看一下具体源码。文章来源地址https://www.toymoban.com/news/detail-623407.html

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		//初始化names.size()大小的list存放创建出来的实例。
		List<T> instances = new ArrayList<>(names.size());
		//遍历传入的工厂扩展类实例名
		for (String name : names) {
			try {
			    //通过反射获取instance的Class对象
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				//instanceClass是一个type类型,向下,否则抛异常IllegalArgumentException
				Assert.isAssignable(type, instanceClass);
				//获取instanceClass的构造器
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				//通过BeanUtils的instantiateClass()方法实例化类。
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
			    //将实例化出来的类放入instances
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

到了这里,关于SpringApplication对象的构建及spring.factories的加载时机的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 类加载的时机

    类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括以下 7 个阶段: 加载 验证 准备 解析 初始化 使用 卸载 验证、准备、解析 3 个阶段统称为连接。 加载、验证、准备、初始化和卸载这 5 个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就

    2024年02月11日
    浏览(30)
  • 【Spring Boot 源码学习】初识 SpringApplication

    《Spring Boot 源码学习系列》 往期的博文, Huazie 围绕 Spring Boot 的核心功能,带大家从总整体上了解 Spring Boot 自动配置的原理以及自动配置核心组件的运作过程。这些内容大家需要重点关注,只有了解这些基础的组件和功能,我们在后续集成其他三方类库的 Starters 时,才能够

    2024年02月05日
    浏览(40)
  • Spring Boot学习随笔- @SpringBootApplication详解、加载绝对路径配置文件、工厂创建对象(@ConfigurationProperties、@Value)

    学习视频:【编程不良人】2021年SpringBoot最新最全教程 这是一个 组合注解 ,就是由多个注解组成。下列注解红框内称为 元注解 (jdk提供) @Target :指定注解作用范围 @Retention :指定注解什么时候生效 重要注解 @SpringBootConfiguration:自动配置Spring、SpringMVC相关环境 @EnableAutoCo

    2024年02月05日
    浏览(57)
  • 从零开始学Spring Boot系列-SpringApplication

    SpringApplication类提供了一种从main()方法启动Spring应用的便捷方式。在很多情况下, 你只需委托给 SpringApplication.run这个静态方法 : 当应用启动时, 你应该会看到类似下面的东西: 默认情况下会显示INFO级别的日志信息, 包括一些相关的启动详情, 比如启动应用的用户等。 通过

    2024年04月08日
    浏览(63)
  • Spring Boot原理分析 | SpringApplication、Yaml、Properties

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring开源框架,轻量级的Java开发框架,解决企业级应用开发的复杂性而创建,简化开发 基于POJO的轻量级和最小侵入型编程 通过IOC,依赖注入(DI)和面向接口实现松耦合 基于切面(AOP)和惯例进行声明式编程 通过切

    2024年02月12日
    浏览(43)
  • 【Spring Boot 源码学习】SpringApplication 的定制化介绍

    《Spring Boot 源码学习系列》 前面的博文, Huazie 带大家从 Spring Boot 的启动类 SpringApplication 上入手,了解了 SpringApplication 的实例化过程。这实例化构造过程中包含了各种初始化的操作,都是 Spring Boot 默认配置的。如果我们需要定制化配置, SpringApplication 也提供了相关的入口

    2024年01月25日
    浏览(36)
  • 《深入理解Java虚拟机》读书笔记: 虚拟机类加载的时机和过程

    虚拟机类加载的时机和过程 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个

    2024年02月12日
    浏览(43)
  • llama factory 是如何加载数据集 通过对数据集加载的代码的理解编写自定义数据集训练代码

    第一层从训练代码追踪到以下代码 这段Python代码定义了一个名为 get_dataset 的函数,其目的是根据给定的参数加载和预处理一个数据集。下面是该函数的逐步解读: 函数参数 : tokenizer : 一个预训练的tokenizer对象,用于处理文本数据。 model_args , data_args , training_args : 分别包含

    2024年04月13日
    浏览(43)
  • spring.factories

    Spring Boot 如何管理第三方Bean 首先抛出一个问题:如果想要被Spring容器管理的Bean的路径不再Spring Boot 的包扫描路径下,怎么办呢?也就是如何去加载第三方的Bean 呢? 换句话说:在 Spring Boot 项目中,如果你想要被 Spring 容器管理的 bean 不在 Spring Boot 包扫描路径下,怎么办?

    2024年02月08日
    浏览(25)
  • spring.factories 文件配置详情

    在程序开发中,可能会出现包名不一样的情况(如:pom 依赖的很多的 jar),如何解决Spring Boot不能被默认路径扫描呢? 方法一:在 Spring Boot Application 主类上使用 @Import 注解。 方法二:使用 spring.factories 文件 方法一比较简单,在此就不做过多介绍,主要谈谈 spring.factories 使

    2024年02月01日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包