Spring Boot

zhenxi
2022-07-31 / 0 评论 / 2 阅读 / 正在检测是否收录...

1 Spring Boot

1.1 Spring Boot概述

Spring Boot设计目的是用来简化Spring应用的创建、运行、调试、部署等。使用Spring Boot可以做到专注于Spring应用的开发,而无需过多关注XML的配置。Spring Boot使用“约定优于配置”的理念,简单来说,它提供了一堆依赖打包,并已经按照使用习惯解决了依赖问题。

1.2 Spring Boot的核心功能

  • 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行。
  • 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目。
  • 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置。
  • 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置。
  • 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查。
  • 无代码生成和xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置。

1.3 SpringBootB的相关好处

  • 使用 Spring 项目引导页面可以在⼏秒构建⼀个项⽬;
  • 方便对外输出各种形式的服务,如 REST API、WebSocket、Web、Streaming、Tasks;
  • 非常简洁的安全策略集成;
  • ⽀持关系数据库和⾮关系数据库;
  • ⽀持运行期内嵌容器,如 Tomcat、Jetty;
  • 强⼤的开发包,⽀持热启动;
  • ⾃动管理依赖;
  • ⾃带应⽤监控;
  • ⽀持各种 IED,如 IntelliJ IDEA 、NetBeans。

和Spirng程序相比,SpringBoot程序在开发的过程中各个层面均具有优势

类配置文件SpringSpringBoot
pom文件中的坐标手工添加勾选添加
web3.0配置类手工制作
Spring/SpringMVC配置类手工制作
控制器手工制作手工制作

2 Spring Boot快速创建

2.1 使用Spring Initializr引导页面进行创建

创建新模块,选择Spring Initializr,并配置模块相关基础信息

image-20220823184435568

选择当前模块需要使用的技术集

image-20220823184926208

开发控制器类

package com.liu.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping
    @RequestMapping("quick1")
    public String findUsers() {
        System.out.println("Spring boot is running");
        return "Spring is running";
    }
}

运行自动生成的Application类

image-20220823190656687

2.2 进入Spring Boot官网进行创建

点击Spring Initializr后进入到创建SpringBoot程序的界面上,下面是输入信息的过程,和前面的一样,只是界面变了而已,根据自己的要求,在左侧选择对应信息和输入对应的信息即可。右侧的ADD DEPENDENCIES用于选择使用何种技术,和之前勾选的Spring WEB是在做同一件事,仅仅是界面不同而已,点击后打开网页版的技术选择界面

image-20220823201834617

所有信息设置完毕后,点击下面左侧按钮,生成一个文件包,保存后得到一个压缩文件,这个文件打开后就是创建的SpringBoot工程文件夹。解压缩此文件后,得到工程目录,在Idea中导入即可使用,和之前创建的东西完全一样。下面就可以自己创建一个Controller测试一下是否能用了。

image-20220823202345282

2.3 使用阿里云地址创建Spring Boot项目

创建新模块,选择Spring Initializr,选择Server URL为start.aliyun.com,并配置模块相关基础信息

image-20220823202653606

选择当前模块需要使用的技术集

image-20220823202727762

运行自动生成的Application类进行测试

image-20220823190656687

3 Spring Boot简介

3.1 parent:进行版本的统一管理

①parent概述

SpringBoot为了解决最合理的依赖版本配置方案,于是将所有的技术版本的常见使用方案都给开发者整理了出来,以后开发者使用时直接用它提供的版本方案,就不用担心冲突问题了,相当于SpringBoot做了无数个技术版本搭配的列表,这个技术搭配列表的名字叫做parent

parent自身具有很多个版本,每个parent版本中包含有几百个其他技术的版本号,不同的parent间使用的各种技术的版本号有可能会发生变化。当开发者使用某些技术时,直接使用SpringBoot提供的parent就行了,由parent帮助开发者统一的进行各种技术的版本管理。

②Spring Boot项目中引用的parent

项目中的pom.xml中继承了一个坐标,打开后可以查阅到其中又继承了一个坐标,这个坐标中定义了两组信息。第一组是各式各样的依赖版本号属性,下面列出依赖版本属性的局部,可以看的出来,定义了若干个技术的依赖版本号;第二组是各式各样的的依赖坐标信息,可以看出依赖坐标定义中没有具体的依赖版本号,而是引用了第一组信息中定义的依赖版本属性值。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.3</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

GIF 2022-8-24 16-21-17

③parent功能

  • 定义了 Java 编译版本为 1.8 。
  • 使用 UTF-8 格式编码。
  • 继承自 spring-boot-dependencies,这个里边定义了依赖的版本,也正是因为继承了这个依赖,所以我们在写4、依赖时才不需要写版本号。
  • 执行打包操作的配置。
  • 自动化的资源过滤。
  • 自动化的插件配置。
  • 针对 application.properties 和 application.yml 的资源过滤,包括通过 profile 定义的不同环境的配置文件,例如 application-dev.properties 和 application-dev.yml。

3.2 starter:减少依赖配置

①starter概述

SpringBoot把所有的技术使用的固定搭配格式都给开发出来,开发者使用的时候,就不用一次写一堆依赖了,直接用Spring Boot做好的这个东西就好了,对于这样的固定技术搭配,SpringBoot给它起了个名字叫做starter。starter定义了使用某种技术时对于依赖的固定搭配格式,也是一种最佳解决方案,使用starter可以帮助开发者减少依赖配置

②Spring Boot项目中引用的starter

项目中的pom.xml定义了使用SpringMVC技术,但是并没有写SpringMVC的坐标,而是添加了一个名字中包含starter的依赖,在spring-boot-starter-web中又定义了若干个具体依赖的坐标。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

③starter与parent的区别

  • starter是一个坐标中定了若干个坐标,以前写多个的,现在写一个,是用来减少依赖配置的书写量的
  • parent是定义了几百个依赖版本号,以前写依赖需要自己手工控制版本,现在由SpringBoot统一管理,这样就不存在版本冲突了,是用来减少依赖冲突的

3.3 引导类

运行这个类就可以启动SpringBoot工程:

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

SpringBoot本身是为了加速Spring程序的开发的,而Spring程序运行的基础是需要创建自己的Spring容器对象(IoC容器)并将所有的对象交给Spring的容器管理,也就是一个一个的Bean。当前这个类运行后就会产生一个Spring容器对象,并且可以将这个对象保存起来,通过容器对象直接操作Bean。

