初识Spring Boot
SpringBoot是建立在Spring框架之上的一个项目,它的目标是简化Spring应用程序的初始搭建以及开发过程。
对比Spring
Spring Boot作为Spring框架的一个模块,旨在简化Spring应用程序的初始搭建和开发过程,以下是Spring Boot相对于传统Spring框架的一些优势和劣势:
优势:
- 简化配置: Spring Boot提供了大量的自动配置类,这些类可以自动配置Spring应用,减少了XML和Java配置代码的编写。
- 快速启动:通过提供默认的配置和启动类,Spring Boot可以快速启动和运行Spring应用,无需进行复杂的配置。
- 独立运行:Spring Boot应用包含内嵌的HTTP服务器(如Tomcat、Jetty或Undertow),可以打包成一个独立的JAR文件,用java -jar命令运行。
- 无需XML配置:Spring Boot不需要使用XML配置文件,尽管它仍然支持XML配置,但推荐使用基于Java的配置。
- 社区和插件生态:Spring Boot有一个活跃的社区,提供了大量的“Starters”或插件,这些插件简化了依赖管理和特定功能的集成。
- 监控和管理:Spring Boot提供了多种监控和管理功能,如健康检査、度量信息收集等,这些功能可以通过actuator模块轻松实现。
- 兼容性:Spring Boot与整个Spring生态系统兼容,可以很容易地集成Spring的其他项目,如’Spring Data、Spring Security等。
劣势:
- 过度依赖自动配置:虽然自动配置简化了开发,但有时可能导致难以理解的配置问题,特别是在自动配置与手动配置混合使用时。
- 可能的过度封装:Spring Boot的封装层次较深,对于需要深入了解底层实现的开发者来说,可能会感到不够透明。
- 灵活性降低:在某些情况下,Spring Boot的自动配置可能会限制灵活性,特别是在需要对Spring框架进行精细控制的场景。
- 版本依赖:Spring Boot应用的依赖管理虽然简化了,但过度依赖Spring Boot的版本可能会限制对Spring框架新特性的采用。
- 不适合小型应用:对于一些非常简单的小型应用,使用Spring Boot可能会显得有些“杀鸡用牛刀”,因为即使是很小的应用也需要包含Spring Boot的整个生态。
Spring Boot项目目录
官方推荐项目目录结构
互联网公司在开发Spring Boot项目时,通常会有一套规范的目录文件结构,这提高了代码的可读性和可维护性、实现了关注点分离、提升项目可扩展性。
下面的目录结构为官方推荐目录结构:
src/main/java:存放业务代码
src/main/resources:存放资源文件
static:存放静态文件,如css、js、image(访问方式:http:localhost:8080/js/main.js)
public:存放公共文件
templates:存放静态页面,如html、jsp
config:存放配置文件,如application.properties
src/test:存放测试代码
启动类位置常见形式
在初学Spring Boot时,经常遇到项目无法启动的奇怪现象,很有可能时应用启动的位置,即Application的启动类位置有问题。
Application启动的方式大致为三种:
- 当启动类和controller在同一个类时,只需要在controller的类上添加注解@Controller即可。
- 当启动类和controller分开时,启动类放在根目录下,只需要在启动类的类上添加注解@SpringBootApplication即可。
- 当启动类和controller分开时,启动类放在非根目录下,只需要在启动类的类上添加注解@ComponentScan,并配置需要扫秒的包名即可。
在实际的工作当中,我们通常会选择第二种,若项目庞大,漏掉了相关的配置扫描包,出现了问题是极其难以排查的。
Spring Boot单元测试
在软件上线之前,软件都需要经历测试这个流程,测试大致包括单元测试、黑白盒测试、回归测试、集成测试和系统测试。
单元测试:完成最小的软件设计单元的验证工作,目标是确保模块被正确的编码。
三大测试注解
@Test、@After、@Before 是 JUnit 框架中的注解,用于控制测试的执行流程和生命周期。
@Test
- 作用:@Test 注解用于标记实际的测试方法。当你执行测试套件时,JUnit 会运行所有标记了@Test 注解的方法。
- 生命周期:每次测试运行时,@Test 注解的方法都会被执行一次。
- 目的:验证代码的特定行为或功能是否按预期工作。
@Before
- 作用:@Before 注解用于标记在每个测试方法执行之前都会运行的方法。这通常用于设置测试环境,如初始化变量、配置测试数据等。
- 生命周期:在每个 @Test 方法执行之前,JUnit 会先执行标记了 @Before 的方法。
- 目的:确保每个测试方法都在一个干净和一致的环境中开始。
@After
- 作用:@After 注解用于标记在每个测试方法执行之后都会运行的方法。这通常用于执行清理工作,如释放资源、删除测试数据等。
- 生命周期:在每个 @Test 方法执行之后,JUnit 会执行标记了 @After 的方法。
- 目的:确保测试完成后,测试环境被恢复到原始状态,以便其他测试方法不会受到干扰。
三大注解实践
假设我们有一个简单的银行账户服务,该服务允许用户进行存款和取款操作,并跟踪账户余额。
我们需要写一个BankAccountServiceTest的测试类来测试这个简单的银行账户服务。
BankAccountService
public class BankAccountService {
private int balance;
public BankAccountService() {
this.balance = 0; // 初始余额为0
}
public void deposit(int amount) {
balance += amount;
}
public boolean withdraw(int amount) {
if (amount > balance) {
return false;
}
balance -= amount;
return true;
}
public int getBalance() {
return balance;
}
}
以上是简单的银行存取款业务。
BankAccountServiceTest
@SpringBootTest
public class BankAccountServiceTest {
private BankAccountService account;
@Before
public void setUp() {
// 初始化银行账户服务
account = new BankAccountService();
System.out.println("初始化成功");
}
@After
public void tearDown() {
// 清理工作,这里没有特别需要清理的资源,但可以用于关闭连接、释放资源等
account = null;
System.out.println("任务完成,清理战场中...");
}
@Test
public void testDeposit() {
account.deposit(100);
assertEquals("余额应该为100", 100, account.getBalance());
}
@Test
public void testWithdraw() {
account.deposit(200);
assertTrue("取款100应该成功", account.withdraw(100));
assertEquals("余额应该为100", 100, account.getBalance());
}
@Test
public void testWithdrawInsufficientFunds() {
account.deposit(50);
assertFalse("取款100应该失败,因为余额不足", account.withdraw(100));
assertEquals("余额应该保持为50", 50, account.getBalance());
}
}
- setup()方法在每个测试方法执行之前被调用,用于初始化BankAccountService实例。
- tearDown ()方法在每个测试方法执行之后被调用,这里用于释放资源,虽然在这个简单的例子中没有需要特别释放的资源,但在实际应用中,这可能包括关闭数据库连接、停止服务等。
- testDeposit()方法测试存款功能,验证账户余额是否正确增加。
- testwithdraw()方法测试取款功能,首先存款,然后验证取款操作是否成功,并检查余额是否正确减少。
- testwithdrawInsufficientFunds ()方法测试在余额不足时取款,验证操作是否失败,并检查余额是否保持不变。
在我们运行后面这三个方法时,在每个方法之前都会运行setup()方法,每个方法之后都会运行tearDown ()方法。
测试结果如下: