简介

本质上说Spring是一个组件容器,它负责创建并管理容器中的组件(也被称为Bean),并管理组件之间的依赖关系。

为什么要用SpringBoot?

Spring缺点是配置过多,SpringBoot就是为了解决这个问题。

SpringBoot为大部分第三方框架整合提供了自动配置。SpringBoot使用约定优于配置(CoC Covention over Configuration)为理念,针对企业开发各种场景,提供对应的Starter。

总体来说SpringBoot具有以下特性:

  1. 内嵌Tomcat、jetty或Undertow服务器。
  2. SpringBoot应用可被做成独立的Java应用程序。
  3. 尽可能的自动配置Spring及第三方框架。
  4. 完全没有代码生成,不需要XML配置。
  5. 提供产品级监控功能,如运行状况检查和外部化配置等。

创建SpringBoot应用

开发环境准备

Java 、Maven(配置本地资源库和国内镜像)

创建SpringBoot项目

只需要创建一个Maven项目即可,单机IDEA主菜单“File”-->"New"-->"project"-->左边选"Maven"-->"Next"

项目结构如下:

使用SpringInitializr工具自动生成Maven项目

单机IDEA主菜单“File”-->"New"-->"project"

编写控制器

@Controller
public class BookController { @GetMapping("/")
public String index(Model model){
model.addAttribute("tip", "Hello, World!");
return "hello";
} @GetMapping("/rest")
// 指定该方法生成restful风格的响应
// 在前后端分离的架构中,SpringBoot应用只需要对外提供restful响应,前端通过restful接口与后端通信
@ResponseBody
public ResponseEntity restIndex() {
return new ResponseEntity<>(
"Hello, World from REST!",null,
HttpStatus.OK);
}
}

运行应用

package com.example.ali2;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; // 被@SpringBootApplication修饰的类位于com.example.ali2包下,Spring Boot会自动扫描该包及其子包下的所有配置类(@Configuration修饰的类)
// 和组件类(@Component、@Controller、@Service、@Repository 等修饰的类),将它变成容器中的bean
@SpringBootApplication
public class Ali2Application { public static void main(String[] args) {
SpringApplication.run(Ali2Application.class, args);
} }

创建jar包

<!--    打jar包首先保证在pom.xml中添加了spring-boot-maven-plugin插件,这样可以通过mvn package命令打包成可执行的jar文件。-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 此外,如果在pom.xml中添加了<packaging>元素,请确保是<packaging>jar</packaging>,省略也是可以的-->

执行maven命令打成jar包

mvn clean

mvn package

执行“mvn package”命令,会从默认生命周期的第一阶段一直执行到“package”阶段,最终生成一个可执行的jar文件。这个jar文件会被放在target目录下,文件名通常是<artifactId>-<version>.jar,例如ali2-0.0.1-SNAPSHOT.jar。
而maven的生命周期包含compile(编译)、test(单元测试)、package(打包)、install(安装到本地)、deploy(部署到远程)等阶段,执行“mvn package”命令会自动执行compile、test等阶段,直到package阶段。
如果前面的阶段失败,则打包失败。

开发业务组件

先引入数据库相关依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>5.3.5</version> <!-- 可根据需要选择版本 -->
</dependency>
<!-- Spring Data JPA 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>3.3.6</version>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
<scope>runtime</scope>
</dependency>

service

public interface BookService {
List<Book> getAllBooks();
Integer addBook(Book book);
void deleteBook(Integer id);
}

serviceImpl

@Service
@Transactional(propagation= Propagation.REQUIRED,timeout = 5) // 事务注解,表示该类中的所有方法需要事务支持
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public List<Book> getAllBooks() {
return (List<Book>)bookDao.findAll();
} @Override
public Integer addBook(Book book) {
bookDao.save(book);
return book.getId();
} @Override
public void deleteBook(Integer id) {
bookDao.deleteById(id);
}
}

controller