@SpringBootApplication
public class Springboot01QuickstartApplication {
   public static void main(String[] args) {
      ConfigurableApplicationContext applicationContext = SpringApplication.run(Springboot01QuickstartApplication.class, args);
      UserController userController = applicationContext.getBean(UserController.class);
      System.out.println(userController);
   }
}

image-20220824163650282

通过上述操作不难看出,其实SpringBoot程序启动还是创建了一个Spring容器对象。这个类在SpringBoot程序中是所有功能的入口,称这个类为引导类。作为一个引导类最典型的特征就是当前类上方声明了一个注解@SpringBootApplication打开该注解可以发现,这个类就是学习Spring的注解核心配置类。

3.4 内嵌tomcat

打开查看web的starter,发现有一个tomcat的starter,这里面有一个核心的坐标,tomcat-embed-core,叫做tomcat内嵌核心。就是这个东西把tomcat功能引入到了我们的程序中。

更换内嵌Tomcat

SpringBoot提供了3款内置的服务器

  • tomcat(默认):apache出品,粉丝多,应用面广,负载了若干较重的组件
  • jetty:更轻量级,负载性能远不及tomcat
  • undertow:负载性能勉强跑赢tomcat

想用哪个,加个坐标就OK。前提是把tomcat排除掉,因为tomcat是默认加载的。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jetty</artifactId>
    </dependency>
</dependencies>

现在就已经成功替换了web服务器,核心思想就是用什么加入对应坐标就可以了。如果有starter,优先使用starter。

4 Rest风格,RestFul开发

在学习Spring MVC时学过REST风格,但是讲课老师讲的不太深入,对于一些注解并没有进行讲解。于是重新复习一遍。

4.1 RESTFUL概述

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP可以使用 XML 格式定义或 JSON 格式定义。最常用的数据格式是JSON。由于JSON能直接被JavaScript读取,所以,使用JSON格式的REST风格的API具有简单、易读、易用的特点

  • REST 是 Representational State Transfer 的缩写,如果一个架构符合 REST 原则,就称它为 RESTful 架构
  • RESTful 架构可以充分的利用 HTTP 协议的各种功能,是 HTTP 协议的最佳实践。
  • RESTful API 是一种软件架构风格、设计风格,可以让软件更加清晰,更简洁,更有层次,可维护性更好

4.2 RESTFUL API 请求设计

请求方式含义
GET(SELECT)从服务器取出资源(一项或多项)
POST(CREATE)在服务器新建一个资源
PUT(UPDATE)在服务器更新资源(更新完整资源)
PATCH(UPDATE)在服务器更新资源, PATCH更新个别属性
DELETE(DELETE)从服务器删除资源

image-20220824165423905

4.3 在Spring MVC中使用RESTFUL开发设计

@Controller
public class ZoosController {
    
    @RequestMapping(value = "/zoos",method = RequestMethod.GET)
    @ResponseBody
    public String findAll(){
        System.out.println("ZoosController.findAll");
        return "zoosController.findAll";
    }
    
    @RequestMapping(value = "/zoos/{id}",method = RequestMethod.GET)
    @ResponseBody
    public String findOne(@PathVariable Integer id){
        System.out.println("ZoosController.findOne");
        return "zoosController.findOne";
    }
    
    @RequestMapping(value = "/zoos",method = RequestMethod.PUT)
    @ResponseBody
    public String update(@RequestBody Zoo zoo){
        System.out.println("ZoosController.update");
        return "zoosController.update";
    }

    @RequestMapping(value = "/zoos/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
        System.out.println("ZoosController.delete");
        return "zoosController.delete";
    }

    @RequestMapping(value = "/zoos",method = RequestMethod.POST)
    @ResponseBody
    public String insert(){
        System.out.println("ZoosController.insert");
        return "zoosController.insert";
    }
}

简化开发相关注解

注解作用
@RestController由 @Controller + @ResponseBody组成(返回 JSON 数据格式)
@PathVariableURL 中的 {xxx} 占位符可以通过@PathVariable(“xxx“) 绑定到控制器处理方法的形参中
@RequestMapping注解用于请求地址的解析,是最常用的一种注解
@GetMapping查询请求
@PostMapping添加请求
@PutMapping更新请求
@DeleteMapping删除请求
@RequestParam将请求参数绑定到你控制器的方法参数上(是springmvc中接收普通参数的注解)
@RestController
@RequestMapping("/zoos")
@ResponseBody
public class ZoosController {
    @GetMapping
    public String findAll(){
        System.out.println("ZoosController.findAll");
        return "zoosController.findAll";
    }
    @GetMapping(value = "/{id}")
    public String findOne(@PathVariable Integer id){
        System.out.println("ZoosController.findOne");
        return "zoosController.findOne";
    }
    @PutMapping
    public String update(@RequestBody Zoo zoo){
        System.out.println("ZoosController.update");
        return "zoosController.update";
    }
    @DeleteMapping(value = "/{id}")
    public String delete(@PathVariable Integer id){
        System.out.println("ZoosController.delete");
        return "zoosController.delete";
    }

    @PostMapping
    public String insert(){
        System.out.println("ZoosController.insert");
        return "zoosController.insert";
    }
}

5 SpringBoot基础配置

5.1 属性配置

SpringBoot通过配置文件application.properties就可以修改默认的配置,properties格式的文件书写规范是key=value。

  1. SpringBoot程序可以在application.properties文件中进行属性配置。
  2. application.properties文件中只要输入要配置的属性关键字就可以根据提示进行设置。
  3. SpringBoot将配置信息集中在一个文件中写,不管你是服务器的配置,还是数据库的配置,总之都写在一起,逃离一个项目十几种配置文件格式的尴尬局面。

更改端口号

# 服务器的端口配置
server.port=80

image-20220824180043817

关闭运行日志图表(banner)

# 关闭banner
spring.main.banner-mode=off
# 修改banner
spring.banner.image.location=wallhaven-2879mg.png

image-20220824180536895

设置运行日志的显示级别

# 日志
logging.level.root = error

image-20220824180958804

5.2 配置文件分类

SpringBoot除了支持properties格式的配置文件,还支持另外两种格式的配置文件。分别如下:

  • application.properties(properties格式)
server.port=80
  • application.yml(yml格式)
server:
  port: 81
  • application.yaml(yaml格式)
server:
  port: 82

仔细看会发现yml格式和yaml格式除了文件名后缀不一样,格式完全一样,是这样的,yml和yaml文件格式就是一模一样的,只是文件后缀不同,所以可以合并成一种格式来看。

