SpringBoot源码阅读(1)SpringApplication的构造器

1.开始

我们开发一个SpringBoot项目,第一段代码一般是长这样的:

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

}

我们会调用SpringApplication的静态方法run。

然后我们点进run方法

	public 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);
	}

调用了SpringApplication的构造器创建了SpringApplication对象,然后执行实例方法run

我们今天来看SpringApplication的构造器。我们跟着debug一起走进去。

	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}

调用的是SpringApplication的双参构造器。接着就如SpringApplication的双参构造器。

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.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));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

再进入SpringApplication(ResourceLoader resourceLoader, Class<?>… primarySources)方法前。JVM会先创建SpringApplication的实例变量:

源文件

	private Set<String> sources = new LinkedHashSet<>();

Banner打印模式为CONSOLE(控制台)

	private Banner.Mode bannerMode = Banner.Mode.CONSOLE;

日志启动信息

	private boolean logStartupInfo = true;

是否添加命令行Propertie

	private boolean addCommandLineProperties = true;

是否需要添加ConversionService

	private boolean addConversionService = true;

headless

	private boolean headless = true;

注册的钩子,用来优雅关闭SpringBoot

	private boolean registerShutdownHook = true;

附加的Profile

	private Set<String> additionalProfiles = Collections.emptySet();

是否自定义的环境

	private boolean isCustomEnvironment = false;

懒加载

	private boolean lazyInitialization = false;

应用上下文工厂

	private ApplicationContextFactory applicationContextFactory = ApplicationContextFactory.DEFAULT;
	private ApplicationStartup applicationStartup = ApplicationStartup.DEFAULT;

目前,我们不需要知道他们都是干啥的。在debug的过程中自然就清楚了。

回到正题

2.SpringApplication的构造器

SpringApplication的构造器上。

	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        //1.设置资源加载器resourceLoader
		this.resourceLoader = resourceLoader;
        //2.校验primarySources是否为空
		Assert.notNull(primarySources, "PrimarySources must not be null");
        //3.设置主源(primarySources,一般为启动类)
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        //4.设置应用类型(取决于我们引入的starter的类型)
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
        //5.设置引导注册初始化器
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        //6.设置初始化器
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        //7.保存写了mian方法的类
		this.mainApplicationClass = deduceMainApplicationClass();
	}

2.1.设置资源加载器resourceLoader

我们参看resourceLoader的类型信息。

/**
 * 用于加载资源(例如,类路径或文件系统资源)的策略接口。
 提供此功能需要org.springframework.context.ApplicationContext以 及扩展的org.springfframework.core.io.support.ResourcePatternResolver支持。
DefaultResourceLoader是一个独立的实现,可在ApplicationContext之外使用,也可由ResourceEditor使用。
当在ApplicationContext中运行时,可以使用特定上下文的资源加载策略从Strings填充Resource和Resource[]类型的Bean
 */
public interface ResourceLoader {

	/** 从类路径“classpath:”加载的伪URL前缀。 */
	String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;


	/**
	 * 返回资源
	 */
	Resource getResource(String location);

	/**
	 * 获取资源加载器使用的ClassLoader(而不是获取线程的ClassLoader)
	 */
	@Nullable
	ClassLoader getClassLoader();

}

我们可以初步了解ResourceLoader是用来加载资源的。所有实现ResourceLoader接口的类都具有资源加载能力。

使用getResource获取资源

我们可以通过setResourceLoader指定我们期望的

在这里插入图片描述

也可以在构造器中传入ResourceLoader

在这里插入图片描述

2.2.校验primarySources是否为空

比较简单,next

2.3.设置SpringBoot启动类

就是有@SpringBootApplication注解的类

2.4.设置应用类型(取决于我们引入的starter的类型)

跟进去

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String className : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

我们来分析这行代码

1.如果存在org.springframework.web.reactive.DispatcherHandler类型并且不存在org.glassfish.jersey.servlet.ServletContainer类型,返回REACTIVE

2.如果javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext有一个不存在,NONE

3.返回SERVLET

我们来看一下