@Controller
public class BookController { @GetMapping("/")
public String index(Model model){
model.addAttribute("tip", "Hello, World!");
return "hello";
} @GetMapping("/rest")
// 指定该方法生成restful风格的响应
// 在前后端分离的架构中,SpringBoot应用只需要对外提供restful响应,前端通过restful接口与后端通信
@ResponseBody
public ResponseEntity restIndex() {
return new ResponseEntity<>(
"Hello, World from REST!",null,
HttpStatus.OK);
} @Autowired
private BookService bookService; @PostMapping("/addBook")
public String addBook(Book book, Model model) {
bookService.addBook(book);
return "redirect:listBooks";
} @PostMapping("/rest/books")
@ResponseBody
public ResponseEntity<Map<String,String>> restAddBook(@RequestBody Book book) {
bookService.addBook(book);
return new ResponseEntity<>(
Map.of("tip", "Book added successfully!"),
HttpStatus.OK
);
} @GetMapping("/listBooks")
public String listBooks(Model model) {
model.addAttribute("books", bookService.getAllBooks());
return "list";
} @GetMapping("/rest/books")
@ResponseBody
public ResponseEntity<List<Book>> restListBooks() {
List<Book> books = bookService.getAllBooks();
return new ResponseEntity<>(books, null,HttpStatus.OK);
} @GetMapping("/deleteBook")
public String deleteBook(Integer id) {
bookService.deleteBook(id);
return "redirect:listBooks";
} @DeleteMapping("/rest/books/{id}")
@ResponseBody
public ResponseEntity<Map<String,String>> restDelete(@PathVariable Integer id) {
bookService.deleteBook(id);
return new ResponseEntity<>(
Map.of("tip", "Book deleted successfully!"),null,
HttpStatus.OK
);
}
}

hello.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/5.3.5/css/bootstrap.min.css}"/>
<script type="text/javascript" th:src="@{/webjars/jquery/3.6.4/jquery.min.js}"></script>
</head>
<body>
<div class="container">
<div class="alert alert-primary" th:text="${tip}"></div>
<h2>添加图书</h2>
<form method="post" th:action="@{/addBook}">
<div class="form-group row">
<label for="title" class="col-sm-3 col-form-label">图书名:</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="title" name="title" placeholder="请输入图书名" required>
</div>
</div>
<div class="form-group row">
<label for="author" class="col-sm-3 col-form-label">作者:</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="author" name="author" placeholder="请输入作者" required>
</div>
</div>
<div class="form-group row">
<label for="price" class="col-sm-3 col-form-label">价格:</label>
<div class="col-sm-9">
<input type="number" step="0.1" class="form-control" id="price" name="price" placeholder="请输入价格" required>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6 text-right">
<button type="submit" class="btn btn-primary">添加</button>
</div>
<div class="col-sm-6">
<button type="reset" class="btn btn-primary">重设</button>
</div>
</div>
</form>
</div>
</body>
</html>

list.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" th:href="@{/webjars/bootstrap/5.3.5/css/bootstrap.min.css}"/>
<script type="text/javascript" th:src="@{/webjars/jquery/3.6.4/jquery.min.js}"></script>
</head>
<body>
<div class="container">
<h2>全部图书</h2>
<table class="table table-hover">
<thead>
<tr>
<th>图书名</th>
<th>作者</th>
<th>价格</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="book : ${books}">
<td th:text="${book.title}">书名</td>
<td th:text="${book.author}">作者</td>
<td th:text="${book.price}">价格</td>
<td>
<a th:href="@{/deleteBook(id=${book.id})}" class="btn btn-danger">删除</a>
</td>
</tr>
</tbody>
</table>
<div class="text-right" >
<a th:href="@{/}" class="btn btn-primary">添加图书</a>
</div>
</div>
</body>
</html>

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/ali2?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true
# default ddl auto
spring.jpa.generate-ddl=true

Book

