为什么以继承的方式引入SpringBoot

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
</dependency>

作为父项目和作为依赖的区别:

继承父工程的优势:

  • 依赖管理:可以在父工程中定义依赖的版本,子模块可以直接引用而不必指定版本号。
  • 插件管理:可以在父工程中配置常用的插件及其版本,子模块可以直接使用这些配置。
  • 属性设置:可以在父工程中定义一些通用的属性,如项目编码、Java 版本等。
  • 统一配置:可以统一多个子模块的构建配置,确保一致性。

直接引入依赖的局限性(如果你不使用继承父工程的方式,而是通过直接引入依赖的方式来管理项目,那么你将失去上述的一些优势)

  • 依赖版本管理:每个子模块都需要单独指定依赖的版本,这会导致大量的重复配置,并且难以维护。
  • 插件配置:每个子模块都需要单独配置插件及其版本,无法共享父工程中的插件配置。
  • 属性设置:每个子模块都需要单独设置通用的属性,如项目编码、Java 版本等。
  • 构建配置:每个子模块的构建配置需要单独维护,难以保证一致性。

    总结:选择哪种方式取决于你的具体需求。

    • 如果你希望多个项目之间共享构建配置,那么使用父项目是一个好的选择;
    • 如果你只是想在项目之间共享代码,那么应该使用依赖关系。

Spring Boot预先对开发中需要用到的依赖进行了版本的统一管理。我们需要和SpringBoot框架共享这个构建配置。因此官方推荐使用继承的方式引入SpringBoot框架。

依赖统一管理的好处

Spring Boot 框架的一个重要特性就是简化项目依赖管理。它通过提供一个叫做依赖管理的功能来帮助开发者更容易地管理和使用第三方库和其他Spring组件。具体来说,SpringBoot 提供了一个包含多个Spring和其他常用库的依赖版本配置文件(通常是在 spring-boot-dependencies 文件中),这使得开发者不需要在自己的项目中显式指定这些依赖的版本号

这样做有以下几个好处:

  • 简化依赖声明
    开发者只需要在 pom.xml 文件中声明需要的依赖而不需要指定其版本号,因为 Spring Boot 已经为这些依赖指定了版本。例如,如果你需要使用mysql驱动,你只需要添加相应的依赖声明而不需要关心版本。

    1
    2
    3
    4
    <dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    </dependency>
  • 避免版本冲突
    当多个库之间存在依赖关系的时候,如果手动管理版本可能会导致版本之间的冲突(即“依赖地狱”)。Spring Boot 提供的统一版本管理可以减少这种冲突的可能性。

  • 易于升级
    当 Spring Boot 发布新版本时,通常会更新其依赖库到最新稳定版。因此,当你升级 Spring Boot 版本时,它所管理的所有依赖也会随之更新到兼容的版本。
  • 减少配置错误
    由于 Spring Boot 自动处理了依赖的版本,减少了手动输入版本号可能引入的拼写或格式错误。
  • 提高开发效率
    开发者可以专注于业务逻辑的编写,而不是花费时间在解决依赖问题上。
  • 总的来说,Spring Boot 的依赖管理功能使得开发者可以更加专注于业务逻辑的实现,同时减少了因依赖版本不一致而引发的问题,提高了项目的可维护性和开发效率。
  • 如果在项目中需要更改某个依赖的版本号,不想使用SpringBoot框架指定的版本号,只需要在引入依赖时强行执行版本号即可,maven支持就近原则

Starter-启动器

在SpringBoot中,启动器(Starter)本质上是一个简化依赖管理的概念。
Spring Boot 的启动器本质上就是一组预定义的依赖集合,它们被组织成一个个Maven的依赖,以方便开发者快速集成特定的功能模块。
如果你想做web开发,只需要引入web启动器。web启动器会自动引入web开发所需要的子依赖。

启动器实现原理

  1. 依赖聚合
    每个启动器通常对应一个特定的功能集或者一个完整的应用模块,如 spring-boot-starter-web 就包含了构建 Web 应用所需的所有基本依赖项,如 Spring MVC, Tomcat 嵌入式容器等。
  2. 依赖传递
    当你在项目中引入一个启动器时,它不仅会把自身作为依赖加入到你的项目中,还会把它的所有直接依赖项(transitive dependencies)也加入进来。这意味着你不需要单独声明这些依赖项,它们会自动成为项目的一部分。
  3. 版本管理
    启动器内部已经指定了所有依赖项的具体版本,这些版本信息存储在一个公共的 BOM(Bill of Materials,物料清单)文件中,通常是 spring-boot-dependencies。当引入启动器时,实际上也间接引用了这个 BOM,从而确保了所有依赖项版本的一致性。
  4. 自动配置
    许多启动器还提供了自动配置(Auto-configuration),这是一种机制,允许 SpringBoot 根据类路径上的可用组件自动设置你的应用程序。例如,如果类路径上有 Spring MVC 和嵌入式 Tomcat,则 Spring Boot 会自动配置它们,并准备好一个 web 应用程序。

