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方法的类(启动类)