@Entity
@Table(name = "book_inf") // 指定数据库表名
public class Book {
@Id // 主键
@Column(name = "book_id") // 指定数据库列名
@GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键
private Integer id; private String title; private String author; private Double price; public Book(){}
public Book(String title, String author, Double price) {
this.title = title;
this.author = author;
this.price = price;
} @Override
public String toString()
{
return "Book{" +
"id=" + id +
", title='" + title + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
} // Getters and Setters
public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getTitle() {
return title;
} public void setTitle(String title) {
this.title = title;
} public String getAuthor() {
return author;
} public void setAuthor(String author) {
this.author = author;
} public Double getPrice() {
return price;
} public void setPrice(Double price) {
this.price = price;
}
}

BookDao

// 继承CrudRepository接口,提供基本的CRUD操作
public interface BookDao extends CrudRepository<Book, Integer> {
}

编写单元测试

测试restful接口

引入单元测试依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
// @SpringBootTest修饰单元测试用例类
// webEnvironment属性指定了测试环境的web环境,这里使用随机端口。不需要知道具体端口号,Spring Boot会自动分配一个可用的端口
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RandomPortTest {
@Autowired
private TestRestTemplate restTemplate; public void testIndexRest() {
// 使用TestRestTemplate发送请求,测试RESTful接口
String response = restTemplate.getForObject("/rest", String.class);
Assertions.assertEquals("Hello, World from REST!", response);
} //这个注解告诉 JUnit 测试是参数化的,并且有测试值注入到其中。
@ParameterizedTest
@CsvSource({"西柚击,黧黑,129.0", "Java编程思想,布鲁斯·埃克尔,99.0"})
public void testRestAddBook(String title,String author, Double price) {
var book = new com.example.ali2.domain.Book(title, author, price);
// 使用TestRestTemplate发送请求
var response = restTemplate.postForObject("/rest/books",book, Map.class);
Assertions.assertEquals(response.get("tip"), "Book added successfully!");
} @Test
public void testRestList() {
// 使用TestRestTemplate发送请求,测试获取书籍列表
var result = restTemplate.getForObject("/rest/books", List.class);
result.forEach(System.out::println);
} @ParameterizedTest
@ValueSource(ints = {4,5})
public void testRestDelete(Integer id) {
// 使用TestRestTemplate发送请求,测试删除书籍
restTemplate.delete("/rest/books/{0}",id);
}
}

模拟web环境测试控制器

如果想测试普通的控制器处理方法,比如读取方法返回的ModelAndView,则可使用MockMVC。

// WebEnvironment.MOCK意味着启动模拟的web环境,不会启动实际的服务器
// WebEnvironment的默认值是WebEnvironment.Mock,其实不写也行
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
// @AutoConfigureMockMvc注解会自动配置MockMvc对象
@AutoConfigureMockMvc
public class MockEnvTest {
@Autowired
private MockMvc mockMvc; @Test
public void testIndex() throws Exception {
// 使用MockMvc发送请求,测试首页
var result = mockMvc.perform(MockMvcRequestBuilders.get(new URI("/"))).andReturn().getModelAndView();
Assertions.assertEquals(Map.of("tip", "Hello, World!"), result.getModel());
Assertions.assertEquals("hello", result.getViewName());
} @ParameterizedTest
@CsvSource({"疯狂Java讲义, 李刚, 129.0", "疯狂Android讲义, 李刚, 128.0"})
public void testAddBook(String title, String author, double price) throws Exception
{
// 测试addBook方法
var result = mockMvc.perform(MockMvcRequestBuilders.post(new URI("/addBook"))
.param("title", title)
.param("author", author)
.param("price", price + ""))
.andReturn().getModelAndView();
Assertions.assertEquals("redirect:listBooks", result.getViewName());
} @Test
public void testList() throws Exception
{
// 测试list方法
var result = mockMvc.perform(MockMvcRequestBuilders.get(new URI("/listBooks")))
.andReturn().getModelAndView();
Assertions.assertEquals("list", result.getViewName());
List<Book> books = (List<Book>) result.getModel().get("books");
books.forEach(System.out::println);
} @ParameterizedTest
@ValueSource(ints = {7, 8})
public void testDelete(Integer id) throws Exception
{
// 测试delete方法
var result = mockMvc.perform(MockMvcRequestBuilders.get("/deleteBook?id={0}", id))
.andReturn().getModelAndView();
Assertions.assertEquals("redirect:listBooks", result.getViewName());
}
}

测试业务组件

如果只是测试service或者Dao组件,则不需要启动web服务器。webEnvironment = WebEnvironment.NONE表示不启动web服务器

// WebEnvironment.NONE意味着不启动实际的web环境,适用于只测试服务层逻辑的情况
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class BookServiceTest {
@Autowired
private BookService bookService; @Test
public void testGetAllBooks()
{
bookService.getAllBooks().forEach(System.out::println);
} @ParameterizedTest
@CsvSource({"疯狂Java讲义, 李刚, 129.0", "疯狂Android讲义, 李刚, 128.0"})
public void testAddBook(String title, String author, double price)
{
var book = new Book(title, author, price);
Integer result = bookService.addBook(book);
System.out.println(result);
Assertions.assertNotEquals(result, 0);
} @ParameterizedTest
@ValueSource(ints = {9, 10})
public void testDeleteBook(Integer id)
{
bookService.deleteBook(id);
}
}

使用模拟组件

实际应用中,组件可能需要依赖其他组件来访问数据库,或者调用第三方接口,为避免这些不稳定因素影响单元测试效果,使用mock组件来模拟这些不稳定的组件。

比如BookDao未开发出来,如果对BookService测试,需要使用mock来模拟BookDao。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MockTest {
// 定义要测试的目标组件:BookService
@Autowired
private BookService bookService; // 使用@MockBean注解模拟BookDao组件
@MockBean
private BookDao bookDao; @Test
public void testGetAllBooks()
{
// 模拟bookDao的findAll()方法的返回值
BDDMockito.given(this.bookDao.findAll()).willReturn(
List.of(new Book("测试1", "李刚", 89.9),
new Book("测试2", "yeeku", 99.9)));
List<Book> result = bookService.getAllBooks();
Assertions.assertEquals(result.get(0).getTitle(), "测试1");
Assertions.assertEquals(result.get(0).getAuthor(), "李刚");
Assertions.assertEquals(result.get(1).getTitle(), "测试2");
Assertions.assertEquals(result.get(1).getAuthor(), "yeeku");
}
}