5.3 配置文件优先级

其实三个文件如果共存的话,谁生效说的就是配置文件加载的优先级别。

application.properties  >  application.yml  >  application.yaml

配置文件间的加载优先级 properties(最高)> yml > yaml(最低),不同配置文件中相同配置按照加载优先级相互覆盖,不同配置文件中不同配置全部保留。

5.4 yaml文件的使用

YAML(YAML Ain't Markup Language),一种数据序列化格式。具有容易阅读、容易与脚本语言交互、以数据为核心,重数据轻格式的特点。

常见的文件扩展名有两种:

  • .yml格式(主流)
  • .yaml格式

对于文件自身在书写时,具有严格的语法格式要求,具体如下:

  1. 大小写敏感
  2. 属性层级关系使用多行描述,每行结尾使用冒号结束
  3. 使用缩进表示层级关系,同层级左侧对齐,只允许使用空格(不允许使用Tab键)
  4. 属性值前面添加空格(属性名与属性值之间使用冒号+空格作为分隔)
  5. 号 表示注释

常见的数据书写格式:

boolean: TRUE                          #TRUE,true,True,FALSE,false,False均可
float: 3.14                            #6.8523015e+5  #支持科学计数法
int: 123                               #0b1010_0111_0100_1010_1110    #支持二进制、八进制、十六进制
null: ~                                #使用~表示null
string: HelloWorld                  #字符串可以直接书写
string2: "Hello World"              #可以使用双引号包裹特殊字符
date: 2018-02-17                    #日期必须使用yyyy-MM-dd格式
datetime: 2018-02-17T15:02:31+08:00  #时间和日期之间使用T连接,最后使用+代表时区
subject:
    - Java
    - 前端
    - 大数据
enterprise:
    name: itcast
    age: 16
    subject:
        - Java
        - 前端
        - 大数据
likes: [王者荣耀,刺激战场]            #数组书写缩略格式
users:                             #对象数组格式一
  - name: Tom
       age: 4
  - name: Jerry
    age: 5
users:                             #对象数组格式二
  -  
    name: Tom
    age: 4
  -   
    name: Jerry
    age: 5                
users2: [ { name:Tom , age:4 } , { name:Jerry , age:5 } ]    #对象数组缩略格式

5.5 yaml文件数据读取

读取单一数据

yaml中保存的单个数据,可以使用Spring中的注解直接读取,使用@Value可以读取单个数据,属性名引用方式:${一级属性名.二级属性名……}

address: 河南

user:
  userName: admin
  password: 123456

likes:
  - game
  - music
  - video

baseDir: c:\windows\system32

tempDir: ${baseDir}\temp
@RestController
@RequestMapping("/user")
public class UserController {
    @Value("${address}")
    private String address;

    @Value("${user.userName}")
    private String userName;

    @Value("${likes[1]}")
    private String likes1;

    @Value("${tempDir}")
    private String tempDir;

    @GetMapping("/quick1")
    public String findUsers() {
        System.out.println("Spring boot is running");
        System.out.println(address);
        System.out.println(userName);
        System.out.println(likes1);
        System.out.println(tempDir);
    }

读取全部数据

SpringBoot提供了一个对象,能够把所有的数据都封装到这一个对象中,这个对象叫做Environment,使用自动装配注解可以将所有的yaml数据封装到这个对象中。

@RestController
@RequestMapping("/user")
public class UserController {
    //自动装配,把所有对象加载到environment对象中
    @Autowired
    private Environment environment;

    @GetMapping("/quick1")
    public String findUsers() {
        System.out.println("Spring boot is running");
        //使用environment获得yMl文件中的信息
        String address = environment.getProperty("address");
        System.out.println(address);
    }
}

读取对象数据

单一数据读取书写比较繁琐,全数据封装又封装的太厉害了,每次拿数据还要一个一个的getProperties(),总之用起来都不是很舒服。由于Java是一个面向对象的语言,很多情况下,我们会将一组数据封装成一个对象。SpringBoot也提供了可以将一组yaml对象数据封装一个Java对象的操作。

①首先定义一个对象,并将该对象纳入Spring管控的范围,也就是定义成一个bean,然后使用注解@ConfigurationProperties指定该对象加载哪一组yaml中配置的信息。

@Component
@ConfigurationProperties(prefix = "datasource")
public class MyDataSource {
    private String driver;
    private String url;
    private String username;
    private String password;

    @Override
    public String toString() {
        return "MyDataSource{" +
                "driver='" + driver + '\'' +
                ", url='" + url + '\'' +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

这个@ConfigurationProperties必须告诉他加载的数据前缀是什么,这样当前前缀下的所有属性就封装到这个对象中。记得数据属性名要与对象的变量名一一对应啊,不然没法封装。其实以后如果你要定义一组数据自己使用,就可以先写一个对象,然后定义好属性,下面到配置中根据这个格式书写即可。

datasource:
  driver: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost
  username: root
  password: 131411

image-20220825110436386

5.6 yaml配置中遇到的问题

①配置yml文件时,设置键名为大写报错:

dataSource:
  driver: com.mysql.cj.jdbc.Driver
  url: jdbc:mysql://localhost
  username: root
  password: 131411

image-20220825102311972

Invalid characters: 'S'
    Bean: userController
    Reason: Canonical names should be kebab-case ('-' separated), lowercase alpha-numeric characters and must start with a letter
无效字符:'S' 
Bean:userController 
原因:规范名称应为 kebab-case('-' 分隔)、小写字母数字字符且必须以字母开头

6 Spring Boot整合Junit

Spring整合JUnit的制作方式

@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class TransactionManagerTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void test() {
        accountService.transfer();
    }
}

@RunWith是设置Spring专用于测试的类运行器,简单说就是Spring程序执行程序有自己的一套独立的运行程序的方式,不能使用JUnit提供的类运行方式了,必须指定一下。

@ContextConfiguration是用来设置Spring核心配置文件或配置类的,简单说就是加载Spring的环境你要告诉Spring具体的环境配置是在哪里写的。

SpringBoot就抓住上述两条没有技术含量的内容书写进行开发简化,能走默认值的走默认值,能不写的就不写,具体格式如下:

①导入test的starter坐标

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

②使用一个注解@SpringBootTest替换了前面两个注解。

@SpringBootTest
class SpringDemo1ApplicationTests {

    @Autowired
    private BookMapper bookMapper;

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

}

7 Spring Boot整合Mybatis

创建模块时勾选要使用的技术,MyBatis,由于要操作数据库,还要勾选对应数据库

image-20220825115055175

配置数据源相关信息

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 131411

使用Lombok创建实体类

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String address;
}

创建映射接口

@Mapper
public interface UserMapper {
    @Select("select * from tb_user")
    List<User> findAll();
    @Insert("insert into tb_user(username, password, gender, address) values (#{username},#{password},#{gender},#{address})")
    void insert(User user);
    @Update("update tb_user set username = #{username} where id=#{id}")
    int update(@Param("username") String username, @Param("id") int id);
    @Delete("delete from tb_user where id=#{id}")
    void delete(int id);
}

进行测试

@SpringBootTest
class SpringBootMybatisApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    void userInserts() {
        User user = new User();
        user.setUsername("刘畅");
        user.setPassword("1314");
        user.setGender("男");
        user.setAddress("河南");
        userMapper.insert(user);
    }
    @Test
    void userUpdates() {
        userMapper.update("六珍惜", 1);
    }

    @Test
    void userDeletes() {
        userMapper.delete(1);
    }
}

部分测试结果

image-20220825120513045

8 Spring Boot整合Mybatis-plus

导入对应的starter

<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>

配置数据源相关信息

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 131411

创建映射接口

@Mapper
public interface UserMapper extends BaseMapper<User> {

}

进行测试

@SpringBootTest
class SpringBootMybatisplusApplicationTests {

    @Autowired
    private UserMapper userMapper;
    @Test
    void contextLoads() {
        List<User> userList = userMapper.selectList(null);
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    void findUserById(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
}

9 Spring Boot整合druid数据源

导入对应的starter

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.11</version>
</dependency>

配置数据源相关信息

spring:
 datasource:
  druid:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: 131411

创建映射接口

@Mapper
public interface UserMapper {
    @Select("select * from tb_user")
    List<User> findAll();
    @Insert("insert into tb_user(username, password, gender, address) values (#{username},#{password},#{gender},#{address})")
    void insert(User user);
    @Update("update tb_user set username = #{username} where id=#{id}")
    int update(@Param("username") String username, @Param("id") int id);
    @Delete("delete from tb_user where id=#{id}")
    void delete(int id);
}

进行测试

@SpringBootTest
class SpringBootMybatisApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        List<User> userList = userMapper.findAll();
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    void userInserts() {
        User user = new User();
        user.setUsername("刘畅");
        user.setPassword("1314");
        user.setGender("男");
        user.setAddress("河南");
        userMapper.insert(user);
    }
    @Test
    void userUpdates() {
        userMapper.update("六珍惜", 1);
    }

    @Test
    void userDeletes() {
        userMapper.delete(1);
    }
}

学习总结:

本周学习进度有些慢,本周主要学习了Spring Boot基础篇的相关知识。通过本次的学习,对SpringMVC的相关知识更加的熟悉;通过进行SSM整合,使用注解开发Spring程序更加得心应手;对于Spring Boot的学习,必须要打牢Spring的基础,只要有牢固的Spring基础,SpringBoot的开发更加的简单。

下周学习计划:

完成Spring Boot基础篇、运维实用篇等课程。由于临近开学,需要整理解决一些开学事务,导致学习时间有所减少,SpringBoot的课程在开学前无法学习完毕,不过在开学后会尽快完成任务,达到Java全栈初级工程师水平。继续努力!

SpringBoot学习笔记 2

1 基于Spring Boot整合SSMP

1.1 模块创建

image-20220825160014496

导入MyBatisPlus与Druid对应的starter,当然mysql的驱动不能少

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

1.2 数据层开发

①创建实体类:User

@Data
public class User {
    private Integer id;
    private String username;
    private String password;
    private String gender;
    private String address;
}

②配置数据源

server:
  port: 80
  
spring:
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test
      username: root
      password: 131411
      
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_

③编写映射接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

④测试

@SpringBootTest
public class UserMapperTest {
    @Autowired
    private UserMapper userMapper;

    @Test
    public void test(){
        List<User> userList = userMapper.selectList(null);
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    public void test2(){
        User user = userMapper.selectById(1);
        System.out.println(user);
    }
    @Test
    public void test3(){
        User user = new User();
        user.setUsername("齐大");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("齐齐哈尔");
        userMapper.insert(user);
    }
    @Test
    public void test4(){
        userMapper.deleteById(191561735);
    }

    @Test
    public void test5(){
        User user = new User();
        user.setId(15);
        user.setUsername("郑大");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("河南郑州");
        userMapper.updateById(user);
    }
}

发现问题一:自动增长的Id值太大。

image-20220825162115469

解决方式一:通过配置的方式:

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
      id-type: auto   #设置主键id字段的生成策略为参照数据库设定的策略,当前数据库设置id生成策略为自增

解决方式二:通过注解:

在实体类User类的id属性上添加此注解。

@TableId(value = "id", type = IdType.AUTO)
private Integer id;

image-20220825165501671

发现问题二:控制台没有打印SQL语句,不利于查看

解决方式:通过配置的形式就可以查阅执行期SQL语句,配置如下:

mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
      id-type: auto
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启打印日志

image-20220825170304679

⑤分页查询测试

前面仅仅是使用了MP提供的基础CRUD功能,实际上MP给我们提供了几乎所有的基础操作。

其中selectPage方法需要传入一个封装分页数据的对象,可以通过new的形式创建这个对象,当然这个对象也是MP提供的。创建此对象时就需要指定分页的两个基本数据

  • 当前显示第几页
  • 每页显示几条数据

可以通过创建Page对象时利用构造方法初始化这两个数据

IPage page = new Page(2,5);

将该对象传入到查询方法selectPage后,可以得到查询结果,但是我们会发现当前操作查询结果返回值仍然是一个IPage对象。

IPage page = bookDao.selectPage(page, null);

原来这个IPage对象中封装了若干个数据,而查询的结果作为IPage对象封装的一个数据存在的,可以理解为查询结果得到后,又塞到了这个IPage对象中,其实还是为了高度的封装,一个IPage描述了分页所有的信息。下面5个操作就是IPage对象中封装的所有信息了。

@Test
public void test6() {
    IPage<User> page = new Page<User>(1,5);
    userMapper.selectPage(page,null);
    System.out.println(page.getCurrent());
    System.out.println(page.getSize());
    System.out.println(page.getPages());
    System.out.println(page.getTotal());
    System.out.println(page.getRecords());
}

到这里就知道这些数据如何获取了,但是当你去执行这个操作时,你会发现并不像我们分析的这样,实际上这个分页当前是无效的。为什么这样呢?这个要源于MP的内部机制。

对于MySQL的分页操作使用limit关键字进行,而并不是所有的数据库都使用limit关键字实现的,这个时候MP为了制作的兼容性强,将分页操作设置为基础查询操作的升级版,你可以理解为IPhone6与IPhone6S-PLUS的关系。

基础操作中有查询全部的功能,而在这个基础上只需要升级一下(PLUS)就可以得到分页操作。所以MP将分页操作做成了一个开关,你用分页功能就把开关开启,不用就不需要开启这个开关。而我们现在没有开启这个开关,所以分页操作是没有的。这个开关是通过MP的拦截器的形式存在的,其中的原理这里不分析了,有兴趣的小伙伴可以学习MyBatisPlus这门课程进行详细解读。具体设置方式如下

定义MP拦截器并将其设置为Spring管控的bean

@Configuration
public class MPConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

上述代码第一行是创建MP的拦截器栈,这个时候拦截器栈中没有具体的拦截器,第二行是初始化了分页拦截器,并添加到拦截器栈中。如果后期开发其他功能,需要添加全新的拦截器,按照第二行的格式继续add进去新的拦截器就可以了。

image-20220825195311035

⑥按条件查询

模糊匹配对应的操作,由like条件书写变为了like方法的调用。

@Test
public void test7() {
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("username","刘");
    userMapper.selectList(queryWrapper);
}

第一句QueryWrapper对象是一个用于封装查询条件的对象,该对象可以动态使用API调用的方法添加条件,最终转化成对应的SQL语句。第二句就是一个条件了,需要什么条件,使用QueryWapper对象直接调用对应操作即可。比如做大于小于关系,就可以使用lt或gt方法,等于使用eq方法,等等。

这组API使用还是比较简单的,但是关于属性字段名的书写存在着安全隐患,比如查询字段name,当前是以字符串的形态书写的,万一写错,编译器还没有办法发现,只能将问题抛到运行器通过异常堆栈告诉开发者,不太友好。MP针对字段检查进行了功能升级,全面支持Lambda表达式,就有了下面这组API。由QueryWrapper对象升级为LambdaQueryWrapper对象,这下就解决了上述问题的出现。

@Test
public void test8() {
    String name = "liu";
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.like(name!=null,User::getUsername,name);
    userMapper.selectList(queryWrapper);
}

image-20220825195839766

1.3 业务层开发

传统业务层开发

①业务层接口定义

public interface UserService {
    Boolean save(User user);
    Boolean update(User user);
    Boolean delete(Integer id);
    User findById(Integer id);
    List<User> getUsers();
    IPage<User> getPages(int currentPage, int pageSize);
}

②业务层实现类如下,转调数据层即可

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public Boolean save(User user) {
        return userMapper.insert(user) > 0;
    }

    @Override
    public Boolean update(User user) {
        return userMapper.updateById(user) > 0;
    }

    @Override
    public Boolean delete(Integer id) {
        return userMapper.deleteById(user) > 0;
    }

    @Override
    public User findById(Integer id) {
        return userMapper.selectById(id);
    }

    @Override
    public List<User> getUsers() {
        return userMapper.selectList(null);
    }

    @Override
    public IPage<User> getPages(int currentPage, int pageSize) {
        Page<User> page = new Page<>();
        return userMapper.selectPage(page,null);
    }
}

③对业务层接口进行测试

@SpringBootTest
public class UserServiceTest {

    @Autowired
    private UserService userService;

    @Test
    public void test() {
        System.out.println(userService.findById(1));
    }
    @Test
    public void test2() {
        IPage<User> page = userService.getPages(1, 5);
        System.out.println(page.getRecords());
    }
    @Test
    public void test3() {
        List<User> userList = userService.getUsers();
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    public void test4() {
        User user = new User();
        user.setId(15);
        user.setUsername("郑大");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("河南郑州");
        userService.save(user);
    }
    @Test
    public void test5() {
        User user = new User();
        user.setId(15);
        user.setUsername("he");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("河南郑州");
        userService.update(user);
    }
}

使用mybatis-plus业务层快速开发

MP技术不仅提供了数据层快速开发方案,业务层MP也给了一个通用接口,实际开发慎用。

①业务层接口快速开发

public interface IUserService extends IService<User> {
}

②业务层接口实现类快速开发,关注继承的类需要传入两个泛型,一个是数据层接口,另一个是实体类

@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

③对业务层快速开发接口进行测试

@SpringBootTest
public class IUserServiceTest {

    @Autowired
    private IUserService userService;

    @Test
    public void test() {

        System.out.println(userService.getById(1));
    }
    @Test
    public void test2() {
        IPage<User> page = new Page<User>(1, 5);
        userService.page(page);
        System.out.println(page.getRecords());
    }
    @Test
    public void test3() {
        List<User> userList = userService.list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
    @Test
    public void test4() {
        User user = new User();
        user.setId(15);
        user.setUsername("郑大");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("河南郑州");
        userService.save(user);
    }
    @Test
    public void test5() {
        User user = new User();
        user.setId(15);
        user.setUsername("he");
        user.setPassword("0000");
        user.setGender("男");
        user.setAddress("河南郑州");
        userService.updateById(user);
    }
    @Test
    public void test6() {
        userService.removeById(16);
    }
}

1.4 表现层开发

不规范的表现层开发如下

①编写表现层相关方法

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private IUserService userService;

    @GetMapping
    public List<User> getUsers() {
        return userService.list();
    }

    @PostMapping
    public Boolean save(@RequestBody User user) {
        return userService.save(user);
    }

    @PutMapping
    public Boolean update(@RequestBody User user) {
        return userService.updateById(user);
    }

    @DeleteMapping("/{id}")
    public Boolean delete(@PathVariable Integer id) {
        return userService.removeById(id);
    }
    @GetMapping("/{id}")
    public User getUser(@PathVariable Integer id) {
        return userService.getById(id);
    }

    @GetMapping("/{currentPage}/{pageSize}")
    public IPage<User> getPages(@PathVariable int currentPage,@PathVariable int pageSize) {
        return userService.getPages(currentPage, pageSize);
    }
}

②使用PostMan进行测试:

image-20220825212654398

image-20220825212733020

image-20220825212923294

image-20220825213116759

image-20220825213214940

image-20220825213921314

为什么说是不规范呢?

目前我们通过Postman测试后业务层接口功能时通的,但是这样的结果给到前端开发者会出现一个小问题。不同的操作结果所展示的数据格式差异化严重。每种不同操作返回的数据格式都不一样,而且还不知道以后还会有什么格式,这样的结果让前端人员看了是很容易让人崩溃的,必须将所有操作的操作结果数据格式统一起来,需要设计表现层返回结果的模型类,用于后端与前端进行数据格式统一,也称为前后端数据协议

规范表现层的开发

①创建一个标准的返回类型R,其中flag用于标识操作是否成功,data用于封装操作数据

package com.liu.controller.utils;

import lombok.Data;

/**
 * @author LiuChang
 * @version 1.0.0
 * @description TODO
 * @date 2022/8/26 10:03
 */
@Data
public class R {
    private boolean flag;
    private Object data;

    public R() {
    }

    public R(boolean flag) {
        this.flag = flag;
    }

    public R(boolean flag, Object data) {
        this.flag = flag;
        this.data = data;
    }
}

②规范表现层方法

@RestController
@RequestMapping("/users")
public class UserController {

    @Autowired
    private IUserService userService;


    @GetMapping
    public R getUsers() {
        return new R(true,userService.list());
    }

    @PostMapping
    public R save(@RequestBody User user) {
        return new R(userService.save(user));
    }

    @PutMapping
    public R update(@RequestBody User user) {
        return new R(userService.updateById(user));
    }

    @DeleteMapping("/{id}")
    public R delete(@PathVariable Integer id) {
        return new R(userService.removeById(id));
    }

    @GetMapping("/{id}")
    public R getUser(@PathVariable Integer id) {
        return new R(true,userService.getById(id));
    }

    @GetMapping("/{currentPage}/{pageSize}")
    public R getPages(@PathVariable int currentPage,@PathVariable int pageSize) {
        return new R(true,userService.getPages(currentPage, pageSize));
    }
}

1.5 页面基础功能开发

①查询所有功能

页面添加条件字段对应的数据模型绑定名称

<el-table size="small" current-row-key="id" :data="dataList" stripe highlight-current-row>
    <el-table-column type="index" align="center" label="序号"></el-table-column>
    <el-table-column prop="username" label="用户名称" align="center"></el-table-column>
    <el-table-column prop="password" label="用户密码" align="center"></el-table-column>
    <el-table-column prop="gender" label="用户性别" align="center"></el-table-column>
    <el-table-column prop="address" label="用户地址" align="center"></el-table-column>
    <el-table-column label="操作" align="center">
        <template slot-scope="scope">
            <el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
               <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>
        </template>
    </el-table-column>
</el-table>

页面封装字段

data:{
    dataList: [],//当前页要展示的列表数据
}

编写方法获取数据

//钩子函数,VUE对象初始化完成后自动执行
created() {
    this.getAll();
},
//列表
getAll() {
    axios.get("/users").then((res)=>{
        console.log(res.data);
        this.dataList = res.data.data;
    })
},

②添加功能

页面添加条件字段对应的数据模型绑定名称

<!-- 新增标签弹层 -->
<div class="add-form">
    <el-dialog title="新增用户" :visible.sync="dialogFormVisible">
        <el-form ref="dataAddForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
            <el-row>
                <el-col :span="12">
                    <el-form-item label="用户名称" prop="username">
                        <el-input v-model="formData.username"/>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用户密码" prop="password">
                        <el-input v-model="formData.password"/>
                    </el-form-item>
                </el-col>
                <el-col :span="12">
                    <el-form-item label="用户性别" prop="gender">
                        <el-input v-model="formData.gender"/>
                    </el-form-item>
                </el-col>
            </el-row>
            <el-row>
                <el-col :span="24">
                    <el-form-item label="地址">
                        <el-input v-model="formData.address" type="address"></el-input>
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div slot="footer" class="dialog-footer">
            <el-button @click="cancel()">取消</el-button>
            <el-button type="primary" @click="handleAdd()">确定</el-button>
        </div>
    </el-dialog>
</div>

页面封装字段

data:{
    dialogFormVisible: false,//添加表单是否可见
    formData: {},//表单数据
}

编写方法插入数据

//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
    this.resetForm();
},

//重置表单
resetForm() {
    this.formData = {};
},

//添加
handleAdd () {
    axios.post("/users",this.formData).then((res)=> {
        //判断当前操作是否成功
        if (res.data.flag) {
            this.dialogFormVisible = false;
            this.$message.success("添加成功");
        }else {
            this.$message.error("添加失败");
        }
    }).finally(()=> {
        this.getAll();
    })
},

//取消
cancel(){
    this.dialogFormVisible = false;
    this.$message.info("操作取消");
},

③删除功能

页面添加条件字段对应的数据模型绑定名称

 <el-button type="danger" size="mini" @click="handleDelete(scope.row)">删除</el-button>

编写方法删除数据

// 删除
handleDelete(row) {
    console.log(row);

    this.$confirm("是否删除?","提示",{type:"info",}).then(()=>{
        axios.delete("/users/"+row.id).then((res)=>{
            if (res.data.flag) {
                this.$message.success("删除成功");
            }else {
                this.$message.error("删除失败");
            }
        }).finally(()=> {
            this.getAll();
        })
    }).catch(()=>{
        this.$message.info("取消操作");
    })
},

④修改操作

页面添加条件字段对应的数据模型绑定名称

<el-button type="primary" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
 <div class="add-form">
                <el-dialog title="编辑检查项" :visible.sync="dialogFormVisible4Edit">
                   <el-form ref="dataEditForm" :model="formData" :rules="rules" label-position="right" label-width="100px">
                        <el-row>
                            <el-col :span="12">
                                <el-form-item label="用户名称" prop="username">
                                    <el-input v-model="formData.username"/>
                                </el-form-item>
                            </el-col>
                            <el-col :span="12">
                                <el-form-item label="用户密码" prop="password">
                                    <el-input v-model="formData.password"/>
                                </el-form-item>
                            </el-col>
                           <el-col :span="12">
                                <el-form-item label="用户性别" prop="gender">
                                    <el-input v-model="formData.gender"/>
                                </el-form-item>
                            </el-col>
                        </el-row>
                        <el-row>
                            <el-col :span="24">
                                <el-form-item label="地址">
                                    <el-input v-model="formData.address" type="address"></el-input>
                                </el-form-item>
                            </el-col>
                        </el-row>
                    </el-form>
                    <div slot="footer" class="dialog-footer">
                       <el-button @click="cancel()">取消</el-button>
                        <el-button type="primary" @click="handleEdit()">确定</el-button>
                    </div>
                </el-dialog>
            </div>

页面封装字段

data:{
    dialogFormVisible4Edit:false,//编辑表单是否可见
}

编写方法更新数据

//弹出编辑窗口
handleUpdate(row) {
    axios.get("/users/" + row.id).then((res)=>{
        if (res.data.flag && res.data.data!=null) {
            this.dialogFormVisible4Edit = true;
            this.formData = res.data.data;
        }else {
            this.$message.error("数据同步失败,自动刷新");
        }
    }).finally(()=> {
        this.getAll();
    });
},

//修改
handleEdit() {
    axios.put("/users",this.formData).then((res)=> {
        //判断当前操作是否成功
        if (res.data.flag) {
            this.dialogFormVisible4Edit = false;
            this.$message.success("修改成功");
        }else {
            this.$message.error("修改失败");
        }
    }).finally(()=> {
        this.getAll();
    })
},

⑤分页查询

页面添加条件字段对应的数据模型绑定名称

<!--分页组件-->
<div class="pagination-container">
    <el-pagination
            class="pagiantion"
            @current-change="handleCurrentChange"
            :current-page="pagination.currentPage"
            :page-size="pagination.pageSize"
            layout="total, prev, pager, next, jumper"
            :total="pagination.total">
    </el-pagination>
</div>

页面封装字段

data:{
    pagination: {//分页相关模型数据
    currentPage: 1,//当前页码
    pageSize:10,//每页显示的记录数
    total:0,//总记录数
}

编写方法分页查询数据

//列表
getAll() {
    axios.get("/users/"+this.pagination.currentPage+"/"+this.pagination.pageSize).then((res)=>{
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pageSize = res.data.data.size;
        this.pagination.total = res.data.data.total;
        this.dataList = res.data.data.records;
    })
},
//切换页码
handleCurrentChange(currentPage) {
    this.pagination.currentPage = currentPage;
    this.getAll();

⑥条件查询

LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.like(Strings.isNotEmpty(user.getUsername()),User::getUsername,user.getUsername());
queryWrapper.like(Strings.isNotEmpty(user.getPassword()),User::getPassword,user.getPassword());
queryWrapper.like(Strings.isNotEmpty(user.getGender()),User::getGender,user.getGender());
queryWrapper.like(Strings.isNotEmpty(user.getAddress()),User::getAddress,user.getAddress());
getAll() {
    //1.获取查询条件,拼接查询条件
    param = "?username="+this.pagination.username;
    param += "&password="+this.pagination.password;
    param += "&gender="+this.pagination.gender;
    param += "&address="+this.pagination.address;
    axios.get("/users/"+this.pagination.currentPage+"/"+this.pagination.pageSize+param).then((res)=>{
        this.pagination.currentPage = res.data.data.current;
        this.pagination.pageSize = res.data.data.size;
        this.pagination.total = res.data.data.total;
        this.dataList = res.data.data.records;
    })
},

2 Spring Boot项目打包

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <excludes>
            <exclude>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </exclude>
        </excludes>
    </configuration>
</plugin>

image-20220827092605110

image-20220827092849356

image-20220827093527924

Spring Boot的jar包在Window环境中运行:

image-20220827093045433

Spring Boot的jar包在Linux环境中运行:

先查看了自己的ubuntu系统版本号:

微信图片_20220827120309

进入IDEA查看Maven自己是否配置成功:

微信图片_20220827120334

查看java版本号:

微信图片_20220827120411

使用Linux环境运行SpringBoot的jar包:

微信图片_20220827120433

微信图片_20220827120437

运行成功之后发现了相关错误:可以看到是由于用户权限不够,于是使用sudo加权

微信图片_20220827120456

微信图片_20220827120500

加权后发现本数据库中没有相关表,于是直接运行相关sql语句:

微信图片_20220827053826

微信图片_20220827053829

再次运行可以发现项目已经成功跑出来了:

微信图片_20220827053855

可以看到后台输出的日志:

微信图片_20220827053901

3 临时属性配置

SpringBoot提供了灵活的配置方式,如果你发现你的项目中有个别属性需要重新配置,可以使用临时属性的方式快速修改某些配置。

  • 在启动的时候添加上对应参数就可以了。

微信图片_20220827053907

  • 打开SpringBoot引导类的运行界面,在里面找到配置项。其中Program arguments对应的位置就是添加临时属性的。

image-20220827150535848

对于IDEA没有出现的需要在Add中寻找:

image-20220827150612663

可以看到端口号已经修改成了8080端口:

image-20220827150332929

  • 也可以在启动类中进行相关设置:

image-20220827150839520

4 SpringBoot高级配置

4.1 SpringBoot加载第三方Bean

使用@ConfigurationProperties注解其实可以为第三方bean加载属性。

@Component
@Data
@ConfigurationProperties(prefix = "servers")
public class ServletConfig {
    private String ipAddress;
    private int port;
    private long timeout;
}
servers:
  ipAddress: 192.168.1.2
  port: 8080
  timeout: 30

datasource:
  driverClassName: com.mysql.jdbc.cj.Driver
@SpringBootApplication
public class Springboot13configurationApplication {
    //加载第三方Bean
    @Bean
    @ConfigurationProperties(prefix = "datasource")
    public DruidDataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        return dataSource;
    }
    public static void main(String[] args) {

        ConfigurableApplicationContext context = SpringApplication.run(Springboot13configurationApplication.class, args);
        ServletConfig config = context.getBean(ServletConfig.class);
        System.out.println(config);
        DruidDataSource dataSource = context.getBean(DruidDataSource.class);
        System.out.println(dataSource.getDriverClassName());
    }
}

image-20220911151532563

绑定第三方数据源:

@DurationUnit(ChronoUnit.HOURS)
private Duration serverTimeout;
@DataSizeUnit(DataUnit.KILOBYTES)
private DataSize dataSize;
servers:
  ipAddress: 192.168.1.2
  port: 8080
  timeout: 30
  serverTimeout: 3
  dataSize: 128

image-20220911154304481

4.2 校验配置

SpringBoot给出了强大的数据校验功能,可以有效的避免此类问题的发生。在JAVAEE的JSR303规范中给出了具体的数据校验标准,开发者可以根据自己的需要选择对应的校验框架,此处使用Hibernate提供的校验框架来作为实现进行数据校验。

<!--导入JSP303规范-->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
</dependency>
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
@Component
@Data
@ConfigurationProperties(prefix = "servers")
//开启对当前Bean的属性注入校验
@Validated
public class ServletConfig {
    private String ipAddress;
    @Max(value = 8888,message = "最大值不能超过8888")
    @Min(value = 202,message = "最小值不能小于202")
    private int port;
    private long timeout;
    @DurationUnit(ChronoUnit.HOURS)
    private Duration serverTimeout;
    @DataSizeUnit(DataUnit.KILOBYTES)
    private DataSize dataSize;
}

image-20220911155436354

4.3 测试配置

加载测试专用配置

@Configuration
public class MsgConfig {
    @Bean
    public String msg() {
        return "Bean msg";
    }
}
@SpringBootTest
@Import({MsgConfig.class})
public class ConfigurationTest {

    @Autowired
    private String msg;

    @Test
    void test1(){
        System.out.println(msg);
    }
}

测试类中启动web环境

@RestController
@RequestMapping("/books")
public class BookController {
    @GetMapping
    public String getById(){
        System.out.println("getById() called");
        return "springboot";
    }
}
//开启web环境
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟调用MVC
@AutoConfigureMockMvc
public class WebTest {
    @Test
    void testWeb(@Autowired MockMvc mvc) throws Exception {
        //执行get请求
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        mvc.perform(builder);
    }
}

image-20220913140841272

匹配响应执行状态

@Test
void testStatus(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books1");
    ResultActions actions = mvc.perform(builder);
    //设定预期值,与真实的值进行比较,成功测试通过,失败测试失败
    StatusResultMatchers status = MockMvcResultMatchers.status();
    //预计本次调用时成功的:状态码为200
    ResultMatcher ok = status.isOk();
    //添加预计值到本次调用过程中进行匹配
    actions.andExpect(ok);
}

image-20220913141540449

匹配响应体

@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions actions = mvc.perform(builder);
    //设定预期值,与真实的值进行比较,成功测试通过,失败测试失败
    ContentResultMatchers content = MockMvcResultMatchers.content();
    //预计本次调用时成功的:body
    ResultMatcher result = content.string("springboot1");
    //添加预计值到本次调用过程中进行匹配
    actions.andExpect(result);
}

image-20220913142238657

匹配json响应体格式:

@Test
void testBody(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions actions = mvc.perform(builder);
    //设定预期值,与真实的值进行比较,成功测试通过,失败测试失败
    ContentResultMatchers content = MockMvcResultMatchers.content();
    //预计本次调用时成功的:body
    ResultMatcher result = content.json("{\"name\":\"瓦尔登湖\",\"id\":1,\"type\":\"自然科学\",\"description\":\"一位隐士记录生活\"}");
    //添加预计值到本次调用过程中进行匹配
    actions.andExpect(result);
}

匹配响应头

@Test
void testContentType(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions actions = mvc.perform(builder);
    //设定预期值,与真实的值进行比较,成功测试通过,失败测试失败
    HeaderResultMatchers header = MockMvcResultMatchers.header();
    ResultMatcher matcher = header.string("Content-Type", "application/json");
    //添加预计值到本次调用过程中进行匹配
    actions.andExpect(matcher);
}

业务层测试事务回滚

@SpringBootTest
//Spring添加事务注解
@Transactional
@Rollback(true)
class SpringBootMybatisApplicationTests {

    @Autowired
    private UserMapper userMapper;

    @Test
    void userInserts() {
        User user = new User();
        user.setUsername("刘畅");
        user.setPassword("1314");
        user.setGender("男");
        user.setAddress("河南");
        userMapper.insert(user);
    }
}

测试用例设置随机数据

testcase:
  user:
    id: ${random.int}
    username: ${random.value}
    password: ${random.value}
    gender: ${random.value}
    address: ${random.value}
@SpringBootTest
//Spring添加事务注解
@Transactional
@Rollback(true)
class SpringBootMybatisApplicationTests {

    @Autowired
    private UserMapper userMapper;


    @Autowired
    private User user;

    @Test
    public void test(){
        System.out.println(user.getId());
        System.out.println(user.getUsername());
        System.out.println(user.getPassword());
        System.out.println(user.getGender());
        System.out.println(user.getAddress());
    }
}

image-20220913155003986

4.4 数据层解决方案

配置内嵌Hikari数据源

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    hikari:
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: 131411

image-20220913164657722

配置内嵌 H2数据库:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
spring:
  h2:
    console:
      path: /h2
      enabled: true
  datasource:
    url: jdbc:h2:~/test
    hikari:
      driver-class-name: org.h2.Driver
      username: sa
      password: 123456
mybatis-plus:
  global-config:
    db-config:
      table-prefix: tb_
server:
  port: 80

连接H2数据库:

image-20220913192747199

进入H2数据库界面:

image-20220913193026147

查询表操作:

image-20220913194521869

创建表操作:

image-20220913195016181

插入表数据操作:

image-20220913195340757

查询所有操作:

image-20220913195409790

发生报错:

Database may be already in use: "C:/Users/77339/test.mv.db". Possible solutions: close all other connection(s); use the server mode [90020-214]

经过查询发现:是数据库被占用,将服务器停掉之后,问题就解决掉了。

image-20220913200834666

image-20220913200725598

NoSQL:

Redis与SpringBoot框架结合:

image-20220913203825557

image-20220913204327337

创建新模块,选择Redis缓存。

image-20220913204625302

spring:
  redis:
    host: localhost
    port: 6379
    client-type: jedis
@SpringBootTest
class SpringBootRedisApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    void set() {
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("age",23);
    }

    @Test
    void get() {
        ValueOperations ops = redisTemplate.opsForValue();
        Object age = ops.get("age");
        System.out.println(age);
    }

    @Test
    void setHash(){
        HashOperations hash = redisTemplate.opsForHash();
        hash.put("info","a","aa");
    }
    @Test
    void getHash(){
        HashOperations hash = redisTemplate.opsForHash();
        Object o = hash.get("info", "a");
        System.out.println(o);
    }

}
@SpringBootTest
public class StringRedis {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    void get() {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        String name = ops.get("name");
        System.out.println(name);
    }
}

image-20220914145053549

ES分布式全文搜索引擎

1.启动ES

image-20220915143949712

2.操作索引

image-20220915144118802

0

评论

博主关闭了所有页面的评论