当前位置: 首页 > article >正文

SpringBoot 启动类 SpringApplication 一 构造器方法

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; //没有传入resourceLoader
	Assert.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)); # 初始化BootstrapRegistry
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContext
	setListeners((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,则为响应式web
			return 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的value
				String[] 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变成不可变list
		result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
	}
	// 表示将result整个map变成不可变map
	return 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)); # 初始化BootstrapRegistry
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContext
	setListeners((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`就会被执行。
    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("事件: " + event);
    }

    @Override
    public 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.kler.cn/a/445377.html

相关文章:

  • springboot453工资信息管理系统(论文+源码)_kaic
  • RestTemplate实时接收Chunked编码传输的HTTP Response
  • C语言进阶(2) ---- 指针的进阶
  • 全面解析 Golang Gin 框架
  • 学习反射(反射的使用,反射的应用场景)
  • python飞机大战游戏.py
  • ESP32物联网开发
  • ChatGPT等大语言模型与水文水资源、水环境领域的深度融合
  • Spring(一)---IOC(控制权反转)
  • Vue.js前端框架教程10:Vue生命周期钩子onBeforeMount 和onMounted
  • ctf文件包含
  • [创业之路-200]:什么是business(业务)?B2B, B2C, B2G业务, 什么是业务设计?
  • 任务一登录安全加固
  • 前端TypeScript学习day03-TS高级类型
  • 任务2 配置防火墙firewalld
  • MySQL——EXPLAIN
  • 深入理解 C++ 中 std::vector 和 std::set 容器的使用
  • 机器学习中做时间序列模型考虑把时间作为特征分析
  • Java 后端给前端返回的long精度缺失,导致数据不一致
  • 京东大数据治理探索与实践 | 京东零售技术实践
  • 构建全方位大健康零售帮助中心:提升服务与体验
  • ES6学习Set 、Map(六)
  • 新能源汽车产销数据分析
  • MYSQL_联合查询(全)
  • MacOS安装MySQL
  • 监控IP频繁登录服务器脚本