springboot启动类--SpringApplication.run()详解

本文深入剖析SpringBoot项目的启动过程,从创建SpringApplication对象到调用run方法,详细解读上下文准备、容器初始化等关键步骤。

前言

实习的第一个项目是利用springboot完成一个需求,在项目搭建的过程中真正感受到springboot的强大,springboot的起步依赖以及自动配置特性简直不要太爽,在项目搭建的过程中解放了我们的小手。而springboot的启动也非常简单,只需要启动springboot的启动类,springboot会帮助我们准备所有的环境,包括server,监听器,装配spring的上下文等等,springboot的启动类如此强大我们没有理由不去深究一下。

目录

启动类

创建SpringApplication对象

调用run方法

总结


启动类

@SpringBootApplication
public class SpringTestApplication {

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

}

SpringTestApplication类执行main方法,main方法调用SpringApplication的run方法。

run方法干了两件事:

  1. 创建SpringApplication对象
  2. 利用创建好的SpringApplication对象调用run方法

创建SpringApplication对象

在执行SpringApplication的run方法时首先会创建一个SpringApplication类的对象,利用构造方法创建SpringApplication对象时会调用initialize方法。

SpringApplication.run()方法的基本调用流程:

public static ConfigurableApplicationContext run(Object source, String... args) {
		return run(new Object[] { source }, args);
	}

public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
		return new SpringApplication(sources).run(args);
	}

public SpringApplication(Object... sources) {
		initialize(sources);
	}

initialize方法:

private void initialize(Object[] sources) {
    // 在sources不为空时,保存配置类
    if (sources != null && sources.length > 0) {
        this.sources.addAll(Arrays.asList(sources));
    }
    // 判断是否为web应用
    this.webEnvironment = deduceWebEnvironment();
    // 获取并保存容器初始化类,通常在web应用容器初始化使用
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationContextInitializer
    setInitializers((Collection) getSpringFactoriesInstances(
        ApplicationContextInitializer.class));
    // 获取并保存监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的ApplicationListener
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 从堆栈信息获取包含main方法的主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

调用run方法

public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    // 配置属性
    configureHeadlessProperty();
    // 获取监听器
    // 利用loadFactoryNames方法从路径MEAT-INF/spring.factories中找到所有的SpringApplicationRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 启动监听
    // 调用每个SpringApplicationRunListener的starting方法
    listeners.starting();
    try {
        // 将参数封装到ApplicationArguments对象中
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        // 准备环境
        // 触发监听事件——调用每个SpringApplicationRunListener的environmentPrepared方法
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
            applicationArguments);
        // 从环境中取出Banner并打印
        Banner printedBanner = printBanner(environment);
        // 依据是否为web环境创建web容器或者普通的IOC容器
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        // 准备上下文
        // 1.将environment保存到容器中
        // 2.触发监听事件——调用每个SpringApplicationRunListeners的contextPrepared方法
        // 3.调用ConfigurableListableBeanFactory的registerSingleton方法向容器中注入applicationArguments与printedBanner
        // 4.触发监听事件——调用每个SpringApplicationRunListeners的contextLoaded方法
        prepareContext(context, environment, listeners, applicationArguments,
            printedBanner);
        // 刷新容器,完成组件的扫描,创建,加载等
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        // 触发监听事件——调用每个SpringApplicationRunListener的finished方法
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        // 返回容器
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}

总结

SpringApplication.run一共做了两件事

  1. 创建SpringApplication对象;在对象初始化时保存事件监听器,容器初始化类以及判断是否为web应用,保存包含main方法的主配置类。
  2. 调用run方法;准备spring的上下文,完成容器的初始化,创建,加载等。会在不同的时机触发监听器的不同事件。

参考文献:

【1】.https://www.cnblogs.com/davidwang456/p/5846513.html

【2】.https://mp.weixin.qq.com/s/O2zS2yyLy1Vu_aPtR28wiQ