有哪些启动器

启动器通常包括:

  • SpringBoot官方提供的启动器
  • 非官方提供的启动器

启动器命名特点:spring-boot-starter-*

启动器命名特点:*-spring-boot-starter

SpringBoot核心注解

创建一个只有Web启动器的新模块,并添加一个Controller类。

@SpringBootApplication

  • SpringBoot的主入口就被@SpringBootApplication注解标注,可见该注解的重要性。
    注解源码:
  • 该注解属于组合注解。拥有@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan的功能。

    @SpringBootConfiguration

    注解源码:
  • 能够发现该注解又被@Configuration注解标注,这说明主入口的程序本质是一个配置类,所以主入口的方法可以被@Bean注解标注,被@Bean注解的标注的方法会被Spring容器自动调用,并且将该方法的返回对象纳入IoC容器的管理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@SpringBootApplication
public class Sb305CoreApplication {
@Bean
public Date getNowDate(){ // 方法名作为bean的id
return new Date();
}
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Sb305CoreApplication.class, args);
Date dateBean1 = applicationContext.getBean(Date.class);
System.out.println(dateBean1);
Date dateBean2 = applicationContext.getBean("getNowDate", Date.class);
System.out.println(dateBean2);
}
}

这个配置类也可以称为,起源的意思,SpringBoot从这个配置类开始加载项目中所有的bean

@EnableAutoConfiguration

  • 该注解表示启用自动配置
  • Spring Boot 会根据引入的依赖自动配置好一系列的 Bean,无需手动编写复杂的配置代码。

例如:在SpringBoot中进行了以下配置:

1
2
3
4
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot
spring.datasource.username=root
spring.datasource.password=123456

并且在依赖中引入了mybatis依赖/mybatis启动器,那么SpringBoot框架将为你自动化配置以下bean:

  • SqlSessionFactory: MyBatis的核心工厂SqlSessionFactory会被自动配置。这个工厂负责创建SqlSession实例,后者用来执行映射文件中的SQL语句。
  • TransactionManager: DataSourceTransactionManager会被自动配置来管理与数据源相关的事务。

@ComponentScan注解

这个注解的作用是:启动组件扫描功能,代替spring框架xml文件中这个配置:

1
<context:component-scan base-package="com.powernode.sb305core"/>

因此被@SpringBootApplication注解标注之后,会自动扫描主入口程序所在包及子包,因此如果一个bean要纳入IoC容器的管理则必须放到主入口程序所在包及子包下。放到主入口程序所在包之外的话,扫描不到。测试一下:


1
2
3
4
5
6
7
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello world!";
}
}

启动服务器测试:

  • 此时UserController不在主入口程序所在包及子包下
1
2
3
4
5
6
7
@RestController
public class UserController {
@GetMapping("/list")
public String list(){
return "user list!";
}
}

启动服务器测试:

</div>
通过测试得知UserController没有被纳入IoC容器的管理。

最终结论:要让Bean纳入IoC容器的管理,必须将类放到主入口程序同级目录下,或者子目录下。

SpringBoot的单元测试

不使用单元测试调用Service

第一步:创建模块

  • 使用脚手架创建一个模块,该模块不添加任何启动器

    第二步:编写Service

    1
    2
    3
    4
    5
    6
    7
    @Service("userService")
    public class UserServiceImpl implements UserService {
    @Override
    public void save() {
    System.out.println("保存用户信息");
    }
    }

    第三步:调用Service

  • 直接在入口程序调用service
    1
    2
    3
    4
    5
    6
    7
    8
    @SpringBootApplication
    public class Sb306TestApplication {
    public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(Sb306TestApplication.class, args);
    UserService userService = applicationContext.getBean("userService", UserService.class);
    userService.save();
    }
    }
    执行结果:

该方法是手动获取Spring上下文对象ConfigurableApplicationContext,然后通过getBean方法获取bean,从Spring容器中获取Service对象,然后调用方法。

使用单元测试调用Service

