SpringBoot 启动类 SpringApplication 一 构造器方法

ops/2024/12/22 11:00:22/

SpringApplication_0">SpringApplication

@SpringBootApplication
@ComponentScan(basePackages = { "com.example.*" })
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args); // Spirngboot程序入口}
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 首先用主类(DemoApplication.class)为参数调用构造器方法,之后执行run()方法return new SpringApplication(primarySources).run(args);
}

构造器方法

第一个参数ResourceLoader是资源加载器,它定义获取资源的策略。它加载各种来源的资源,比如文件资源(file:/var/local/a.txt),类路径资源(classpath: application.yml),URL资源(URL:https://www.baidu.com),并且统一封装为Resource对象。
Resource对象是对各种资源的抽象,调用Resource对象不用考虑底层实现。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader; //没有传入resourceLoaderAssert.notNull(primarySources, "PrimarySources must not be null"); // 参数校验this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath(); # 判断服务类型this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); # 初始化BootstrapRegistrysetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContextsetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); # this.mainApplicationClass = deduceMainApplicationClass();
}

判断服务类型

WebApplicationType 是枚举类,有3种类型

  1. NONE表示非web应用,不需要web服务器
  2. SERVLET表示基于SERVLET的WEB应用,需要SERVLET服务器
  3. REACTIVE表示响应式WEB应用,需要响应式web服务器

spring-mvc默认使用的Tomcat属于第二种,因此springApplication.webApplicationType = SERVLET,后续在refresh()方法里会启动Tomcat
ClassUtils.isPresent调用Class.forName()方法查询类路径上是否存在某个类。

public enum WebApplicationType {NONE,SERVLET,REACTIVE;private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext" };private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";static WebApplicationType deduceFromClasspath() {if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {// 如果JVM无法加载WEBMVC_INDICATOR_CLASS类和JERSEY_INDICATOR_CLASS// 但是JVM可以加载WEBFLUX_INDICATOR_CLASS,则为响应式webreturn WebApplicationType.REACTIVE;}// 只要没有SERVLET_INDICATOR_CLASSES 中任意一个类,都属于非WEB服务for (String className : SERVLET_INDICATOR_CLASSES) {if (!ClassUtils.isPresent(className, null)) {return WebApplicationType.NONE;}}// 同时包含SERVLET_INDICATOR_CLASSES的所有类,属于SERVELT web服务return WebApplicationType.SERVLET;}
}

getSpringFactoriesInstances

private <T> List<T> getSpringFactoriesInstances(Class<T> type) {return getSpringFactoriesInstances(type, null);
}
private <T> List<T> getSpringFactoriesInstances(Class<T> type, ArgumentResolver argumentResolver) {return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
}
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}

getSpringFactoriesInstances方法分2步.第一步加载工厂类SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()),DefaultResourceLocation就是"META-INF/spring.factories".第二步实例化工厂类load(type, argumentResolver)

加载工厂类

public class SpringFactoriesLoader {public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";static final Map<ClassLoader, Map<String, SpringFactoriesLoader>> cache = new ConcurrentReferenceHashMap<>();// 加载"META-INF/spring.factories"工厂类的类加载器private final ClassLoader classLoader;private final Map<String, List<String>> factories;// 默认加载"META-INF/spring.factories"中的类public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);}
}

SpringFactoriesLoader spring.factories工厂类文件的加载器。forDefaultResourceLocation静态方法表示默认从每个jar包的"META-INF/spring.factories"文件加载类。
forResourceLocation静态方法实现加载工厂类并且保存在SpringFactoriesLoader类对象的静态变量cache中,每次调用getSpringFactoriesInstances方法就不用重复加载"META-INF/spring.factories"

