简介

本质上说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. 线性探测法的查找函数 作者 DS课程组 单位 浙江大学

    虽然但是,我真的讨厌c语言这样一大坨typedef命名来命名去的,很多时候其实我们会写,但是看不懂这个存储结构 函数的接口定义 Position Find( HashTable H, ElementT ...

  2. python批量下载网易云音乐文件到本地

    现在听歌大多数只支持在线听,下载要钱,没网络就白搭了.好吧,用技术手段解决免费.下载.批量等一些列问题 整个脚本的逻辑和流程是,把歌曲地址都存在一个txt中,然后循环每次取一条链接,分析链接对应歌曲的 ...

  3. 揭秘AI编排爆火真相:从"人工智障"到"真正智能"的关键一跃

    当行业还在追捧大模型参数竞赛时,领先团队早已转向新战场: AI编排(Agent Orchestration)-- 这个方向是 AI 技术"从聊天到做事"的关键突破口. 1.为什么说 ...

  4. 使用sealos快速搭建kubernetes集群!!!

    什么是sealos? Sealos 是一款基于 Kubernetes 的轻量级操作系统,专为云原生环境设计,主要用于快速部署和管理 Kubernetes 集群.它采用"容器化内核" ...

  5. Typora+PicGo+OSS搭建博客图床

    Typora+PicGo+OSS搭建博客图床 Q:为什么需要搭建博客图床呢? A:首先,博客园平台支持markdown语法进行创作 其次,最近想在微信公众号同步文章,在思考有没有多平台快捷发布的办法, ...

  6. 操作系统:设备I/O -- 如何在内核中注册设备?

    在上节课里,我们对设备进行了分类,建立了设备与驱动的数据结构,同时也规定了一个驱动程序应该提供哪些标准操作方法,供操作系统内核调用.这相当于设计了行政部门的规章制度,一个部门叫什么,应该干什么,这些就 ...

  7. 阿里云手工配置Nginx

    Nginx服务器是网站反向代理,负载均衡,以及动静分离的神器,由于是第一次在阿里云上配置这个,花费了大半天,终于配置成功了.下面简要的介绍下其流程,以备后面又搞忘了.前提是自动装装不上 购买阿里云服务 ...

  8. wso2~自定义event-publisher

    自定义event/publishers的步骤 介绍 event/publishers功能位于carbon平台的event菜单,选择publishers菜单项即可打开发布者配置列表,你可以添加自定义的发 ...

  9. 阿里云ECS(Ubuntu 22.04 LTS)优化脚本

    #!/bin/bash set -e echo " Updating system..." apt update -y && apt upgrade -y echo ...

  10. [译]MIT6.824_1.1分布式系统介绍——驱动力与挑战

    这是6.824分布式系统课程,我会开始用简明的介绍我所认为的分布式系统. 分布式系统的核心是通过网络以完成一致任务的一组协作计算机. 因此我们将在本课程中重点介绍各种实例,例如大型网站的存储或MapR ...