引入test-starter

  • SpringBoot在创建项目时就为我们生成了单元测试类,但如果要使用单元测试,还需要引入单元测试的启动器,如果使用脚手架创建SpringBoot项目,这个test启动器会自动引入

    1
    2
    3
    4
    5
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>

    @SpringBootTest注解

    @SpringBootTest 会创建一个完整的 Spring 应用程序上下文(Application Context),这个上下文包含了应用程序的所有组件和服务。以下是 @SpringBootTest 做的一些主要工作:

  • 创建 ApplicationContext

    • @SpringBootTest 使用 SpringApplicationrun() 方法来启动一个 Spring Boot 应用程序上下文。这意味着它会加载应用程序的主配置类和其他相关的配置类。
  • 加载配置文件

    • 它会查找并加载默认的配置文件,如 application.properties
  • 自动配置

    • 如果应用程序依赖于 Spring Boot 的自动配置特性,@SpringBootTest 会确保这些自动配置生效。这意味着它会根据可用的类和bean来自动配置一些组件,如数据库连接、消息队列等。
  • 注入依赖

    • 使用 @SpringBootTest 创建的应用程序上下文允许你在测试类中使用 @Autowired 注入需要的 bean,就像在一个真实的 Spring Boot 应用程序中一样。

总的来说,@SpringBootTest 为你的测试提供了尽可能接近实际运行时环境的条件,这对于验证应用程序的行为非常有用。

注入Service并调用

1
2
3
4
5
6
7
8
9
10
11
12
@SpringBootTest
class Sb306TestApplicationTests {

@Autowired
private UserService userService;

@Test
void contextLoads() {
userService.save();
}

}

测试结果如下:

外部化配置

什么是外部化配置?

外部化配置:将配置信息存储在应用程序代码之外的地方。这样配置信息可以独立于代码进行管理。这样方便了配置的修改,并且修改后不需要重新编译代码,也不需要重新部署项目。
官方文档

5.1.1. 外部化配置的方式

SpringBoot支持多种外部化配置方式,包括但不限于:

  • properties文件
  • YAML文件
  • 系统环境变量
  • 命令行参数
  • ……

5.1.2. 外部化配置的优势

  1. 灵活性:配置文件可以独立于应用程序部署,这使得可以根据运行环境的不同来调整配置,而无需修改代码。
  2. 易于维护:配置变更不需要重新构建和部署应用程序,降低了维护成本。
  3. 安全性:敏感信息如数据库密码、API密钥等可以存储在外部,并且可以限制谁有权限访问这些配置信息。
  4. 共享性:多实例或多服务可以共享相同的配置信息,减少重复配置的工作量。
  5. 版本控制:配置文件可以存放在版本控制系统中,便于跟踪历史版本和回滚配置。

总之,外部化配置使得配置更加灵活、安全、易于管理和共享,是现代云原生应用中非常推荐的做法

5.1.3. 外部化配置对比传统配置

  • 在传统的SSM三大框架中,如果修改XML的配置后,需要对应用重新打包,重新部署
  • 使用SpringBoot框架的外部化配置后,修改配置后,不需要对应用重新打包,也不需要重新部署,最多重启一下服务即可。

5.2. properties文件

  • application.properties配置文件是SpringBoot框架默认的配置文件。
  • application.properties可以不配置,因为SpringBoot为我们提供了一套默认配置。
  • 如果你要改变这些默认的行为,可以在application.properties文件中进行配置。
  • application.properties可以放在类路径当中,也可以放在项目之外。因此称为外部化配置

Spring Boot 框架在启动时会尝试从以下位置加载 application.properties 配置文件:

  1. file:./config/:首先在Spring Boot 当前工作目录下的 config 文件夹中查找。
    • 注意:如果没有找到**application.properties**会继续找**application.yml**,如果这两个都没有找到,才会进入以下位置查找,以此类推。
  2. file:./:如果在当前工作目录下config目录中找不到时,再从当前工作目录中查找。
  3. classpath:/config/:如果从工作目录中找不到,会从类路径中找,先从类路径的 /config/ 目录下寻找配置文件。
  4. classpath:/:如果在 /config/ 下没有找到,它会在类路径的根目录下查找。

Spring Boot 会按照这个顺序来加载配置文件,如果在多个位置有相同的属性定义,那么最先检查的位置中的属性值将优先使用。

如果你想要指定其他的配置文件位置或者改变默认的行为,可以通过 —spring.config.location= 后跟路径的方式来指定配置文件的具体位置。例如 :

1
java -jar sb3-01-first-web-1.0-SNAPSHOT.jar --spring.config.location=file:///E:\a\b\application.properties

此时SpringBoot会首先从E:\a\b这个路径加载配置文件。这种方式可以用来覆盖默认的配置文件。

注意:以上的—spring.config.location=file:///E:\a\b\application.properties就属于命令行参数,它将来会被传递到main方法的(String[] args)参数上。

优先级总结:
application.properties/yml (classpath) < application.properties/yml (外部路径) < profile 配置文件 < 环境变量 & JVM 参数 < 命令行参数