public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");// 获得类加载器,用于加载类,是AppClassLoader系统类加载器ClassLoader resourceClassLoader = (classLoader != null ? classLoader :SpringFactoriesLoader.class.getClassLoader());// loaders是当前类加载器所加载的资源键值对,key是资源名称,是`META-INF/spring.factories`,value是SpringFactoriesLoader对象// cache是类的静态变量,全局唯一Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());return loaders.computeIfAbsent(resourceLocation, key ->new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}

loadFactoriesResource实现用AppClassLoader加载"META-INF/spring.factories"。结合下图分析源码。

protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {Map<String, List<String>> result = new LinkedHashMap<>();try {// Enumeration不是枚举类,是迭代器类Enumeration<URL> urls = classLoader.getResources(resourceLocation);while (urls.hasMoreElements()) {// resource是`META-INF/spring.factories`文件的路径UrlResource resource = new UrlResource(urls.nextElement());// Properties类定义:`class Properties extends Hashtable<Object,Object>`// Properties类是线程安全的属性类,用键值对表示属性Properties properties = PropertiesLoaderUtils.loadProperties(resource);properties.forEach((name, value) -> {// 将键值对的值用逗号分隔开,并且将其放入result的valueString[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);List<String> implementations = result.computeIfAbsent(((String) name).trim(),key -> new ArrayList<>(factoryImplementationNames.length));Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);});}// 表示将result的value变成不可变listresult.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);}// 表示将result整个map变成不可变mapreturn Collections.unmodifiableMap(result);
}

在这里插入图片描述
图中的spirng.factories是本项目自定义的。Springboot会加载所有包的spring.factories。比如:spring-boot-autoconfigure-3.3.5.jar!/META-INF/spring.factories, spring-data-redis-3.3.5.jar!/META-INF/spring.factories

实例化工厂类

SpringApplication构造器方法setInitializers方法的参数ApplicationContextInitializer.class用于实例化该接口的实现类,即factoryType
load方法首先根据factoryType参数获取实现类的全限定类名,之后实例化。

public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {return load(factoryType, argumentResolver, null);
}
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,@Nullable FailureHandler failureHandler) {Assert.notNull(factoryType, "'factoryType' must not be null");// loadFactoryNames方法就是获取cache中保存的`factoryType`的实现类全限定类名List<String> implementationNames = loadFactoryNames(factoryType);logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));List<T> result = new ArrayList<>(implementationNames.size());FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;for (String implementationName : implementationNames) {// 实例化对象T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);if (factory != null) {result.add(factory);}}AnnotationAwareOrderComparator.sort(result);return result;
}
protected <T> T instantiateFactory(String implementationName, Class<T> type,@Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {try {// 在JVM中加载implementationName实现类Class<?> factoryImplementationClass = ClassUtils.forName(implementationName, this.classLoader);// 校验实现类是否是type的子类Assert.isTrue(type.isAssignableFrom(factoryImplementationClass), () ->"Class [%s] is not assignable to factory type [%s]".formatted(implementationName, type.getName()));// FactoryInstantiator<T>获得`factoryImplementationClass`类的构造器对象`Constructor<T>`FactoryInstantiator<T> factoryInstantiator = FactoryInstantiator.forClass(factoryImplementationClass);// 调用构造器对象的`constructor.newInstance(args)`方法生成对象。return factoryInstantiator.instantiate(argumentResolver);}catch (Throwable ex) {failureHandler.handleFailure(type, implementationName, ex);return null;}}

初始化

	this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); # 初始化BootstrapRegistrysetInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContextsetListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

上下文

this.bootstrapRegistryInitializers加载META-INF/spring.factoriesBootstrapRegistryInitializer.class的实现类,用于初始化DefaultBootstrapContext上下文对象。而setInitializers加载ApplicationContextInitializer.class的实现类,用于初始化ApplicationContext上下文对象。
这两个上下文,前者是引导上下文,在IOC容器启动之前保存预先加载的类,IOC容器启动之后会销毁引导上下文。一般springboot项目用不到引导上下文springcloud中有用到。
后者就是IOC容器。SpringBoot默认加载图中7个初始化器。
在这里插入图片描述

监听器

setListeners方法加载ApplicationListener.class的实现类,用于监听启动SpringApplication定义的事件。
具体流程是,自定义SpringApplicationRunListener 实现类,定义监听的事件。

public class MyListener implements ApplicationListener<ApplicationEvent> {// 当出现事件(`ApplicationEvent`的实现类),`onApplicationEvent`就会被执行。@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("事件: " + event);}@Overridepublic boolean supportsAsyncExecution() {return ApplicationListener.super.supportsAsyncExecution();}
}

META-INF/spring.factories加一org.springframework.context.ApplicationListener=com.example.demo.MyListener
启动项目,加载的实现类中就包含自定义的实现类。
在这里插入图片描述

deduceMainApplicationClass

推导主类。源码表示:在(栈帧StackWalker对象)中找到第一个名称为main的方法,该方法所在的类就是主类。

private Class<?> deduceMainApplicationClass() {return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk(this::findMainClass).orElse(null);
}
private Optional<Class<?>> findMainClass(Stream<StackFrame> stack) {return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main")).findFirst().map(StackWalker.StackFrame::getDeclaringClass);
}

http://www.ppmy.cn/ops/144001.html

相关文章

计算机毕业设计hadoop+spark视频推荐系统 短视频推荐系统 视频流量预测系统 短视频爬虫 视频数据分析 视频可视化 视频大数据 大数据

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

《第十二部分》1.STM32之RTC实时时钟介绍---BKP实验

本章将介绍一种计数计时的外设 RTC实时时钟-----Whappy STM32提供了4中时钟来源&#xff01; 函数名功能作用void BKP_DeInit(void);复位备份区域寄存器配置&#xff0c;将备份域的所有寄存器恢复到默认状态。void BKP_TamperPinLevelConfig(uint16_t BKP_TamperPinLevel);配置…

ubuntu批量依赖库拷贝(ldd)

背景 如何将程序依赖的动态库拷贝到指定的目录&#xff1f; 例子 通过LDD查看依赖的动态库。 $ ldd extract_gpulinux-vdso.so.1 (0x00007ffd931e4000)libopencv_cudacodec.so.4.1 > /home/joyner/disk1/mmaction/third_party/opencv-4.1.0/build/lib/libopencv_cudacod…

可编辑46PPT | AI+智能中台企业架构设计_重新定义制造

荐言分享&#xff1a;随着信息技术的飞速发展&#xff0c;人工智能&#xff08;AI&#xff09;逐渐成为推动企业数字化转型和提升竞争力的关键力量。AI中台作为一种新兴的技术框架&#xff0c;通过整合不同的AI技术和资源&#xff0c;实现了AI能力的快速研发、共享复用与灵活部…

数据可视化-2. 条形图

目录 1. 条形图适用场景分析 1.1 比较不同类别的数据 1.2 展示数据分布 1.3 强调特定数据点 1.4 展示时间序列数据的对比 1.5 数据可视化教育 1.6 特定领域的应用 2. 条形图局限性 3. 条形图图代码实现 3.1 Python 源代码 3.2 条形图效果&#xff08;网页显示&#…

ECharts柱状图-柱图42,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

解决vscode ssh远程连接服务器一直卡在下载 vscode server问题

目录 方法1&#xff1a;使用科学上网 方法2&#xff1a;手动下载 方法3 在使用vscode使用ssh远程连接服务器时&#xff0c;一直卡在下载"vscode 服务器"阶段&#xff0c;但MobaXterm可以正常连接服务器&#xff0c;大概率是网络问题&#xff0c;解决方法如下: 方…

【开源免费】基于Vue和SpringBoot的靓车汽车销售网站(附论文)

本文项目编号 T 093 &#xff0c;文末自助获取源码 \color{red}{T093&#xff0c;文末自助获取源码} T093&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…