欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

SpringBoot启动主流程-构造方法

时间:2023-07-09
版本说明

Spring Boot:【2.4.6】

Spring: 【5.3.7】

SpringApplication构造方法主流程

新建一个简单的springboot项目,只导入web模块。

从入口开始分析

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

先不用管注解@SpringBootApplication的事,点开run方法

# SpringApplication.javapublic static ConfigurableApplicationContext run(Class<?> primarySource, String..、args) { return run(new Class<?>[] { primarySource }, args);}

继续跟run方法

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

这里new了一个SpringApplication,并且调用该实例的run方法

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(); // 获取BootstrapRegistryInitializer并设置 this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); // 获取ApplicationContextInitializer并设置 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); // 获取ApplicationListener并设置 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 通过栈调用链决议主类 this.mainApplicationClass = deduceMainApplicationClass();}

上述方法里通过调用getSpringFactoriesInstances获取相应的工厂实例,这块是个比较重要的点。

private Collection getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object..、args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates // 第一次load,从spring.factories下加载工厂名字,放到cache中,之后从cache读取Set names = new linkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 通过反射实例化List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}

上面代码出现了Spring框架的一个比较重要的类SpringFactoriesLoader,负责从classpath下的meta-INF/spring.factories下加载和实例化指定类型的工厂。

General purpose factory loading mechanism for internal use within the framework.
SpringFactoriesLoader loads and instantiates factories of a given type from “meta-INF/spring.factories” files which may be present in multiple JAR files in the classpath、The spring.factories file must be in Properties format, where the key is the fully qualified name of the interface or abstract class, and the value is a comma-separated list of implementation class names、For example:
example.MyService=example.MyServiceImpl1,example.MyServiceImpl2
where example.MyService is the name of the interface, and MyServiceImpl1 and MyServiceImpl2 are two implementations.

SpringFactoriesLoader内部持有一个cache,只读取一次,多次load的时候直接从cache读取

static final Map>> cache = new ConcurrentReferenceHashMap<>();

关键方法如下,很简单,先从cache读,没有则从FACTORIES_RESOURCE_LOCATION加载资源,解析后放到cache中。

private static Map> loadSpringFactories(ClassLoader classLoader) { Map> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); 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(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements 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;}

End

构造方法内完成的事情

决议web应用类型获取BootstrapRegistryInitializer并设置获取ApplicationContextInitializer并设置获取ApplicationListener并设置通过栈调用链决议主类

其中比较关键的调用链路

下图是具体代码的执行流程

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。