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时,却会。我们看一下具体源码。

复制代码
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;
	}
相关推荐
带刺的坐椅21 小时前
SpringBoot3 使用 SolonMCP 开发 MCP
java·ai·springboot·solon·mcp
LUCIAZZZ2 天前
JVM之虚拟机运行
java·jvm·spring·操作系统·springboot
堕落年代2 天前
SpringSecurity当中的CSRF防范详解
前端·springboot·csrf
一零贰肆2 天前
深入理解SpringBoot中的SpringCache缓存技术
java·springboot·springcache·缓存技术
LUCIAZZZ4 天前
JVM之内存管理(一)
java·jvm·spring·操作系统·springboot
LUCIAZZZ5 天前
JVM之内存管理(二)
java·jvm·后端·spring·操作系统·springboot
天上掉下来个程小白6 天前
缓存套餐-01.Spring Cache入门案例
java·redis·spring·缓存·springboot·springcache
程序员buddha8 天前
SpringBoot+Dubbo+Zookeeper实现分布式系统步骤
分布式·zookeeper·dubbo·springboot
天上掉下来个程小白8 天前
缓存套餐-03.功能测试
redis·缓存·springboot·苍穹外卖·springcache
Ten peaches9 天前
苍穹外卖(订单状态定时处理、来单提醒和客户催单)
java·数据库·sql·springboot