SpringBoot--简单入门的更多相关文章

  1. springboot简单入门笔记

    一.Spring Boot 入门 1.Spring Boot 简介 简化Spring应用开发的一个框架: 整个Spring技术栈的一个大整合: J2EE开发的一站式解决方案: 2.微服务 2014,m ...

  2. Spring学习之路-SpringBoot简单入门

    简单讲SpringBoot是对spring和springMVC的二次封装和整合,新添加了一些注解和功能,不算是一个新框架. 学习来源是官方文档,虽然很详细,但是太墨迹了… 地址:https://doc ...

  3. SpringBoot简单入门

    一.创建SpringBoot项目 1.创建maven项目,pom引入springboot父级启动器(starter)依赖: <?xml version="1.0" encod ...

  4. springboot 学习之路 1(简单入门)

    目录:[持续更新.....] spring 部分常用注解 spring boot 学习之路1(简单入门) spring boot 学习之路2(注解介绍) spring boot 学习之路3( 集成my ...

  5. SpringData 基于SpringBoot快速入门

    SpringData 基于SpringBoot快速入门 本章通过学习SpringData 和SpringBoot 相关知识将面向服务架构(SOA)的单点登录系统(SSO)需要的代码实现.这样可以从实战 ...

  6. springboot 简单搭建

    springboot的入门请参考:https://blog.csdn.net/hanjun0612/article/details/81538449 这里就简单看下搭建: 一,看一下项目结构: 创建一 ...

  7. 01-项目简介Springboot简介入门配置项目准备

    总体课程主要分为4个阶段课程: ------------------------课程介绍------------------------ 01-项目简介Springboot简介入门配置项目准备02-M ...

  8. springcloud+eureka简单入门案例

    springcloud+eureka简单入门案例 一.服务提供者 直接提供服务,入门案例没有特别要设置的地方,注意下端口,由于要启动多个服务,可能会冲突 配置文件(src/main/resources ...

  9. springboot简单介绍

    1.springboot简单介绍 微服务架构 Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程. 该框架使用了特定的方 ...

  10. SPRING-BOOT系列之SpringBoot快速入门

    今天 , 正式来介绍SpringBoot快速入门 : 可以去如类似 https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/refer ...

随机推荐

  1. 探秘Transformer系列之(21)--- MoE

    探秘Transformer系列之(21)--- MoE 目录 探秘Transformer系列之(21)--- MoE 0x00 概要 0x01 前置知识 1.1 MoE出现的原因 1.1.1 神经网络 ...

  2. eolinker校验规则之 Json结构定位:返回结果校验的方法和案例(父参、子参内容校验)

    如下图,订单编号的参数在data父字段内 Eolinker返参校验的写法就需要有些变化 先写Data父参,添加子字段,再写子参 预期结果不支持全局变量 可通过添加绑定,绑定前一个接口返回参数,进行匹配

  3. Elasticsearch7.6.1配套安装包自取

    包含Elasticsearch,ik分词器,kibana 7.6.1安装包自取:https://pan.baidu.com/s/1Y6XdDOzqIzI2qerOODQHmg提取码:5nm4

  4. 解决多个if-else的方案

    参考链接: 遇到大量if记住下面的口诀: 互斥条件表驱动 嵌套条件校验链 短路条件早return 零散条件可组合 解释: 互斥条件,表示几个条件之间是冲突的,不可能同时达成的.比如说一个数字,它不可能 ...

  5. .NET Core短信验证(分布式session)

    一.手机短信验证码登录过程 1.构造手机验证码,需要生成一个6位的随机数字串: 2.找短信平台获取使用接口向短信平台发送手机号和验证码,然后短信平台再把验证码发送到制定手机号上 3.将手机号验证码.操 ...

  6. CTF实验吧加了料的报错注入

    实验吧地址 http://ctf5.shiyanbar.com/web/baocuo/index.php F12审查元素发现源码中的提示是这样一整句的查询 基本确定此题为一个SQL注入 /# = un ...

  7. 1K star!这个开源项目让短信集成简单到离谱,开发效率直接翻倍!

    嗨,大家好,我是小华同学,关注我们获得"最新.最全.最优质"开源项目和高效工作学习方法 "让简单的事情回归简单的本质" -- SMS4J 项目宣言 SMS4J ...

  8. Wan2.1 t2v模型Lora Fine-Tune

    Wan2.1 t2v模型Lora Fine-Tune 1. Wan2.1模型 Wan2.1是由阿里巴巴开源的AI视频生成大模型,具备强大的视觉生成能力,支持文本到视频(T2V)和图像到视频(I2V)任 ...

  9. 从 UEFI 启动到双系统——记一次双系统 Linux 分区迁移

    前言 我的台式电脑上,装了 Windows 和 Linux 双系统. 我有两块 1 TB 硬盘,就把它们叫作硬盘 0 和硬盘 1 吧.最开始的时候,硬盘 0 上装了 Windows 系统,而我的数据分 ...

  10. Nacos源码—9.Nacos升级gRPC分析四

    大纲 10.gRPC客户端初始化分析 11.gRPC客户端的心跳机制(健康检查) 12.gRPC服务端如何处理客户端的建立连接请求 13.gRPC服务端如何映射各种请求与对应的Handler处理类 1 ...