【 SpringBoot 】- [SpringApplication] -- 初始化

本文深入探讨了SpringApplication的初始化过程,包括WebApplicationType的推断、通过SpringFactoriesLoader加载ApplicationContextInitializer和ApplicationListener的实现类,以及如何通过反射实例化和排序。最后详细解释了如何通过deduceMainApplicationClass方法确定启动类,完成SpringApplication的初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

·康忙,先跟着下图走一遍静静心...


 

  • 创建SpringBoot项目 -> main 启动

@SpringBootApplication
public class DemoApplication {

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

}
  • -> SpringApplication.run(DemoApplication.class, args)

 public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class[]{primarySource}, args);
    }

 public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return (new SpringApplication(primarySources)).run(args);
    }

  • -> new SpringApplication,这里只分析初始化,run启动后面分析

    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 = new HashSet();
        this.isCustomEnvironment = false;
        // 资源加载器
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // Set<Class<?>> primarySources:意义在于存放 Java Config 类,这里就是  DemoApplication
        this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 1.通过 classpath ,判断 Web 应用类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2.设置 List<ApplicationContextInitializer<?>> initializers
        this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3.设置 List<ApplicationListener<?>> listeners
        this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 4.获取调用 main 方法的启动类:Class<?> mainApplicationClass
        this.mainApplicationClass = this.deduceMainApplicationClass();
    }

目录.  第1. --> WebApplicationType.deduceFromClasspath()

 第2与3. --> 都调用 SpringApplication.getSpringFactoriesInstances(),这里拿 type=ApplicationContextInitializer.class 做分析

第4. --> SpringApplication.deduceMainApplicationClass()


第1. --> 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;
                }
            }
//private static final String[] SERVLET_INDICATOR_CLASSES = new String[]
//{"javax.servlet.Servlet","org.springframework.web.context.ConfigurableWebApplicationContext"} 
// 判断classpath下是否存在该数组里面的class,存在则返回 SERVLET
            return SERVLET;
        }
    }

第2与3. --> 都调用 SpringApplication.getSpringFactoriesInstances(),这里拿 type=ApplicationContextInitializer.class 做分析

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
        return this.getSpringFactoriesInstances(type, new Class[0]);
    }

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = this.getClassLoader();
// 1. 加载配置文件 'META-INF/spring.factories',并且从中获取指定类型的类名的数组,放入set去重
        Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 2. 创建对象实例
        List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 3. 排序对象集合
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

 ·这里只分析介绍 --> SpringFactoriesLoader.loadFactoryNames()

    public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
        // 获得factoryClassName,我们在这里的 factoryClassName =org.springframework.context.ApplicationContextInitializer.
        String factoryClassName = factoryClass.getName();
        // 获取 factoryClassName 为key下的 className 集合
        return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
    }

// 加载 META-INF/spring.factories 文件,信息以 key-value 形式存入 map
    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 首先从缓存 Map<ClassLoader, MultiValueMap<String, String>> cache 中获取
        MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
        if (result != null) {
            return result;// 缓存中存在,返回
        } else {
            try {
                // 加载 META-INF/spring.factories 文件
                Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
                LinkedMultiValueMap result = new LinkedMultiValueMap();

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    // 把resource 转为 properties
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryClassName = ((String)entry.getKey()).trim();
                        String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        int var10 = var9.length;

                        for(int var11 = 0; var11 < var10; ++var11) {
                            String factoryName = var9[var11];
                            // 把 factoryClassName 下的所有 className 存入 result中
                            result.add(factoryClassName, factoryName.trim());
                        }
                    }
                }
                // 放入缓存
                cache.put(classLoader, result);
                return result;
            } catch (IOException var13) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
            }
        }
    }

断点调试查看 result

·以 org.springframework.context.ApplicationContextInitializer 为key的集合中共有6个元素

因为 /Users/~/.m2/repository/org/springframework/boot/spring-boot/2.1.6.RELEASE/spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories 中有4个,/Users/~/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.6.RELEASE/spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring.factories有2个

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

·以 org.springframework.context.ApplicationListener 为key的集合中共有10个元素

