1.SpringApplication.run(DemoApplication.class, args);
DemoApplication.java
调用了SpringApplication类中的静态方法,DemoApplication.class,args为环境变量,详情参考底层
2.public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{primarySource}, args);
}
SpringApplication.class
进入后将primarySource---DemoApplication.class,转换为一个Class反射数组,运行了方法签名为 run(Class<?>[] primarySources, String[] args)的静态方法
3.public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
SpringApplication.class
new SpringApplication(primarySources)运行了这个SpringApplication.class的构造方法,让我们继续看看SpringApplication(primarySources)方法签名做了什么:
public SpringApplication(Class<?>... primarySources) { this((ResourceLoader)null, primarySources);}
调用了另一个构造方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = Collections.emptySet(); this.isCustomEnvironment = false; this.lazyInitialization = false; this.applicationContextFactory = ApplicationContextFactory.DEFAULT; this.applicationStartup = ApplicationStartup.DEFAULT; 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(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass();}
这个构造方法很长,并且调用了很多现存方法,
public interface Banner {
void printBanner(Environment environment, Class<?> sourceClass, PrintStream out);
public static enum Mode {
OFF,
CONSOLE,
LOG;
private Mode() {
}
}
}
Mode.CONSOLE在接口里面声明了一个静态枚举类,并且使用private Mode()防止实例化,这个写法很有趣。在接口定义了内部静态类
ApplicationContextFactory.DEFAULT;@FunctionalInterface
public interface ApplicationContextFactory {
ApplicationContextFactory DEFAULT = new DefaultApplicationContextFactory();
这个写法也很特别,在函数式接口中定义了一个DEFAULT 变量,调用直接返回DefaultApplicationContextFactory();构造。
class DefaultApplicationContextFactory implements ApplicationContextFactory {
DefaultApplicationContextFactory() {
}
可以发现这个DefaultApplicationContextFactory构造实现了ApplicationContextFactory 接口。这个接口返回了实现他的DefaultApplicationContextFactory。所以接口可以代表无数个自己的实现类,毕竟都算是一个类型。
private ApplicationContextFactory applicationContextFactory;
可见ApplicationContextFactory 变量的类型,但是显然返回的是DefaultApplicationContextFactory
Assert.notNull(primarySources, "PrimarySources must not be null");
Assert来自于springboot框架,如果不符合条件就会报错。报错的类型尚且未知。
WebApplicationType.deduceFromClasspath();
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
ClassUtils.isPresent这个方法会检查未知的包中是否存在if中的路径类,假如符合第一个条件就会返回此枚举类中REACTIVE这个枚举类中不仅有静态方法还有一个静态类,并且静态类中有属于静态类的实例的实例成员方法?
这个方法一般会返回一个SERVLET。
private <T> List<T> getSpringFactoriesInstances(Class<T> type) {
return
this.getSpringFactoriesInstances(type, (SpringFactoriesLoader.ArgumentResolver)null);
}
(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
private <T> List<T> getSpringFactoriesInstances(Class<T> type, SpringFactoriesLoader.ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(this.getClassLoader()).load(type, argumentResolver);
}
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation("META-INF/spring.factories", classLoader);
}
public ClassLoader getClassLoader() { return this.resourceLoader != null ? this.resourceLoader.getClassLoader() : ClassUtils.getDefaultClassLoader();}
因为上文中的resourceLoader 必定为null ,所以调用了ClassUtils.getDefaultClassLoader();
@Nullable
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (Throwable var3) {
}
if (cl == null) {
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getSystemClassLoader();
} catch (Throwable var2) {
}
}
}
return cl;
}
关于1:
`Thread.currentThread().getContextClassLoader()` 是用于获取当前线程的上下文类加载器(Context ClassLoader)的方法。
在Java应用程序中,每个线程都有一个关联的上下文类加载器,它用于加载类和资源。上下文类加载器通常用于解决类加载问题,尤其是在多线程和模块化应用程序中。
以下是关于这段代码的解释:
1. `Thread.currentThread()`:这是一个静态方法,用于获取当前正在执行的线程的引用。
2. `getContextClassLoader()`:这是`Thread`类的一个方法,用于获取当前线程的上下文类加载器。上下文类加载器是一个`ClassLoader`对象,负责加载当前线程中所需的类和资源。
在多线程应用程序中,线程的上下文类加载器可以被设置为不同的`ClassLoader`,以满足不同的类加载需求。这在某些情况下非常有用,例如在应用程序中有多个模块或插件,每个模块使用不同的类加载器加载自己的类。通过设置上下文类加载器,可以确保每个模块使用正确的类加载器加载自己的类,避免类加载冲突。
总之,`Thread.currentThread().getContextClassLoader()` 用于获取当前线程的上下文类加载器,可以用于解决复杂的类加载问题,确保正确加载所需的类和资源。
涉及类加载器的内容很深,有机会再来研究。
显然这个方法使用Thread.currentThread().getContextClassLoader()之后,jvm提供了一个
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
ClassLoader resourceClassLoader = classLoader != null ? classLoader : SpringFactoriesLoader.class.getClassLoader();
Map<String, SpringFactoriesLoader> loaders = (Map)cache.computeIfAbsent(resourceClassLoader, (key) -> {
return new ConcurrentReferenceHashMap();
});
return (SpringFactoriesLoader)loaders.computeIfAbsent(resourceLocation, (key) -> {
return new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation));
});
}
刚才获取的类加载器被传入了这个方法。
static final Map<ClassLoader, Map<String, SpringFactoriesLoader>> cache = new ConcurrentReferenceHashMap();
`computeIfAbsent` 是Java中`java.util.Map`接口提供的一个方法,用于在映射中根据指定的键计算一个值并将其插入到映射中,如果映射中不存在该键或与该键关联的值为 `null`,则进行计算和插入。
方法签名如下:
```java
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
```
其中:
- `key` 是要计算值的键。
- `mappingFunction` 是一个函数,用于根据键计算一个新值。它接受键作为输入并返回与键关联的新值。
`computeIfAbsent` 方法的工作方式如下:
1. 如果映射包含指定键 `key` 并且与该键关联的值不为 `null`,则不会执行计算,方法直接返回关联的值。
2. 如果映射不包含指定键 `key` 或与该键关联的值为 `null`,则会执行 `mappingFunction` 函数来计算一个新的值,然后将新值与键关联,并将新值返回。
这个方法在需要根据键动态计算值并将其存储在映射中的场景中非常有用。通常用于避免显式的 `null` 值检查和插入操作。
以下是一个示例:
import java.util.HashMap;
import java.util.Map;
public class ComputeIfAbsentExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
使用computeIfAbsent计算并插入值
map.computeIfAbsent("one", key -> 1);
map.computeIfAbsent("two", key -> 2);
map.computeIfAbsent("three", key -> 3);
输出: {one=1, two=2, three=3}
如果键已存在,computeIfAbsent不会覆盖现有值
map.computeIfAbsent("one", key -> 10);
输出: {one=1, two=2, three=3}
}
}
在上述示例中,`computeIfAbsent` 方法用于计算并插入键值对,如果键已经存在则不进行插入。这可以确保在映射中只有当键不存在或值为 `null` 时才会计算和插入新值。
`ConcurrentReferenceHashMap` 是 Spring Framework 中的一个类,用于实现一个线程安全的、基于弱引用(Weak Reference)的哈希映射(Hash Map)。它是 Spring 框架中的一部分,位于 `org.springframework.util.concurrent` 包中。
`ConcurrentReferenceHashMap` 主要用于以下场景:
1. **缓存管理**:它可以用作缓存的数据结构,允许使用弱引用来管理缓存中的对象。当对象的强引用被移除或变为不可达时,相关的缓存项也会被自动清理,这有助于防止内存泄漏。
2. **高并发访问**:它是线程安全的,支持多线程并发访问,因此可以安全地用于多线程环境中。
3. **特定需求的哈希映射**:它提供了一种可以根据需要配置的哈希映射,可以设置不同的引用类型(如弱引用、软引用等)以及其他选项来满足特定的应用需求。
`ConcurrentReferenceHashMap` 的使用方式与普通的哈希映射类似,但它具有一些额外的功能和配置选项,如引用类型、初始容量、并发级别等。通常,您可以使用它来创建一个线程安全的缓存,例如:
```java
ConcurrentReferenceHashMap<String, Integer> cache = new ConcurrentReferenceHashMap<>(
弱引用
软引用也是可选的
初始容量
并发级别
是否支持并发
);
// 将键值对放入缓存
cache.put("key1", 123);
cache.put("key2", 456);
// 从缓存中获取值
Integer value = cache.get("key1");
```
在上述示例中,我们创建了一个 `ConcurrentReferenceHashMap` 实例,使用弱引用作为引用类型,并设置了初始容量、并发级别等选项。然后,我们可以将键值对放入缓存并从缓存中获取值。
请注意,`ConcurrentReferenceHashMap` 是 Spring 框架的一部分,如果您使用 Spring 框架,可以方便地集成和使用它。在其他情况下,您也可以选择使用 Java 标准库中的其他 Map 实现来满足您的需求。
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
Map<String, List<String>> result = new LinkedHashMap();
try {
Enumeration<URL> urls = classLoader.getResources(resourceLocation);
while(urls.hasMoreElements()) {
UrlResource resource = new UrlResource((URL)urls.nextElement());
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
properties.forEach((name, value) -> {
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)value);
List<String> implementations = (List)result.computeIfAbsent(((String)name).trim(), (key) -> {
return new ArrayList(factoryImplementationNames.length);
});
Stream var10000 = Arrays.stream(factoryImplementationNames).map(String::trim);
Objects.requireNonNull(implementations);
var10000.forEach(implementations::add);
});
}
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
return Collections.unmodifiableMap(result);
} catch (IOException var6) {
throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", var6);
}
}
该方法有些复杂,我们看看loadFactoriesResource拿着ClassLoader 和resourceLocation干了什么。
Enumeration<URL> urls = classLoader.getResources(resourceLocation);
代码片段是用于获取给定资源路径 `META-INF/spring.factories` 在指定的 `ClassLoader` 中的所有 URL 资源的枚举(Enumeration)。
具体解析如下:
1. `classLoader` 是一个 `ClassLoader` 对象,通常代表一个类加载器,用于加载类和资源。
2. `resourceLocation` 是一个字符串,表示要查找的资源的路径。在这个例子中,资源路径是 `META-INF/spring.factories`,这通常用于Spring框架中的自动配置。
3. `classLoader.getResources(resourceLocation)` 是通过类加载器 `classLoader` 来获取资源路径 `resourceLocation` 下的所有资源。这个方法返回一个 `Enumeration<URL>` 对象,其中包含了找到的所有匹配资源的URL。
4. 枚举(Enumeration)是一种迭代器,它允许您逐个访问集合中的元素。在这个上下文中,`Enumeration<URL>` 将允许您逐个访问 `META-INF/spring.factories` 下的所有 URL 资源。
通常,此类代码在框架或库中用于发现和加载特定类型的配置文件或资源。在Spring框架中,`META-INF/spring.factories` 文件通常用于自动配置,其中包含了各种Spring组件的配置信息。通过类加载器获取资源,可以在应用程序启动时动态加载这些配置信息。一旦您有了 `Enumeration<URL>`,您可以迭代它并访问每个资源的URL,然后进行相应的处理。
PropertiesLoaderUtils.loadProperties(resource);
public static Properties loadProperties(Resource resource) throws IOException {
Properties props = new Properties();
fillProperties(props, resource);
return props;
}
public static void fillProperties(Properties props, Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
String filename = resource.getFilename();
if (filename != null && filename.endsWith(".xml")) {
props.loadFromXML(is);
} else {
props.load(is);
}
} catch (Throwable var6) {
if (is != null) {
try {
is.close();
} catch (Throwable var5) {
var6.addSuppressed(var5);
}
}
throw var6;
}
if (is != null) {
is.close();
}
}
使用类加载器classLoader自带的方法getResources。从当前文件的类加载的上下文中获取此resourceLocation的文件。详见loadProperties。已经通过 while(urls.hasMoreElements()) {
从类加载已获取的枚举循渐器中获取了Resource 。详见loadProperties获取了resourceLocation的文件内容,java自带的Properties 很好的解析了文件内容,并返回Properties类。
一一对应了。可以推测出Properties会推断分析以及提取文件属性并按Properties文件的格式来存储为对象。
可以研究一下代码明细,很巧妙的使用computeIFABSENT函数来填充以及转换了props对象,其中的数组成员操作也巧妙使用了Stream流。
之前说过result使用了Map
Map<String, List<String>> result = new LinkedHashMap();
可能有许多加工,但最终返回此result。
注意result的类型。
抽栈,回到上方一个lambda返回方法,此处调用了SpringFactoriesLoader的构造方法。
他的第二个参数就是上一个方法块的返回值。
protected SpringFactoriesLoader(@Nullable ClassLoader classLoader, Map<String, List<String>> factories) {
this.classLoader = classLoader;
this.factories = factories;
}
抽栈。
到 public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {
return this.load(factoryType, argumentResolver, (FailureHandler)null);
}
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver, @Nullable FailureHandler failureHandler) {
Assert.notNull(factoryType, "'factoryType' must not be null");
List<String> implementationNames = this.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;
Iterator var7 = implementationNames.iterator();
while(var7.hasNext()) {
String implementationName = (String)var7.next();
T factory = this.instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
if (factory != null) {
result.add(factory);
}
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
这个泛型的对象方法仔细看看。
注意一开始的主静态类给的第一个Class类型是BootstrapRegistryInitializer,
可以发现THROWING_FAILURE_HANDLER调用了此函数式接口的一个静态方法,此静态方法调用了其他的静态方法,很绕,来分析一下这个方法都做了什么。
@FunctionalInterface
public interface FailureHandler {
void handleFailure(Class<?> factoryType, String factoryImplementationName, Throwable failure);
static FailureHandler throwing() {
return throwing(IllegalArgumentException::new);
}
static FailureHandler throwing(BiFunction<String, Throwable, ? extends RuntimeException> exceptionFactory) {
return handleMessage((messageSupplier, failure) -> {
throw (RuntimeException)exceptionFactory.apply((String)messageSupplier.get(), failure);
});
}
static FailureHandler logging(Log logger) {
return handleMessage((messageSupplier, failure) -> {
logger.trace(LogMessage.of(messageSupplier), failure);
});
}
static FailureHandler handleMessage(BiConsumer<Supplier<String>, Throwable> messageHandler) {
return (factoryType, factoryImplementationName, failure) -> {
Supplier<String> messageSupplier = () -> {
return "Unable to instantiate factory class [%s] for factory type [%s]".formatted(factoryImplementationName, factoryType.getName());
};
messageHandler.accept(messageSupplier, failure);
};
}
}
throwing(IllegalArgumentException::new);
调用了throwing(BiFunction<String, Throwable, ? extends RuntimeException> exceptionFactory)。所以得知exceptionFactory的实质是一个非法语句异常的构造方法,进入此方法之后立马调用了handleMessage,handleMessage的参数只有一个函数式接口,接收一个匿名方法,直接看handleMessage,handleMessage接收1个参数,和此函数式接口签名一致,所以直接返回,也就是说,handleMessage接收 1个匿名方法对象,一开始的throwing是一个构造匿名函数的过程,
handleMessage中对匿名方法参数进行了使用,在最后使用了参数中的匿名方法,此匿名方法使用了一开始throwing的函数式接口参数中的方法,所以他的本质应该是
(factoryType, factoryImplementationName, failure) -> {
Supplier<String> messageSupplier = () -> {
return "Unable to instantiate factory class [%s] for factory type [%s]".formatted(factoryImplementationName, factoryType.getName());
};
messageHandler.accept(messageSupplier, failure);
此方法调用了这个匿名函数
(messageSupplier, failure) -> {
throw (RuntimeException)exceptionFactory.apply((String)messageSupplier.get(), failure);
});
在此匿名函数中调用了
IllegalArgumentException::new
};
}
切记,一开始的语句中在retrun调用了方法,所以每一句话的结尾都会把返回值的方法参数出栈,放入下一个语句进行包装。在一开始的时候throwing把new方法传给了throwing(匿名方法),此方法中对匿名方法进行了新的包装,返回了一个新的匿名方法,此匿名方法的值被传给了handleMessage,此方法对刚刚传入的对象进行了使用,然后依次出栈,最后得到的对象是一个已被初始化包装后的函数式接口对象,也就是此接口本身,但是其中已经有了刚刚包装好的一个匿名方法。
handleMessage的实质已经在上文中有了描述,它和接口的函数式接口抽象方法的签名一致,所以它符合此类型。而且它本身已经拥有了内容。
先从外向内,当handleMessage包装好方法的内容之后,再从内往外出栈,故最后调用了 public IllegalArgumentException(String message, Throwable cause) {
super(message, cause);
}为实质,以上的类型与参数由编译器推断,不做过多阐述。
逐个出栈,自此 this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
执行完全,因为不存在(spring工厂配置文件中spring.factories)BootstrapRegistryInitializer,所以此bootstrapRegistryInitializers 的值永远为0.
@Nullable
protected <T> T instantiateFactory(String implementationName, Class<T> type, @Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {
try {
Class<?> factoryImplementationClass = ClassUtils.forName(implementationName, this.classLoader);
Assert.isTrue(type.isAssignableFrom(factoryImplementationClass), () -> {
return "Class [%s] is not assignable to factory type [%s]".formatted(implementationName, type.getName());
});
FactoryInstantiator<T> factoryInstantiator = SpringFactoriesLoader.FactoryInstantiator.forClass(factoryImplementationClass);
return factoryInstantiator.instantiate(argumentResolver);
} catch (Throwable var7) {
failureHandler.handleFailure(type, implementationName, var7);
return null;
}
}
当工厂文件中配置了需要构造的类,会调用此方法。
`isAssignableFrom` 是Java中的一个方法,通常用于检查一个类是否可以从另一个类进行赋值或转换。它是Class类的一个方法,可以用于比较两个类之间的继承或赋值关系。
具体来说,`isAssignableFrom` 方法有两种使用方式:
1. 使用类对象的方法:
```
boolean isAssignableFrom(Class<?> cls)
```
这个方法会检查调用它的类是否可以从传入的`cls`参数表示的类进行赋值。如果可以,返回`true`;否则,返回`false`。这通常用于检查类之间的继承关系。
例如:
```java
Class<?> class1 = String.class;
Class<?> class2 = Object.class;
boolean result = class1.isAssignableFrom(class2);
输出 true,因为 String 是 Object 的子类
```
2. 使用类对象的静态方法:
```
static boolean isAssignableFrom(Class<?> cls1, Class<?> cls2)
```
这个方法与前面的方法类似,但不需要调用特定的类对象。它用于比较两个类是否存在继承或赋值关系。
例如:
```java
Class<?> class1 = String.class;
Class<?> class2 = Object.class;
boolean result = Class.isAssignableFrom(class1, class2);
输出 true,因为 String 是 Object 的子类
```
`isAssignableFrom` 主要用于在运行时进行类的类型检查和操作,例如在反射中,以确定一个类是否可以赋值给另一个类,或者是否可以使用某个类来实例化对象等。
以上再一次检查了反射类是否为同族。
SpringFactoriesLoader.FactoryInstantiator.forClass
首先调用了findConstructor,
@Nullable
private static Constructor<?> findConstructor(Class<?> factoryImplementationClass) {
Constructor<?> constructor = findPrimaryKotlinConstructor(factoryImplementationClass);
constructor = constructor != null ? constructor : findSingleConstructor(factoryImplementationClass.getConstructors());
constructor = constructor != null ? constructor : findSingleConstructor(factoryImplementationClass.getDeclaredConstructors());
constructor = constructor != null ? constructor : findDeclaredConstructor(factoryImplementationClass);
return constructor;
}
很漂亮的三元写法,依次判断为空就依次补上调用。非常漂亮,没见过用的这么好的三元。
所以此方法很显然返回了一个反射构造器。
最后调用了此工厂中的return factoryInstantiator.instantiate(argumentResolver);
返回了一个无参构造后的对象。
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
3条语句同理。
最后一句话
this.mainApplicationClass = this.deduceMainApplicationClass();
}
private Class<?> deduceMainApplicationClass() {
return (Class)((Optional)StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).walk(this::findMainClass)).orElse((Object)null);
}
这段代码是一个私有方法 `deduceMainApplicationClass()`,它用于推断应用程序的主类(Main Class)。这通常在Spring Boot等框架中用于确定应用程序的入口点。
让我来解释代码的各个部分:
1. `StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)` - 这部分代码使用 `StackWalker` 类的 `getInstance` 方法来创建一个 `StackWalker` 对象,该对象用于遍历堆栈帧。`Option.RETAIN_CLASS_REFERENCE` 参数用于指示 `StackWalker` 保留类引用,以便在遍历堆栈时能够访问类信息。
2. `.walk(this::findMainClass)` - 这是一个方法链调用,它调用 `StackWalker` 对象的 `walk` 方法,并传递了一个方法引用 `this::findMainClass` 作为参数。这表示要在堆栈上执行 `findMainClass` 方法,以查找主类。
3. `(Optional)` - `walk` 方法返回一个 `Stream`,该 `Stream` 包含与堆栈帧相关的元素。将其包装在 `Optional` 中,以便在没有找到主类的情况下返回空值。
4. `.orElse((Object)null)` - 如果 `walk` 方法找不到主类,则返回一个空值 `null`。
总之,这段代码的目的是使用 `StackWalker` 遍历堆栈帧,并尝试查找应用程序的主类。如果找到主类,则返回它的 `Class` 对象;如果没有找到主类,则返回 `null`。这通常在应用程序启动时用于确定哪个类包含 `main` 方法或类似的入口点,以便启动应用程序。
成功通过javaAPI获取到主程序入口类,目前SpringApplication构造完成,使用构造后的实例运行run方法。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
static Startup create() {
ClassLoader classLoader = Startup.class.getClassLoader();
return (Startup)(ClassUtils.isPresent("jdk.crac.management.CRaCMXBean", classLoader) && ClassUtils.isPresent("org.crac.management.CRaCMXBean", classLoader) ? new CoordinatedRestoreAtCheckpointStartup() : new StandardStartup());
}
此方法的判断很多,并且使用了反射类的初始化构造器,关于为什么使用反射类本身的构造器。可能是为了基于线程隔离与命名空间,后续详细探讨。
关于方法中判断的包名现在不做研究。
看来不存在,也就是不符合以上的条件,此处返回 new StandardStartup()
static final SpringApplicationShutdownHook shutdownHook = new SpringApplicationShutdownHook();
if (this.registerShutdownHook) {
shutdownHook.enableShutdownHookAddition();
}
void enableShutdownHookAddition() {
this.shutdownHookAdditionEnabled = true;
}
private void configureHeadlessProperty() {
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
}
这个 `configureHeadlessProperty` 方法主要做了以下几件事情:
1. 使用 `System.setProperty` 方法设置系统属性 "java.awt.headless" 的值。
2. 它设置的值是通过表达式 `Boolean.toString(this.headless)` 确定的。
具体来说:
- `System.setProperty("java.awt.headless", ...)` 设置系统属性 "java.awt.headless" 的值为一个特定的值。这个系统属性用于指示Java运行时环境是否以无界面(headless)模式运行,也就是没有图形用户界面(GUI)的模式。
- `System.getProperty("java.awt.headless", Boolean.toString(this.headless))` 用于获取系统属性 "java.awt.headless" 的当前值。如果该属性尚未设置,它将默认为 `Boolean.toString(this.headless)` 指定的值。
- `Boolean.toString(this.headless)` 将布尔值 `this.headless` 转换为字符串表示形式。这个布尔值通常用于指示是否以无界面模式运行Java程序。如果 `this.headless` 为 `true`,那么系统属性 "java.awt.headless" 将被设置为 "true";如果 `this.headless` 为 `false`,则系统属性 "java.awt.headless" 将被设置为 "false"。
此值在构造时已被定义为true。
故默认为true,中途没有方法修改此值。
private SpringApplicationRunListeners getRunListeners(String[] args) {
SpringFactoriesLoader.ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
List<SpringApplicationRunListener> listeners = this.getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver);
SpringApplicationHook hook = (SpringApplicationHook)applicationHook.get();
SpringApplicationRunListener hookListener = hook != null ? hook.getRunListener(this) : null;
if (hookListener != null) {
listeners = new ArrayList((Collection)listeners);
((List)listeners).add(hookListener);
}
return new SpringApplicationRunListeners(logger, (List)listeners, this.applicationStartup);
}
SpringFactoriesLoader.ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);又构造了一个函数式接口,通过传入的参数,已经构造出了一个匿名方法。
private SpringApplicationRunListeners getRunListeners(String[] args) {
SpringFactoriesLoader.ArgumentResolver argumentResolver = ArgumentResolver.of(SpringApplication.class, this);
argumentResolver = argumentResolver.and(String[].class, args);
List<SpringApplicationRunListener> listeners = this.getSpringFactoriesInstances(SpringApplicationRunListener.class, argumentResolver);
SpringApplicationHook hook = (SpringApplicationHook)applicationHook.get();
SpringApplicationRunListener hookListener = hook != null ? hook.getRunListener(this) : null;
if (hookListener != null) {
listeners = new ArrayList((Collection)listeners);
((List)listeners).add(hookListener);
}
return new SpringApplicationRunListeners(logger, (List)listeners, this.applicationStartup);
}中getSpringFactoriesInstances再一次调用了。此次带入了argumentResolver参数。
private Object[] resolveArgs(@Nullable ArgumentResolver argumentResolver) {
Class<?>[] types = this.constructor.getParameterTypes();
Object[] var3;
if (argumentResolver != null) {
Stream var10000 = Arrays.stream(types);
Objects.requireNonNull(argumentResolver);
var3 = var10000.map(argumentResolver::resolve).toArray();
} else {
var3 = new Object[types.length];
}
return var3;
}
这个地方对传入的参数进行了解析。因为相比以前多了一个argumentResolver,所以会调用到此方法。
T instantiate(@Nullable ArgumentResolver argumentResolver) throws Exception {
Object[] args = this.resolveArgs(argumentResolver);
return isKotlinType(this.constructor.getDeclaringClass()) ? SpringFactoriesLoader.KotlinDelegate.instantiate(this.constructor, args) : this.constructor.newInstance(args);
}
解析出来之后调用了这个方法,还是老样子。不过多了构造参数
List<SpringApplicationRunListener> listeners已被工厂生产。
getRunListeners后续的代码HOOK因为返回值都为null,所以没有什么实质操作。
最后返回 return new SpringApplicationRunListeners(logger, (List)listeners, this.applicationStartup);
此时SpringApplicationRunListeners listeners = this.getRunListeners(args);
为:
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
this.doWithListeners("spring.boot.application.starting", (listener) -> {
listener.starting(bootstrapContext);
}, (step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
private static final DefaultStartupStep DEFAULT_STARTUP_STEP = new DefaultStartupStep();
public DefaultStartupStep start(String name) {
return DEFAULT_STARTUP_STEP;
}