2025-07-16T08:39:46.554+08:00 ERROR 16124 --- [takeout system] [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'userMapper' defined in file [E:\IDEA\takeoutsystem\target\classes\com\example\takeoutsystem\mapper\UserMapper.class]: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getTypeForFactoryBean(AbstractAutowireCapableBeanFactory.java:864) ~[spring-beans-6.2.8.jar:6.2.8] at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:745) ~[spring-beans-6.2.8.jar:6.2.8] at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(DefaultListableBeanFactory.java:817) ~[spring-beans-6.2.8.jar:6.2.8] at org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector.detect(AnnotationDependsOnDatabaseInitializationDetector.java:36) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor.detectDependsOnInitializationBeanNames(DatabaseInitializationDependencyConfigurer.java:152) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor.postProcessBeanFactory(DatabaseInitializationDependencyConfigurer.java:115) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363) ~[spring-context-6.2.8.jar:6.2.8] at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:197) ~[spring-context-6.2.8.jar:6.2.8] at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) ~[spring-context-6.2.8.jar:6.2.8] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) ~[spring-context-6.2.8.jar:6.2.8] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1361) ~[spring-boot-3.4.7.jar:3.4.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1350) ~[spring-boot-3.4.7.jar:3.4.7] at com.example.takeoutsystem.TakeoutApplication.main(TakeoutApplication.java:11) ~[classes/:na] 进程已结束,退出代码为 1 这是因为上面
07-17
<think>我们遇到了一个Spring Boot启动失败的问题,错误信息如下: ``` 2025-07-16T08:39:46.554+08:00 ERROR 16124 --- [takeout system] [ main] o.s.boot.SpringApplication : Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'userMapper' defined in file [E:\IDEA\takeoutsystem\target\classes\com\example\takeoutsystem\mapper\UserMapper.class]: Invalid value type for attribute 'factoryBeanObjectType': java.lang.String ... ``` 错误原因:在名为'userMapper'的bean定义中,属性'factoryBeanObjectType'的值类型无效,期望的类型是java.lang.String,但实际可能是其他类型。 根据堆栈信息,问题发生在`AbstractAutowireCapableBeanFactory.getTypeForFactoryBean`方法中。这通常与Spring的bean创建过程有关,特别是当bean是一个工厂bean(FactoryBean)时。 可能的原因: 1. 可能是在使用MyBatis时,Mapper接口被Spring错误地当作普通bean处理,而不是通过MyBatis的`MapperFactoryBean`来创建。通常,我们使用`@MapperScan`来扫描Mapper接口,这样Spring会为每个Mapper接口创建一个`MapperFactoryBean`,然后通过这个工厂bean来生成Mapper的代理对象。 2. 另一个可能的原因是配置问题,比如没有正确配置MyBatis的`SqlSessionFactory`,或者`@MapperScan`注解没有正确使用。 3. 还有可能是版本兼容性问题,但这里使用的是Spring Boot 3.4.7和MyBatis Spring Boot Starter,需要确保版本匹配。 解决方案: 1. 确保在Spring Boot主类上(或者配置类上)添加了`@MapperScan`注解,指定Mapper接口所在的包。例如: ```java @SpringBootApplication @MapperScan("com.example.takeoutsystem.mapper") public class TakeoutApplication { public static void main(String[] args) { SpringApplication.run(TakeoutApplication.class, args); } } ``` 2. 检查pom.xml中是否引入了MyBatis Spring Boot Starter依赖,并且版本与Spring Boot兼容。例如: ```xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> <!-- 请根据Spring Boot版本选择兼容的版本 --> </dependency> ``` 注意:Spring Boot 3.x需要MyBatis Spring Boot Starter 3.x版本。 3. 检查Mapper接口是否被正确标注了`@Mapper`注解(如果使用`@MapperScan`则可以不标注,但若没有`@MapperScan`,每个Mapper接口都需要标注`@Mapper`)。不过,通常推荐使用`@MapperScan`。 4. 检查Mapper接口是否被错误地添加了`@Component`或其他Spring的注解?Mapper接口应该只是一个接口,不需要其他注解(除了MyBatis的注解如`@Select`等)。 5. 检查配置文件中是否有关于MyBatis的配置,比如`mybatis.mapper-locations`是否指向了正确的mapper.xml文件位置(如果有XML文件的话)。 6. 如果使用了多个数据源或特殊配置,可能需要自定义`SqlSessionFactoryBean`,但需要确保正确配置。 由于错误信息中提到了`factoryBeanObjectType`属性类型错误,可能是Spring在尝试将`UserMapper`接口当作一个普通的FactoryBean来处理,但期望的`factoryBeanObjectType`应该是`Class<?>`类型,而实际可能是其他类型。这通常是因为没有正确配置MyBatis,导致Spring无法识别这是一个MyBatis的Mapper工厂bean。 因此,重点检查`@MapperScan`和依赖。 如果以上步骤都没有解决问题,可以尝试清理和重新构建项目(比如删除target目录,重新运行`mvn clean install`或Gradle的相应命令)。 下面是一个示例的配置: 1. 主类配置: ```java package com.example.takeoutsystem; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.takeoutsystem.mapper") public class TakeoutApplication { public static void main(String[] args) { SpringApplication.run(TakeoutApplication.class, args); } } ``` 2. pom.xml依赖(示例): ```xml <dependencies> <!-- 其他依赖 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <!-- 数据库驱动,比如MySQL --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> </dependencies> ``` 3. 确保Mapper接口在`com.example.takeoutsystem.mapper`包下,例如: ```java package com.example.takeoutsystem.mapper; import org.apache.ibatis.annotations.Mapper; @Mapper // 如果使用了@MapperScan,这个注解可以省略,但建议保留或者二选一 public interface UserMapper { // 方法定义 } ``` 4. 在application.properties或application.yml中配置数据源和MyBatis(如果需要): ```properties spring.datasource.url=jdbc:mysql://localhost:3306/takeout?serverTimezone=UTC spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 如果需要指定mapper.xml文件的位置(如果使用XML方式) mybatis.mapper-locations=classpath:mapper/*.xml ``` 如果问题仍然存在,请检查是否有其他配置干扰,比如是否自定义了`SqlSessionFactory`?如果有,请确保其正确性。 由于错误信息指向了`UserMapper`,所以请仔细检查`UserMapper`的定义和相关的配置。 希望这些步骤能帮助你解决问题。 §§相关问题§§: 1. 如何正确配置MyBatis与Spring Boot的集成? 2. 在Spring Boot中,使用MyBatis时有哪些常见的配置错误? 3. 如何解决Spring Boot启动时Bean创建失败的问题? 4. 如何自定义MyBatis的SqlSessionFactory?</think>### 问题分析 根据错误日志,Spring Boot应用启动失败的原因是`userMapper`的Bean定义无效。核心错误信息是: ``` Invalid bean definition with name 'userMapper'... Invalid value type for attribute 'factoryBeanObjectType': java.lang.String ``` 这表明Spring在尝试创建`userMapper` Bean时,遇到了类型不匹配的问题。具体来说,`factoryBeanObjectType`属性期望一个`Class`类型,但实际获取到的是`String`类型。 ### 解决方案 #### 1. 检查Mapper接口注解 确保Mapper接口正确使用了`@Mapper`注解(或`@Repository`),且没有错误添加其他注解: ```java // 正确示例 package com.example.takeoutsystem.mapper; import org.apache.ibatis.annotations.Mapper; @Mapper // ✅ 关键注解 public interface UserMapper { // 方法定义 } ``` #### 2. 检查Mapper扫描配置 在Spring Boot主类或配置类中添加`@MapperScan`扫描Mapper接口: ```java package com.example.takeoutsystem; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan("com.example.takeoutsystem.mapper") // ✅ 指定Mapper包路径 public class TakeoutApplication { public static void main(String[] args) { SpringApplication.run(TakeoutApplication.class, args); } } ``` #### 3. 验证依赖配置 在`pom.xml`中确保包含正确的MyBatis依赖: ```xml <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> <!-- 匹配Spring Boot 3.x --> </dependency> ``` #### 4. 检查XML映射文件(如果使用) 若使用XML映射文件,确保路径配置正确(`application.properties`): ```properties # 正确配置XML路径 mybatis.mapper-locations=classpath*:mapper/**/*.xml ``` #### 5. 清理并重建项目 执行Maven清理和重新编译: ```bash mvn clean install ``` ### 错误原因详解 1. **factoryBeanObjectType类型不匹配** Spring期望该属性为`Class<?>`类型,但实际获取到`String`。通常是因为: - Mapper接口未被正确识别为MyBatis组件 - 缺少`@MapperScan`导致Spring尝试用错误方式创建Bean 2. **常见触发场景** - 忘记添加`@Mapper`或`@MapperScan` - Mapper接口被错误添加`@Component`或`@Service` - MyBatis依赖版本不兼容(如Spring Boot 3.x使用MyBatis 2.x) ### 验证修复 修复后启动日志应显示Mapper被正确注册: ``` 2025-07-16 INFO [main] o.s.b.f.s.DefaultListableBeanFactory : Creating shared instance of singleton bean 'userMapper' ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值