因为 /Users/~/.m2/repository/org/springframework/boot/spring-boot/2.1.6.RELEASE/spring-boot-2.1.6.RELEASE.jar!/META-INF/spring.factories 中有9个,/Users/~/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.1.6.RELEASE/spring-boot-autoconfigure-2.1.6.RELEASE.jar!/META-INF/spring.factories有1个

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

· 然后拿着当前class类型下的 names集合,进行实例化(利用反射,就不详细介绍),最后进行排序。

--> classType=ApplicationContextInitializer.class 时获取到的实例如下

--> classType=ApplicationListener.class 时获取到的实例如下

stop......... 从第2、3步中的思绪中跳出来,分析初始化 SpringApplication 的最后一步


第4. --> SpringApplication.deduceMainApplicationClass()

   private Class<?> deduceMainApplicationClass() {
        try {
            // 获取当前方法调用栈元素
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                // 找到 main 函数的类
                if ("main".equals(stackTraceElement.getMethodName())) {
                // 获取获取启动类
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }

 debug --> 通过寻找 main 方法找到启动类 DemoApplication

赋与私有属性 private Class<?> mainApplicationClass ;主要用于日志的打印


至此,SpringApplication 的初始化就完成啦!

### Spring Boot 和 JavaFX 集成与打包解决方案 在现代开发环境中,将 JavaFX 与 Spring Boot 结合可以充分利用两者的优势:Spring Boot 提供强大的微服务框架支持,而 JavaFX 则提供丰富的图形界面功能。以下是关于如何实现两者的集成以及正确打包的方法。 #### 1. 添加依赖项 为了使 Spring Boot 支持 JavaFX 的运行环境,需要引入必要的 Maven 或 Gradle 依赖项。以下是一个典型的 Maven `pom.xml` 文件配置: ```xml <dependencies> <!-- Spring Boot Starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!-- JavaFX SDK --> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-controls</artifactId> <version>17.0.1</version> </dependency> <dependency> <groupId>org.openjfx</groupId> <artifactId>javafx-fxml</artifactId> <version>17.0.1</version> </dependency> </dependencies> ``` 上述代码片段展示了如何通过 Maven 来管理 JavaFX 控件和 FXML 解析器的相关库[^1]。 #### 2. 创建启动类 由于 JavaFX 应用程序基于 `Application.launch()` 方法初始化 GUI 线程,因此需要自定义一个继承于 `javafx.application.Application` 并嵌套调用 Spring 上下文的入口点。下面展示了一个简单的例子: ```java import javafx.application.Application; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import org.springframework.boot.SpringApplication; import org.springframework.context.ConfigurableApplicationContext; public class MainApp extends Application { private ConfigurableApplicationContext springContext; @Override public void init() throws Exception { // 启动 Spring Boot 应用上下文 springContext = SpringApplication.run(YourBootApplication.class); } @Override public void start(Stage primaryStage) throws Exception { Parent root = springContext.getBean(MainController.class).getView(); Scene scene = new Scene(root); primaryStage.setTitle("Spring Boot + JavaFX"); primaryStage.setScene(scene); primaryStage.show(); } @Override public void stop() throws Exception { super.stop(); springContext.close(); // 关闭 Spring 上下文 } } ``` 此部分实现了 JavaFX 生命周期方法中的 Spring 初始化逻辑,并确保资源能够被正常释放[^2]。 #### 3. 打包方案 对于最终部署而言,传统的 JAR 文件可能无法满足需求,因为它们不包含本地平台特定的二进制文件(如 DLL、SO)。推荐使用工具如 **JPackage** 或者第三方构建插件来完成跨平台分发的任务。 ##### 使用 JPackage 工具 从 JDK 14 开始提供了官方命令行实用程序——JPackage,它允许开发者轻松创建可执行安装包。基本流程如下所示: ```bash jpackage --type app-image \ --input target/your-app-name.jar \ --main-jar your-main-class-file.jar \ --dest output_directory/ ``` 以上脚本会生成适用于目标操作系统的独立应用程序镜像目录结构[^3]。 #### 总结 通过合理设置项目架构并利用现代化技术手段解决传统难题,可以使复杂的桌面应用更加易于维护和发展。同时也要注意版本兼容性和性能优化等问题,在实际生产环境下需进一步测试验证其稳定性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值