ClassUtils.isPresent方法

	public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
		try {
			forName(className, classLoader);
			return true;
		}
		catch (IllegalAccessError err) {
			throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" +
					className + "]: " + err.getMessage(), err);
		}
		catch (Throwable ex) {
			// Typically ClassNotFoundException or NoClassDefFoundError...
			return false;
		}
	}

在看一下forName方法

	public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
			throws ClassNotFoundException, LinkageError {

		Assert.notNull(name, "Name must not be null");

		Class<?> clazz = resolvePrimitiveClassName(name);
		if (clazz == null) {
			clazz = commonClassCache.get(name);
		}
		if (clazz != null) {
			return clazz;
		}

		// "java.lang.String[]" style arrays
		if (name.endsWith(ARRAY_SUFFIX)) {
			String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
			Class<?> elementClass = forName(elementClassName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[Ljava.lang.String;" style arrays
		if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
			String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		// "[[I" or "[[Ljava.lang.String;" style arrays
		if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
			String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
			Class<?> elementClass = forName(elementName, classLoader);
			return Array.newInstance(elementClass, 0).getClass();
		}

		ClassLoader clToUse = classLoader;
		if (clToUse == null) {
			clToUse = getDefaultClassLoader();
		}
		try {
			return Class.forName(name, false, clToUse);
		}
		catch (ClassNotFoundException ex) {
			int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
			if (lastDotIndex != -1) {
				String nestedClassName =
						name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
				try {
					return Class.forName(nestedClassName, false, clToUse);
				}
				catch (ClassNotFoundException ex2) {
					// Swallow - let original exception get through
				}
			}
			throw ex;
		}
	}

通过类型加载器去加载类,加载不到说明不存在这个类

2.5.设置引导注册初始化器

	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) {
        //1.获取classLoader
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
        //2.获取META-INF/spring.factories文件下父类为type的类名名称
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		//3.创建实例
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		//4.排序
        AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}
	//通过反射创建对象
	@SuppressWarnings("unchecked")
	private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
			ClassLoader classLoader, Object[] args, Set<String> names) {
		List<T> instances = new ArrayList<>(names.size());
		for (String name : names) {
			try {
				Class<?> instanceClass = ClassUtils.forName(name, classLoader);
				Assert.isAssignable(type, instanceClass);
				Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
				T instance = (T) BeanUtils.instantiateClass(constructor, args);
				instances.add(instance);
			}
			catch (Throwable ex) {
				throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
			}
		}
		return instances;
	}

2.5.加载ApplicationContextInitializer接口子类

逻辑同4,设置initializers

加载到的对象
在这里插入图片描述
ApplicationContextInitializer接口

/**

 用于在刷新之前初始化Spring ConfigurationApplicationContext的回调接口。

通常在需要对应用程序上下文进行一些编程初始化的web应用程序中使用。例如,针对上下文的环境注册属性源或激活配置文件。请参阅ContextLoader和FrameworkServlet对分别声明“contextInitializerClasses”上下文参数和init参数的支持。

鼓励ApplicationContextInitializer处理器在调用之前检测Spring的Ordered接口是否已实现或@Order注释是否存在,并对实例进行相应排序。
 */
@FunctionalInterface
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {

	/**
	 * 初始化应用上下文
	 * @param applicationContext the application to configure
	 */
	void initialize(C applicationContext);

}

2.6.加载ApplicationListener接口

逻辑同4,设置listeners
在这里插入图片描述
ApplicationListener接口

/**
 * 要由应用程序事件侦听器实现的接口。

基于Observer设计模式的标准java.util.EventListener接口。

从Spring 3.0开始,ApplicationListener可以通用地声明它感兴趣的事件类型。当向Spring ApplicationContext注册时,事件将被相应地过滤,侦听器只为匹配的事件对象调用。
 *
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * 处理应用事件
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);


	/**
	 * 创建一个新的监听器
	 */
	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
		return event -> consumer.accept(event.getPayload());
	}

}

7.记录mainApplicationClass

private Class<?> deduceMainApplicationClass() {
	try {
		StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			if ("main".equals(stackTraceElement.getMethodName())) {
				return Class.forName(stackTraceElement.getClassName());
			}
		}
	}
	catch (ClassNotFoundException ex) {
		// Swallow and continue
	}
	return null;
}

根据方法调用的栈,遍历查找编写mian方法的类(启动类)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值