SpringBoot 基本应用

约定优于配置

约定优于配置(Convention over Configuration),又称按约定编程,是一种软件设计范式。

本质上是说,系统、类库或框架应该假定合理的默认值,而非要求提供不必要的配置。比如说模型中有一个名为 User 的类,那么数据库中对应的表就会默认命名为 User。只有在偏离这一个约定的时候,例如想要将该表命名为 person,才需要写有关这个名字的配置。

比如平时架构师搭建项目就是限制软件开发随便写代码,制定出一套规范,让开发人员按统一的要求进行开发编码测试之类的,这样就加强了开发效率与审查代码效率。所以说写代码的时候就需要按要求命名,这样统一规范的代码就有良好的可读性与维护性了。

约定优于配置简单来理解,就是遵循约定。

Spring Boot 是所有基于 Spring 开发的项目的起点。Spring Boot 的设计是为了尽可能快的跑起来 Spring 应用程序并且尽可能减少配置文件。

SpringBoot 概念

Spring 优缺点分析

优点:Spring 是 Java 企业版(Java Enterprise Edition,JEE,也称 J2EE)的轻量级代替品。无需开发重量级的 Enterprise Java Bean - EJB,Spring 为企业级 Java 开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单的 Java 对象(Plain Old Java Object,POJO)实现了 EJB 的功能。

缺点:虽然 Spring 的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring 用 XML 配置,而且是很多 XML 配 置。Spring 2.5 引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式 XML 配置。Spring 3.0 引入 了基于 Java 的配置,这是一种类型安全的可重构配置方式,可以代替 XML。

所有这些配置都代表了开发时的损耗。因为在思考 Spring 特性配置和解决业务问题之间需要进行思维切换,所以编写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring 实用,但与此同时它要求的回报也不少。

除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度。SSM 整合:Spring、Spring MVC、Mybatis、Spring-Mybatis 整合包、数据库驱动,引入依赖的数量繁多、容易存在版本冲突。

Spring Boot 解决上述 Spring 的问题

SpringBoot 对上述 Spring 的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的效率,一定程度上缩短了项目周期。

起步依赖:

起步依赖本质上是一个 Maven 项目对象模型 (Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。 简单的说,起步依赖就是将具备某种功能的依赖坐标打包到一起,并提供一些默认的功能。

自动配置:

springboot 的自动配置,指的是 springboot 会自动将一些配置类的 bean 注册进 ioc 容器,在需要的地方使用 @autowired 或者 @resource 等注解来使用它。“自动”的表现形式就是只需要引想用功能的包,相关的配置完全不用管,springboot 会自动注入这些配置 bean,直接使用这些 bean 即可。

Springboot 可以简单、快速、方便地搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率。

SpringBoot 入门案例

案例需求:请求 Controller 中的方法,并将返回值响应到页面。

1) 依赖管理

pom.xml

<parent>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-parentartifactId>    <version>2.2.2.RELEASEversion>parent>

<dependencies>

    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-webartifactId>    dependency>dependencies>

<build>    <plugins>        <plugin>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-maven-pluginartifactId>        plugin>    plugins>build>
2) 启动类

com.renda.SpringBootDemo1Application

/** * SpringBoot 的启动类通常放在二级包中, * 比如:com.renda.SpringBootDemo1Application。 * 因为 SpringBoot 项目在做包扫描, * 会扫描启动类所在的包及其子包下的所有内容。 * * @author Renda Zhang * @since 2020-10-28 23:11 */@SpringBootApplication // 标识当前类为 SpringBoot 项目的启动类public class SpringBootDemo1Application {

    public static void main(String[] args) {        // 样板代码        SpringApplication.run(SpringBootDemo1Application.class, args);    }

}
3) Controller

com.renda.controller.HelloController

@RestController@RequestMapping("/hello")public class HelloController {

    @RequestMapping("/boot")    public String helloSpringBoot() {        return "Hello Spring Boot";    }

}

SpringBoot 快速构建

案例需求:请求 Controller 中的方法,并将返回值响应到页面。

1)使用 Spring Initializr 方式构建 Spring Boot 项目

本质上说,Spring Initializr 是一个 Web 应用,它提供了一个基本的项目结构,能够快速构建一个基础的 Spring Boot 项目。

注意使用快速方式创建 Spring Boot 项目时,所在主机须在联网状态下;本质上是在开发工具执行各项参数后,由 Spring 提供的 URL 所对应的服务器生成, IDEA 将服务器生成的 SpringBoot 项目下载到本地的工作空间中。

Project SDK 用于设置创建项目使用的 JDK 版本,这里,使用之前初始化设置好的 JDK 版本即可;在 Choose Initializr Service URL 下使用默认的初始化服务地址 https://start.spring.io 进行 Spring Boot 项目创建。

创建完成后的 pom 文件

<?xml  version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0modelVersion>    <parent>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-parentartifactId>        <version>2.2.2.RELEASEversion>        <relativePath/>     parent>    <groupId>com.rendagroupId>    <artifactId>springbootdemo2artifactId>    <version>0.0.1-SNAPSHOTversion>    <name>springbootdemo2name>    <description>Demo project for Spring Bootdescription>

    <properties>        <java.version>11java.version>    properties>

    <dependencies>        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-webartifactId>        dependency>

        <dependency>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-starter-testartifactId>            <scope>testscope>            <exclusions>                <exclusion>                    <groupId>org.junit.vintagegroupId>                    <artifactId>junit-vintage-engineartifactId>                exclusion>            exclusions>        dependency>    dependencies>

    <build>        <plugins>            <plugin>                <groupId>org.springframework.bootgroupId>                <artifactId>spring-boot-maven-pluginartifactId>            plugin>        plugins>    build>

project>

创建完成后,可以删除自动生成的 .mvn.gitignoreHELP.mdmvnwmvnw.cmd

创建好的 Spring Boot 项目结构如图:

/src/main/java/    com.renda.Springbootdemo2Application - 项目主程序启动类/src/main/resources/    static - 静态资源文件夹    templates - 模板页面文件夹    application.properties - 全网配置文件/src/test/java/    com.renda.Springbootdemo2ApplicationTests - 项目测试类

使用 Spring Initializr 方式构建的 Spring Boot 项目会默认生成项目启动类、存放前端静态资源和页面的文件夹、编写项目配置的配置文件以及进行项目单元测试的测试类。

2)创建一个用于 Web 访问的 Controller

注意:确保项目启动类 Springbootdemo2Application 在 com.renda 包下。

com.renda.controller.HelloController

@RestController@RequestMapping("/hello")public class HelloController {

    @RequestMapping("/boot")    public String hello() {        return "What's up! Spring Boot!";    }

}
3) 运行项目

application.properties 中修改 tomcat 的默认端口号:

server.port=8888

运行主程序启动类 Springbootdemo2Application,项目启动成功后,在控制台上会发现 Spring Boot 项目默认启动的端口号为 8888,此时,可以在浏览器上访问 http://localhost:8888/hello/boot。

页面输出的内容是 “What's up! Spring Boot!”,至此,构建 Spring Boot 项目就完成了。

单元测试与热部署

单元测试

开发中,每当完成一个功能接口或业务方法的编写后,通常都会借助单元测试验证该功能是否正确。

1)添加 spring-boot-starter-test 测试依赖启动器,在项目的 pom.xml 文件中添加 spring-boot-starter-test 测试依赖启动器,示例代码如下 :

<dependency>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-testartifactId>    <scope>testscope>    <exclusions>        <exclusion>            <groupId>org.junit.vintagegroupId>            <artifactId>junit-vintage-engineartifactId>        exclusion>    exclusions>dependency>

注意:使用 Spring Initializr 方式搭建的 Spring Boot 项目,会自动加入 spring-boot-starter-test 测试依赖启动器,无需再手动添加。

2)编写单元测试类和测试方法:

/** *  SpringJUnit4ClassRunner.class: Spring 运行环境 *  JUnit4.class: JUnit 运行环境 *  SpringRunner.class: Spring Boot 运行环境 */@RunWith(SpringRunner.class) // @RunWith: 运行器@SpringBootTest // 标记为当前类为 SpringBoot 测试类,加载项目的 ApplicationContext 上下文环境class Springbootdemo2ApplicationTests {

    @Autowired    private HelloController helloController;

    @Test    void contextLoads() {        String result = helloController.hello();        System.out.println(result);    }

}

上述代码中,先使用 @Autowired 注解注入了 HelloController 实例对象,然后在 contextLoads() 方法中调用了 HelloController 类中对应的请求控制方法 hello(),并输出打印结果。

热部署

在开发过程中,通常会对一段业务代码不断地修改测试,在修改之后往往需要重启服务,有些服务需要加载很久才能启动成功,这种不必要的重复操作极大的降低了程序开发效率。为此,Spring Boot 框架专门提供了进行热部署的依赖启动器,用于进行项目热部署,而无需手动重启项目 。

热部署:在修改完代码之后,不需要重新启动容器,就可以实现更新。

使用步骤:

1)添加 spring-boot-devtools 热部署依赖启动器。

在 Spring Boot 项目进行热部署测试之前,需要先在项目的 pom.xml 文件中添加 spring-boot-devtools 热部署依赖启动器:

<dependency>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-devtoolsartifactId>dependency>

由于使用的是 IDEA 开发工具,添加热部署依赖后可能没有任何效果,接下来还需要针对 IDEA 开发工具进行热部署相关的功能设置。

2)开启 IDEA 的自动编译。

选择 IDEA 工具界面的 【File】->【Settings】 选项,打开 Compiler 面板设置页面。

选择 Build 下的 Compiler 选项,在右侧勾选 “Build project automatically” 选项将项目设置为自动编译,单击 【Apply】->【OK】 按钮保存设置。

3)开启 IDEA 的在项目运行中自动编译的功能。

在项目任意页面中使用组合快捷键 “Ctrl+Shift+Alt+/” 打开 Maintenance 选项框,选中并打开 Registry 页面。

列表中找到 “compiler.automake.allow.when.app.running”,将该选项后的 Value 值勾选,用于指定 IDEA 工具在程序运行过程中自动编译,最后单击 【Close】 按钮完成设置。
热部署效果测试

启动 Springbootdemo2Application http://localhost:8888/hello/boot

页面原始输出的内容是 “What's up! Spring Boot!”。

为了测试配置的热部署是否有效,接下来,在不关闭当前项目的情况下,将 HelloController 类中的请求处理方法 hello() 的返回值修改为 “Hello! Spring Boot!” 并保存,查看控制台信息会发现项目能够自动构建和编译,说明项目热部署生效。

再次刷新浏览器,输出了 “Hello! Spring Boot!”,说明项目热部署配置成功。

全局配置文件

全局配置文件能够对一些默认配置值进行修改。Spring Boot 使用一个 application.properties 或者 application.yaml 的文件作为全局配置文件,该文件存放在 src/main/resource 目录或者类路径的 /config,一般会选择 resource 目录。

Spring Boot 配置文件的命名及其格式:

  • application.properties

  • application.yaml

  • application.yml

`application.properties` 配置文件

引入相关依赖:

<dependency>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-jdbcartifactId>dependency>

<dependency>    <groupId>mysqlgroupId>    <artifactId>mysql-connector-javaartifactId>    <version>6.0.6version>dependency>

使用 Spring Initializr 方式构建 Spring Boot 项目时,会在 resource 目录下自动生成一个空的 application.properties 文件,Spring Boot 项目启动时会自动加载 application.properties 文件。

# 修改 tomcat 的版本号server.port=8080# 定义数据库的连接信息 JdbcTemplatespring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/renda01spring.datasource.username=rootspring.datasource.password=password

可以在 application.properties 文件中定义 Spring Boot 项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置。

预先准备了两个实体类文件,后续会演示将 application.properties 配置文件中的自定义配置属性注入到 Person 实体类的对应属性中。

1)先在项目的 com.renda 包下创建一个 pojo 包,并在该包下创建两个实体类 Pet 和 Person。

/** * 宠物类 * * @author Renda Zhang * @since 2020-10-29 1:03 */public class Pet {    // 品种    private String type;    // 名称    private String name;    // setter and getter, toString ...}

/** * 人类 * * @author Renda Zhang * @since 2020-10-29 1:04 */@Component/** * 将配置文件中所有以 person 开头的配置信息注入当前类中 * 前提 1:必须保证配置文件中 person.xx 与当前 Person 类的属性名一致 * 前提 2:必须保证当前 Person 中的属性都具有 set 方法 */@ConfigurationProperties(prefix = "person")public class Person {    // id    private int id;    // 名称    private String name;    // 爱好    private List hobby;    // 家庭成员    private String[] family;    private Map map;    // 宠物    private Pet pet;    // setter and getter, toString ...}

@ConfigurationProperties(prefix = "person") 注解的作用是将配置文件中以 person 开头的属性值通过 setXX() 方法注入到实体类对应属性中。

@Component 注解的作用是将当前注入属性值的 Person 类对象作为 Bean 组件放到 Spring 容器中,只有这样才能被 @ConfigurationProperties 注解进行赋值。

2)打开项目的 resources 目录下的 application.properties 配置文件,在该配置文件中编写需要对 Person 类设置的配置属性。

# 自定义配置信息注入到 Person 对象中person.id=100person.name=张人大## listperson.hobby=看书,写代码,吃饭## String[]person.family=兄弟,父母## mapperson.map.k1=v1person.map.k2=v2## pet 对象person.pet.type=dogperson.pet.name=旺财

3)查看 application.properties 配置文件是否正确,同时查看属性配置效果,打开通过 IDEA 工具创建的项目测试类,在该测试类中引入 Person 实体类 Bean,并进行输出测试。

@RunWith(SpringRunner.class)@SpringBootTestclass Springbootdemo2ApplicationTests {

    @Autowired    private Person person;

    @Test    void showPersonInfo() {        System.out.println(person);    }

}

可以看出,测试方法 showPersonInfo() 运行成功,同时正确打印出了 Person 实体类对象。至此,说明 application.properties 配置文件属性配置正确,并通过相关注解自动完成了属性注入。

4)解决浏览器请求出现中文乱码问题。

调整文件编码格式:Settings -> Editor -> File Encodings -> 确保 Global Encoding 和 Project Encoding 为 UTF-8,修改 Default ecoding for properties files 为 UTF-8 并勾选 Transparent native-to-ascii conversion。

application.properties 文件中增加配置:

# 解决中文乱码server.tomcat.uri-encoding=UTF-8spring.http.encoding.force=truespring.http.encoding.charset=UTF-8spring.http.encoding.enabled=true
`application.yaml` 配置文件

YAML 文件格式是 Spring Boot 支持的一种 JSON 文件格式,相较于传统的 Properties 配置文件,YAML 文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。application.yaml 配置文件的工作原理和 application.properties 是一样的,只不过 yaml 格式配置文件看起来更简洁一些。

  • YAML 文件的扩展名可以使用 .yml 或者 .yaml

  • application.yml 文件使用 key: value 格式配置属性,使用缩进控制层级关系。

SpringBoot 的三种配置文件是可以共存的:application.propertiesapplication.yamlapplication.yml

1)value 值为普通数据类型(例如数字、字符串、布尔等)

当 YAML 配置文件中配置的属性值为普通数据类型时,可以直接配置对应的属性值,同时对于字符串类型的属性值,不需要额外添加引号,示例代码如下:

server:  port: 8080  servlet:    context-path: /hello

2)value 值为数组和单列集合

当 YAML 配置文件中配置的属性值为数组或单列集合类型时,主要有两种书写方式:缩进式写法和行内式写法。

其中,缩进式写法还有两种表示形式,示例代码如下:

person:   hobby:     - play    - read    - sleep 

或者使用如下示例形式:

person:  hobby:    play,    read,    sleep

上述代码中,在 YAML 配置文件中通过两种缩进式写法对 person 对象的单列集合(或数组)类型的爱好 hobby 赋值为 play、read 和 sleep。其中一种形式为 “- 属性值”,另一种形式为多个属性值之前加英文逗号分隔;注意,最后一个属性值后不要加逗号。

person:   hobby: [play,read,sleep]

通过上述示例对比发现,YAML 配置文件的行内式写法更加简明、方便。另外,包含属性值的中括号 “[]” 还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对。

3)value 值为 Map 集合和对象

当 YAML 配置文件中配置的属性值为 Map 集合或对象类型时,YAML 配置文件格式同样可以分为两种书写方式 - 缩进式写法和行内式写法。

其中,缩进式写法的示例代码如下:

person:   map:     k1: v1    k2: v2

对应的行内式写法示例代码如下:

person:  map: {k1: v1,k2: v2}

在YAML配置文件中,配置的属性值为Map集合或对象类型时,缩进式写法的形式按照YAML文件格式编写即可,而行内式写法的属性值要用大括号“{}”包含。

接下来,在 Properties 配置文件演示案例基础上,注释掉 Properties 中跟 Person 相关的注入,然后通过配置 application.yaml 配置文件对 Person 对象进行赋值,具体使用如下。

在项目的 resources 目录下,新建一个 application.yaml 配置文件,在该配置文件中编写为 Person 类设置的配置属性:

person:  id: 1000  name: 张人大  hobby:    - 跑步    - 瑜伽    - 游泳  family:    - 张小明    - 李小红  map:    k1: 这是 k1 对应的 value    k2: 这是 k2 对应的 value  pet:    type: dog    name: 金毛

再次执行测试。

可以看出,测试方法 showPersonInfo() 同样运行成功,并正确打印出了 Person 实体类对象。

需要说明的是,本次使用 application.yaml 配置文件进行测试时需要提前将 application.properties 配置文件中编写的配置注释,这是因为 application.properties 配置文件会覆盖 application.yaml 配置文件。

配置文件属性值的注入

spring-boot-starter-parent 的 pom 文件可以知道配置文件的优先级从低到高如下:

<includes>    <include>**/application*.ymlinclude>    <include>**/application*.yamlinclude>    <include>**/application*.propertiesinclude>includes>

使用 Spring Boot 全局配置文件设置属性时:

如果配置属性是 Spring Boot 已有属性,例如服务端口 server.port,那么 Spring Boot 内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。

如果配置的属性是用户自定义属性,例如刚刚自定义的 Person 实体类属性,还必须在程序中注入这些配置属性方可生效。

Spring Boot 支持多种注入配置文件属性的方式,下面来介绍如何使用注解 @ConfigurationProperties@Value 注入属性。

使用 `@ConfigurationProperties` 注入属性

Spring Boot 提供的 @ConfigurationProperties 注解用来快速、方便地将配置文件中的自定义属性值批量注入到某个 Bean 对象的多个对应属性中。假设现在有一个配置文件,如果使用 @ConfigurationProperties 注入配置文件的属性,示例代码如下:

@Component@ConfigurationProperties(prefix = "person")public class Person {    private int id;    private String name;    private List hobby;    private String[] family;    private Map map;    private Pet pet;    // setter and getter, toString ...}

上述代码使用 @Component@ConfigurationProperties(prefix = “person”) 将配置文件中的每个属性映射到 person 类组件中。

使用 `@Value` 注入属性

@Value 注解是 Spring 框架提供的,用来读取配置文件中的属性值并逐个注入到 Bean 对象的对应属性中,Spring Boot 框架从 Spring 框架中对 @Value 注解进行了默认继承,所以在 Spring Boot 框架中还可以使用该注解读取和注入配置文件属性值。使用 @Value 注入属性的示例代码如下:

@Componentpublic class Student {    @Value("${person.id}")    private int number;    // getter setter toString ...}

上述代码中,使用 @Component@Value 注入 Person 实体类的id属性。其中,@Value 不仅可以将配置文件的属性注入 Person 的 id 属性,还可以直接给 id 属性赋值,这点是@ConfigurationProperties 不支持的。

1)在 com.renda.pojo 包下新创建一个实体类 Student,并使用 @Value 注解注入属性:

@Componentpublic class Student {

    @Value("${person.id}")    private String number;

    @Value("${person.name}")    private String name;

    // getter setter toString ...}

Student 类使用 @Value 注解将配置文件的属性值读取和注入。

从上述示例代码可以看出,使用 @Value 注解方式需要对每一个属性注入设置,同时又免去了属性的 setXX() 方法。

2)再次打开测试类进行测试:

@RunWith(SpringRunner.class)@SpringBootTestclass Springbootdemo2ApplicationTests {

    @Autowired    private Student student;

    @Test    public void showStudentInfo(){        System.out.println(student);    }

}

可以看出,测试方法 showStudentInfo() 运行成功,同时正确打印出了 Student 实体类对象。需要说明的是,本示例中只是使用 @Value 注解对实例中 Student 对象的普通类型属性进行了赋值演示,而 @Value 注解对于包含 Map 集合、对象以及 YAML 文件格式的行内式写法的配置文件的属性注入都不支持,如果赋值会出现错误。

自定义配置

Spring Boot 免除了项目中大部分的手动配置,对于一些特定情况,可以通过修改全局配置文件以适应具体生产环境,可以说,几乎所有的配置都可以写在 application.yml 文件中,Spring Boot 会自动加载全局配置文件从而免除手动加载的烦恼。但是,如果自定义配置文件,Spring Boot 是无法识别这些配置文件的,此时就需要手动加载。

使用 `@PropertySource` 加载配置文件

对于这种加载自定义配置文件的需求,可以使用 @PropertySource 注解来实现。@PropertySource 注解用于指定自定义配置文件的具体位置和名称。

如果需要将自定义配置文件中的属性值注入到对应类的属性中,可以使用 @ConfigurationProperties 或者 @Value 注解进行属性值注入。

1)打开 Spring Boot 项目的 resources 目录,在项目的类路径下新建一个 my.properties 自定义配置文件,在该配置文件中编写需要设置的配置属性。

# 对实体类对象 Product 进行属性配置product.id=99product.name=小米

2)在 com.renda.pojo 包下新创建一个配置类 Product,提供 my.properties 自定义配置文件中对应的属性,并根据 @PropertySource 注解的使用进行相关配置。

@Component@PropertySource("classpath:my.properties")@ConfigurationProperties(prefix = "product")public class Product {    private int id;    private String name;    // getter setter toString ...}

主要是一个自定义配置类,通过相关注解引入了自定义的配置文件,并完成了自定义属性值的注入。针对示例中的几个注解,具体说明如下:

  • @PropertySource("classpath:my.properties") 注解指定了自定义配置文件的位置和名称,此示例表示自定义配置文件为 classpath 类路径下的 my.properties 文件。

  • @ConfigurationProperties(prefix = "product") 注解将上述自定义配置文件 my.properties 中以 product 开头的属性值注入到该配置类属性中。

3)进行测试。

@RunWith(SpringRunner.class)@SpringBootTestclass Springbootdemo2ApplicationTests {

    @Autowired    private Product product;

    @Test    public void showProductInfo() {        System.out.println(product);    }

}
使用 `@Configuration` 编写自定义配置类

在 Spring Boot 框架中,推荐使用配置类的方式向容器中添加和配置组件。

在 Spring Boot 框架中,通常使用 @Configuration 注解定义一个配置类,Spring Boot 会自动扫描和识别配置类,从而替换传统 Spring 框架中的 XML 配置文件。

当定义一个配置类后,还需要在类中的方法上使用 @Bean 注解进行组件配置,将方法的返回对象注入到 Spring 容器中,并且组件名称默认使用的是方法名,当然也可以使用 @Bean 注解的 name 或 value 属性自定义组件的名称。

1)在项目下新建一个 com.renda.config 包,并在该包下新创建一个类 MyService,该类中不需要编写任何代码:

package com.renda.service;public class MyService {}

创建了一个空的 MyService 类,而该类目前没有添加任何配置和注解,因此还无法正常被 Spring Boot 扫描和识别。

2)在项目的 com.renda.config 包下,新建一个类 MyConfig,并使用 @Configuration 注解将该类声明一个配置类,内容如下:

@Configuration // 标识当前类是一个配置类,SpringBoot 会扫描该类,将所有标识 @Bean 注解的方法的返回值注入的容器中public class MyConfig {

    @Bean // 注入的名称就是方法的名称,注入的类型就是返回值的类型    public MyService myService(){        return new MyService();    }

    @Bean("service_")    public MyService myService2(){        return new MyService();    }

}

MyConfig 是 @Configuration 注解声明的配置类(类似于声明了一个 XML 配置文件),该配置类会被 Spring Boot 自动扫描识别;使用 @Bean 注解的 myService() 方法,其返回值对象会作为组件添加到了 Spring 容器中(类似于 XML 配置文件中的标签配置),并且该组件的 id 默认是方法名 myService

3)测试类:

@RunWith(SpringRunner.class)@SpringBootTestclass Springbootdemo2ApplicationTests {

    @Autowired    private ApplicationContext applicationContext;

    @Test    public void testConfig(){        System.out.println(applicationContext.containsBean("myService"));        System.out.println(applicationContext.containsBean("service_"));    }

}

上述代码中,先通过 @Autowired 注解引入了 Spring 容器实例 ApplicationContext,然后在测试方法 testConfig() 中测试查看该容器中是否包括 id 为 myServiceservice_ 的组件。

从测试结果可以看出,测试方法 testConfig() 运行成功,显示运行结果为 true,表示 Spirng 的 IOC 容器中也已经包含了 id 为 myServiceservice_的实例对象组件,说明使用自定义配置类的形式完成了向 Spring 容器进行组件的添加和配置。

SpringBoot 原理深入及源码剖析

依赖管理

在 Spring Boot 入门程序中,项目 pom.xml 文件有两个核心依赖,分别是 spring-boot-starter-
parent 和 spring-boot-starter-web,关于这两个依赖的相关介绍具体如下:

spring-boot-starter-parent 依赖
<parent>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-parentartifactId>    <version>2.2.2.RELEASEversion>    <relativePath/>parent>

上述代码中,将 spring-boot-starter-parent 依赖作为 Spring Boot 项目的统一父项目依赖管理,并将项目版本号统一为 2.2.2.RELEASE,该版本号根据实际开发需求是可以修改的。

使用 “Ctrl+鼠标左键” 进入并查看 spring-boot-starter-parent 底层源文件,发现 spring-boot-
starter-parent 的底层有一个父依赖 spring-boot-dependencies,核心代码具体如下

<parent>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-dependenciesartifactId>    <version>2.2.2.RELEASEversion>    <relativePath>../../spring-boot-dependenciesrelativePath>parent>

继续查看 spring-boot-dependencies 底层源文件,核心代码具体如下:

<properties>    <activemq.version>5.15.11activemq.version>    ...    <solr.version>8.2.0solr.version>    <spring-amqp.version>2.2.2.RELEASEspring-amqp.version>    <spring-batch.version>4.2.1.RELEASEspring-batch.version>    <spring-cloud-connectors.version>2.0.7.RELEASEspring-cloud-connectors.version>    <spring-data-releasetrain.version>Moore-SR3spring-data-releasetrain.version>    <spring-framework.version>5.2.2.RELEASEspring-framework.version>    <spring-hateoas.version>1.0.2.RELEASEspring-hateoas.version>    <spring-integration.version>5.2.2.RELEASEspring-integration.version>    <spring-kafka.version>2.3.4.RELEASEspring-kafka.version>    <spring-ldap.version>2.3.2.RELEASEspring-ldap.version>    <spring-restdocs.version>2.0.4.RELEASEspring-restdocs.version>    <spring-retry.version>1.2.4.RELEASEspring-retry.version>    <spring-security.version>5.2.1.RELEASEspring-security.version>    <spring-session-bom.version>Corn-RELEASEspring-session-bom.version>    <spring-ws.version>3.0.8.RELEASEspring-ws.version>    <sqlite-jdbc.version>3.28.0sqlite-jdbc.version>    <sun-mail.version>${jakarta-mail.version}sun-mail.version>    <thymeleaf.version>3.0.11.RELEASEthymeleaf.version>    <thymeleaf-extras-data-attribute.version>2.0.1thymeleaf-extras-data-attribute.version>    ...properties>

从 spring-boot-dependencies 底层源文件可以看出,该文件通过标签对一些常用技术框架的依赖文件进行了统一版本号管理,例如 activemq、spring、tomcat 等,都有与 Spring Boot 2.2.2 版本相匹配的版本,这也是 pom.xml 引入依赖文件不需要标注依赖文件版本号的原因。

需要说明的是,如果 pom.xml 引入的依赖文件不是 spring-boot-starter-parent 管理的,那么在 pom.xml 引入依赖文件时,需要使用标签指定依赖文件的版本号。

spring-boot-starter-web 依赖

spring-boot-starter-parent 父依赖启动器的主要作用是进行版本统一管理;项目运行依赖的 JAR 包是从 Spring Boot Starters 中导入。

查看 spring-boot-starter-web 依赖文件源码,核心代码具体如下:

<dependencies>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starterartifactId>        <version>2.2.2.RELEASEversion>        <scope>compilescope>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-jsonartifactId>        <version>2.2.2.RELEASEversion>        <scope>compilescope>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-tomcatartifactId>        <version>2.2.2.RELEASEversion>        <scope>compilescope>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-validationartifactId>        <version>2.2.2.RELEASEversion>        <scope>compilescope>        <exclusions>            <exclusion>                <artifactId>tomcat-embed-elartifactId>                <groupId>org.apache.tomcat.embedgroupId>            exclusion>        exclusions>    dependency>    <dependency>        <groupId>org.springframeworkgroupId>        <artifactId>spring-webartifactId>        <version>5.2.2.RELEASEversion>        <scope>compilescope>    dependency>    <dependency>        <groupId>org.springframeworkgroupId>        <artifactId>spring-webmvcartifactId>        <version>5.2.2.RELEASEversion>        <scope>compilescope>    dependency>dependencies>

从上述代码可以发现,spring-boot-starter-web 依赖启动器的主要作用是提供 Web 开发场景所需的底层所有依赖。

正是如此,在 pom.xml 中引入 spring-boot-starter-web 依赖启动器时,就可以实现 Web 场景开发,而不需要额外导入 Tomcat 服务器以及其他 Web 依赖文件等。当然,这些引入的依赖文件的版本号还是由 spring-boot-starter-parent 父依赖进行的统一管理。

Spring Boot Starters:

https://github.com/spring-projects/spring-boot/tree/v2.1.0.RELEASE/spring-boot-project/spring-boot-starters

https://mvnrepository.com/search?q=starter

Spring Boot 除了提供有上述介绍的 Web 依赖启动器外,还提供了其他许多开发场景的相关依赖,可以打开 Spring Boot 官方文档,搜索 “Starters” 关键字查询场景依赖启动器。

需要说明的是,Spring Boot 官方并不是针对所有场景开发的技术框架都提供了场景启动器,例如数据库操作框架 MyBatis、阿里巴巴的 Druid 数据源等,Spring Boot 官方就没有提供对应的依赖启动器。为了充分利用 Spring Boot 框架的优势,在 Spring Boot 官方没有整合这些技术框架的情况下,MyBatis、Druid 等技术框架所在的开发团队主动与 Spring Boot 框架进行了整合,实现了各自的依赖启动器,例如 mybatis-spring-boot-starter、druid-spring-boot-starter 等。在 pom.xml 文件中引入这些第三方的依赖启动器时,切记要配置对应的版本号。

自动配置

概念:能够在添加 jar 包依赖的时候,自动配置一些组件的相关配置,无需手动配置或者只需要少量手动配置就能运行编写的项目。

Spring Boot 应用的启动入口是 @SpringBootApplication 注解标注类中的 main() 方法。

@SpringBootApplication:SpringBoot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的 main() 方法启动 SpringBoot 应用。

进入到 @SpringBootApplication 内:

// 注解的适用范围, Type 表示注解可以描述在类、接口、注解或枚举中@Target(ElementType.TYPE)// 表示注解的生命周期,Runtime 运行时@Retention(RetentionPolicy.RUNTIME)// 表示注解可以记录在 javadoc 中@Documented// 表示可以被子类继承该注解@Inherited// 标明该类为 Spring Boot 配置类@SpringBootConfiguration// 启动自动配置功能@EnableAutoConfiguration@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })public @interface SpringBootApplication {

    // 根据 class 来排除特定的类,使其不能加入 spring 容器,传入参数 value 类型是 class 类型。    @AliasFor(annotation = EnableAutoConfiguration.class)    Class>[] exclude() default {};

    // 根据 classname 来排除特定的类,使其不能加入 spring 容器,传入参数 value 类型是class的全类名字符串数组。    @AliasFor(annotation = EnableAutoConfiguration.class)    String[] excludeName() default {};

    // 指定扫描包,参数是包名的字符串数组。    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")    String[] scanBasePackages() default {};

    // 扫描特定的包,参数类似是 Class 类型数组。    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")    Class>[] scanBasePackageClasses() default {};

    ...   }

从上述源码可以看出,@SpringBootApplication 注解是一个组合注解,前面 4 个是注解的元数据信息, 主要看后面 3 个注解:@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 三个核心注解。

@SpringBootConfiguration 注解

@SpringBootConfiguration: SpringBoot 的配置类,标注在某个类上,表示这是一个  SpringBoot 的配置类。

查看 @SpringBootConfiguration 注解源码:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Configurationpublic @interface SpringBootConfiguration {    ...}

从上述源码可以看出,@SpringBootConfiguration 注解内部有一个核心注解 @Configuration,该注解是 Spring 框架提供的,表示当前类为一个配置类(XML 配置文件的注解表现形式),并可以被组件扫描器扫描。由此可见,@SpringBootConfiguration 注解的作用与 @Configuration 注解相同,都是标识一个可以被组件扫描器扫描的配置类,只不过 @SpringBootConfiguration 是被 Spring Boot 进行了重新封装命名而已。

`@EnableAutoConfiguration` 注解

@EnableAutoConfiguration:开启自动配置功能,以前需要手动配置的东西,现在由  SpringBoot 自动配置,这个注解就是 Springboot 能实现自动配置的关键。

查看该注解内部查看源码信息,核心代码具体如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited// 自动配置包@AutoConfigurationPackage// Spring 的底层注解 @Import,给容器中导入一个组件;导入的组件是AutoConfigurationPackages.Registrar.class@Import(AutoConfigurationImportSelector.class)// 告诉 SpringBoot 开启自动配置功能,这样自动配置才能生效。public @interface EnableAutoConfiguration {

    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    // 返回不会被导入到 Spring 容器中的类    Class>[] exclude() default {};

    // 返回不会被导入到 Spring 容器中的类名    String[] excludeName() default {};

}

可以发现它是一个组合注解,  Spring  中有很多以 Enable 开头的注解,其作用就是借助 @Import 来收集并注册特定场景相关的 Bean ,并加载到 IOC 容器。@EnableAutoConfiguration 就是借助 @Import 来收集所有符合自动配置条件的 bean 定义,并加载到 IoC 容器。

@AutoConfigurationPackage 注解

查看 @AutoConfigurationPackage 注解内部源码信息,核心代码具体如下:

@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(AutoConfigurationPackages.Registrar.class)public @interface AutoConfigurationPackage {

}

从上述源码可以看出,@AutoConfigurationPackage 注解的功能是由 @Import 注解实现的,它是 spring 框架的底层注解,它的作用就是给容器中导入某个组件类,例如 @Import(AutoConfigurationPackages.Registrar.class),它就是将 Registrar 这个组件类导入到容器中,可查看 Registrar 类中 registerBeanDefinitions 方法,这个方法就是导入组件类的具体实现:

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

    @Override    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {        register(registry, new PackageImport(metadata).getPackageName());    }

    @Override    public Set determineImports(AnnotationMetadata metadata) {        return Collections.singleton(new PackageImport(metadata));    }

}

从上述源码可以看出,在 Registrar 类中有一个 registerBeanDefinitions() 方法,使用 Debug 模式启动项目, 可以看到选中的部分就是 com.renda。也就是说,@AutoConfigurationPackage 注解的主要作用就是将主程序类所在包及所有子包下的组件到扫描到 spring 容器中。

因此在定义项目包结构时,要求定义的包结构非常规范,项目主程序启动类要定义在最外层的根目录位置,然后在根目录位置内部建立子包和类进行业务开发,这样才能够保证定义的类能够被组件扫描器扫描。

@Import({AutoConfigurationImportSelector.class}) 注解

AutoConfigurationImportSelector 这个类导入到 Spring 容器中,AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器 ApplicationContext 中。

继续研究 AutoConfigurationImportSelector 这个类,通过源码分析这个类中是通过selectImports 这个方法告诉 springboot 都需要导入那些组件:

@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) {    if (!isEnabled(annotationMetadata)) {        return NO_IMPORTS;    }    // 获得自动配置元信息,需要传入 beanClassLoader 这个类加载器    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader        .loadMetadata(this.beanClassLoader);    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,                                                                              annotationMetadata);    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}

深入研究 loadMetadata 方法 :

// 文件中为需要加载的配置类的类路径protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";

private AutoConfigurationMetadataLoader() {}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {    return loadMetadata(classLoader, PATH);}

static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {    try {        // 读取 spring-boot-autoconfigure-2.1.5.RELEASE.jar 包中的 spring-autoconfigure-metadata.properties 的信息生成 url        Enumeration urls = (classLoader != null) ? classLoader.getResources(path)            : ClassLoader.getSystemResources(path);        Properties properties = new Properties();// 解析 urls 枚举对象中的信息封装成 properties 对象并加载while (urls.hasMoreElements()) {            properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));        }// 根据封装好的 properties 对象生成 AutoConfigurationMetadata 对象并返回return loadMetadata(properties);    }catch (IOException ex) {throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);    }}

AutoConfigurationImportSelectorgetAutoConfigurationEntry 方法:

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {    // 判断 EnabledAutoConfiguration 注解有没有开启, 默认开启    if (!isEnabled(annotationMetadata)) {        return EMPTY_ENTRY;    }    // 获得注解的属性信息    AnnotationAttributes attributes = getAttributes(annotationMetadata);    // 获取默认支持的自动配置类列表    List configurations = getCandidateConfigurations(annotationMetadata, attributes);// 去重    configurations = removeDuplicates(configurations);// 去除一些多余的配置类,根据 @EnabledAutoConfiguration 的 exclusions 属性进行排除    Set exclusions = getExclusions(annotationMetadata, attributes);    checkExcludedClasses(configurations, exclusions);    configurations.removeAll(exclusions);// 根据 pom 文件中加入的依赖文件筛选中最终符合当前项目运行环境对应的自动配置类    configurations = filter(configurations, autoConfigurationMetadata);// 触发自动配置导入监听事件    fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);}

深入 getCandidateConfigurations 方法:

这个方法中有一个重要方法 loadFactoryNames,这个方法是让 SpringFactoryLoader 去加载一些组件的名字。

protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {    /**     * loadFactoryNames:     * 这个方法需要传入两个参数 getSpringFactoriesLoaderFactoryClass() 和      * getBeanClassLoader()。     * getSpringFactoriesLoaderFactoryClass() 这个方法返回的是     * @EnableAutoConfiguration.class。     * getBeanClassLoader() 这个方法返回的是 beanClassLoader 类加载器     */    List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct.");return configurations;}/** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */protected Class> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;}...@Overridepublic void setBeanClassLoader(ClassLoader classLoader) {this.beanClassLoader = classLoader;}protected ClassLoader getBeanClassLoader() {return this.beanClassLoader;}

继续点开 loadFactoryNames 方法:

public static List loadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) {    // 获取出入的键    String factoryTypeName = factoryType.getName();    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}

private static Map> loadSpringFactories(@Nullable ClassLoader classLoader) {    MultiValueMap result = cache.get(classLoader);if (result != null) {return result;    }try {// 如果类加载器不为 null,则加载类路径下 spring.factories 文件,将其中设置的配置类的全路径信息封装为 Enumeration 类对象        Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));        result = new LinkedMultiValueMap<>();// 循环 Enumeration 类对象,根据相应的节点信息生成 Properties 对象,通过传入的键获取值,在将值切割为一个个小的字符串转化为 Array,加入方法 result 集合中while (urls.hasMoreElements()) {            URL url = urls.nextElement();            UrlResource resource = new UrlResource(url);            Properties properties = PropertiesLoaderUtils.loadProperties(resource);for (Map.Entry, ?> entry : properties.entrySet()) {                String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {                    result.add(factoryTypeName, factoryImplementationName.trim());                }            }        }        cache.put(classLoader, result);return result;    }catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);    }}

上面代码需要读取的 FACTORIES_RESOURCE_LOCATION 最终路径的长这样,而这个是 spring 提供的一个工具类:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

它其实是去加载一个外部的文件,而这文件是在 spring-boot-autoconfigure-2.2.2.RELEASE.jar 包下的 META-INF/spring.factories,打开 spring.factories 文件:

# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\...

@EnableAutoConfiguration 就是从 classpath 中搜寻 META-INF/spring.factories 配置文件,并将其中 org.springframework.boot.autoconfigure.EnableutoConfiguration 对应的配置项通过反射 Java Refletion 实例化为对应的标注了 @Configuration 的 JavaConfig 形式的配置类,并加载到 IOC 容器中。

以入门项目为例,在项目中加入了 Web 环境依赖启动器,对应的 WebMvcAutoConfiguration 自动配置类就会生效,打开该自动配置类会发现,在该配置类中通过全注解配置类的方式对 Spring MVC 运行所需环境进行了默认配置,包括默认前缀、默认后缀、视图解析器、MVC 校验器等。而这些自动配置类的本质是传统 Spring MVC 框架中对应的 XML 配置文件,只不过在 Spring Boot 中以自动配置类的形式进行了预先配置。因此,在 Spring Boot 项目中加入相关依赖启动器后,基本上不需要任何配置就可以运行程序,当然,也可以对这些自动配置类中默认的配置进行更改。

总结:

因此 springboot 底层实现自动配置的步骤是:

1. springboot 应用启动。

2. @SpringBootApplication 起作用。

3. @EnableAutoConfiguration。

4. @AutoConfigurationPackage:这个组合注解主要是 @Import(AutoConfigurationPackages.Registrar.class),它通过将 Registrar 类导入到容器中,而 Registrar 类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到 springboot 创建管理的容器中。

5. @Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector 类导入到容器中;AutoConfigurationImportSelector 类作用是通过 selectImports 方法执行的过程中,会使用内部工具类 SpringFactoriesLoader,查找 classpath 上所有 jar 包中的 META-INF/spring.factories 进行加载,实现将配置类信息交给 SpringFactory 加载器进行一系列的容器创建过程。
@ComponentScan 注解

@ComponentScan 注解具体扫描的包的根路径由 Spring Boot 项目主程序启动类所在包位置决定,在扫描过程中由前面介绍的 @AutoConfigurationPackage 注解进行解析,从而得到 Spring Boot 项目主程序启动类所在包的具体位置。

总结
@SpringBootApplication 的注解的功能简单来说就是 3 个注解的组合注解:

|- @SpringBootConfiguration   // 通过 javaConfig 的方式来添加组件到 IOC 容器中   |- @Configuration|- @EnableAutoConfiguration   // 自动配置包,与 @ComponentScan 扫描到的添加到 IOC   |- @AutoConfigurationPackage   // 到 META-INF/spring.factories 中定义的 bean 添加到 IOC 容器中   |- @Import(AutoConfigurationImportSelector.class) // 包扫描|- @ComponentScan

SpringBoot 数据访问

Spring Boot 整合 MyBatis

MyBatis 是一款优秀的持久层框架,Spring Boot 官方虽然没有对 MyBatis 进行整合,但是 MyBatis 团队自行适配了对应的启动器,进一步简化了使用 MyBatis 进行数据的操作。

因为 Spring Boot 框架开发的便利性,所以实现 Spring Boot 与数据访问层框架(例如 MyBatis)的整合非常简单,主要是引入对应的依赖启动器,并进行数据库相关参数设置即可。

基础环境搭建
1)数据准备

在 MySQL 中,先创建了一个数据库 springbootdata,然后创建了两个表 t_articlet_comment 并向表中插入数据。其中评论表 t_commenta_id 与文章表 t_article 的主键 id 相关联。

# 创建数据库CREATE DATABASE IF NOT EXISTS springbootdata DEFAULT CHARACTER SET utf8;# 选择使用数据库USE springbootdata;

# 创建表 t_article 并插入相关数据DROP TABLE IF EXISTS t_article;CREATE TABLE t_article(    id      int(20) NOT NULL AUTO_INCREMENT COMMENT '文章id',    title   varchar(200) DEFAULT NULL COMMENT '文章标题',    content longtext COMMENT '文章内容',    PRIMARY KEY (id)) ENGINE = InnoDB AUTO_INCREMENT = 2 DEFAULT CHARSET=utf8;

INSERT INTO t_article VALUES (1, 'Spring Boot 基础入门', '从入门到精通讲解...');INSERT INTO t_article VALUES (2, 'Spring Cloud 基础入门', '从入门到精通讲解...');

# 创建表 t_comment 并插入相关数据DROP TABLE IF EXISTS t_comment;CREATE TABLE t_comment(    id      int(20) NOT NULL AUTO_INCREMENT COMMENT '评论id',    content longtext COMMENT '评论内容',    author  varchar(200) DEFAULT NULL COMMENT '评论作者',    a_id    int(20)      DEFAULT NULL COMMENT '关联的文章id',    PRIMARY KEY (id)) ENGINE = InnoDB AUTO_INCREMENT = 3 DEFAULT CHARSET=utf8;INSERT INTO t_comment VALUES (1, '很全、很详细', 'lucy', 1);INSERT INTO t_comment VALUES (2, '赞一个', 'tom', 1);INSERT INTO t_comment VALUES (3, '很详细', 'eric', 1);INSERT INTO t_comment VALUES (4, '很好,非常详细', '张三', 1);INSERT INTO t_comment VALUES (5, '很不错', '李四', 2);
2)创建项目,引入相应的启动器

使用 Spring Initializr 来初始化项目。

项目名:springbootmybatis

包名:com.renda

启动器:SQL 的 MyBatis Framework、MySQL Driver,Web 的 Spring Web

3)编写与数据库表 t_comment 和 t_article 对应的实体类 Comment 和 Article

com.renda.pojo.Comment

public class Comment {    private Integer id;    private String content;    private String author;    private Integer aId;    // getter setter toString ...}

com.renda.pojo.Article

public class Article {    private Integer id;    private String title;    private String content;    // getter setter toString ...}
4)编写配置文件

application.properties  更名为 application.yml。在 application.properties 配置文件中进行数据库连接配置:

spring:  datasource:    url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&characterEncoding=UTF-8    username: root    password: password
注解方式整合 Mybatis

需求:实现通过 ID 查询 Comment 信息。

1)创建一个对 t_comment 表数据操作的接口 CommentMapper

com.renda.mapper.CommentMapper

public interface CommentMapper {    @Select("select * from t_comment where id = #{id}")    Comment findById(Integer id);}
2)在 Spring Boot 项目启动类上添加 @MapperScan("xxx") 注解

com.renda.SpringbootmybatisApplication

@SpringBootApplication@MapperScan("com.renda.bootmybatis.mapper") //执行扫描mapper的包名public class SpringbootmybatisApplication {

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

}
3)编写测试方法

导入 Junit 的依赖,增加测试方法:

com.renda.SpringbootmybatisApplicationTests

@RunWith(SpringRunner.class)@SpringBootTestclass SpringbootmybatisApplicationTests {

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")    @Autowired    private CommentMapper commentMapper;

    @Test    void findCommentById() {        Comment comment = commentMapper.findById(1);        System.out.println(comment);    }

}

控制台中查询的 Comment 的 aId 属性值为 null,没有映射成功。这是因为编写的实体类 Comment 中使用了驼峰命名方式将 t_comment 表中的 a_id 字段设计成了 aId 属性,所以无法正确映射查询结果。

为了解决上述由于驼峰命名方式造成的表字段值无法正确映射到类属性的情况,可以在 Spring Boot 全局配置文件 application.yml 中添加开启驼峰命名匹配映射配置,示例代码如下:

mybatis:  configuration:    # 开启驼峰命名匹配映射    map-underscore-to-camel-case: true
配置文件的方式整合 MyBatis

第一、二步骤使用 Free Mybatis plugin 插件生成:使用 IDEA 连接 Database,然后选中要自动生成代码的表,右键 -> mybatis-generator -> 按照需求输入信息,点击 ok。

1)创建一个用于对数据库表 t_article 数据操作的接口 ArticleMapper
public interface ArticleMapper {    int deleteByPrimaryKey(Integer id);

    int insert(Article record);

    int insertSelective(Article record);

    Article selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(Article record);

    int updateByPrimaryKey(Article record);}
2)创建 XML 映射文件

resources 目录下创建一个统一管理映射文件的包 mapper,并在该包下编写与 ArticleMapper 接口方应的映射文件 ArticleMapper.xml

<?xml  version="1.0" encoding="UTF-8"?>mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.renda.mapper.ArticleMapper">  <resultMap id="BaseResultMap" type="com.renda.pojo.Article">    <id column="id" jdbcType="INTEGER" property="id" />    <result column="title" jdbcType="VARCHAR" property="title" />    <result column="content" jdbcType="VARCHAR" property="content" />  resultMap>  <sql id="Base_Column_List">    id, title, content  sql>  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">    select     <include refid="Base_Column_List" />    from t_article    where id = #{id,jdbcType=INTEGER}  select>  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">    delete from t_article    where id = #{id,jdbcType=INTEGER}  delete>  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.Article" useGeneratedKeys="true">    insert into t_article (title, content)    values (#{title,jdbcType=VARCHAR}, #{content,jdbcType=VARCHAR})  insert>  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.Article" useGeneratedKeys="true">    insert into t_article    <trim prefix="(" suffix=")" suffixOverrides=",">      <if test="title != null">        title,      if>      <if test="content != null">        content,      if>    trim>    <trim prefix="values (" suffix=")" suffixOverrides=",">      <if test="title != null">        #{title,jdbcType=VARCHAR},      if>      <if test="content != null">        #{content,jdbcType=VARCHAR},      if>    trim>  insert>  <update id="updateByPrimaryKeySelective" parameterType="com.renda.pojo.Article">    update t_article    <set>      <if test="title != null">        title = #{title,jdbcType=VARCHAR},      if>      <if test="content != null">        content = #{content,jdbcType=VARCHAR},      if>    set>    where id = #{id,jdbcType=INTEGER}  update>  <update id="updateByPrimaryKey" parameterType="com.renda.pojo.Article">    update t_article    set title = #{title,jdbcType=VARCHAR},      content = #{content,jdbcType=VARCHAR}    where id = #{id,jdbcType=INTEGER}  update>mapper>
3)配置 XML 映射文件路径

在项目中编写的 XML 映射文件,Spring Boot 并无从知晓,所以无法扫描到该自定义编写的 XML 配置文件,还必须在全局配置文件 application.yml 中添加 MyBatis 映射文件路径的配置,同时需要添加实体类别名映射路径,示例代码如下:

mybatis:  configuration:    # 开启驼峰命名匹配映射    map-underscore-to-camel-case: true  # 加载 resources/mapper 文件夹下的所有的 xml 文件  mapper-locations: classpath:mapper/*.xml  # 配置 XML 映射文件中指定的实体类别名路径  type-aliases-package: com.renda.pojo
4)编写单元测试进行接口方法测试
@RunWith(SpringRunner.class)@SpringBootTestclass SpringbootmybatisApplicationTests {

    @Autowired    private ArticleMapper articleMapper;

    @Test    void findArticleById() {        Article article = articleMapper.selectByPrimaryKey(1);        System.out.println(article);    }

}

Spring Boot 整合 Redis

1)添加Redis依赖包

在项目的 pom.xml 中添加如下:

<dependency>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-data-redisartifactId>dependency>
2)配置 Redis 数据库连接

在 application.yml 中配置redis数据库连接信息,如下:

spring:  redis:    # Redis 服务器地址    host: 192.168.186.128    # Redis 服务器连接端口    port: 6379    jedis:      pool:        # 连接池最大连接数(使用负值表示没有限制)        max-active: 18        # 连接池最大阻塞等待时间(使用负值表示没有限制)        max-wait: 3000        # 连接池中的最大空闲连接        max-idle: 20        # 连接池中的最小空闲连接        min-idle: 2    # 连接超时时间(毫秒)    timeout: 3000    # Redis 数据库索引(默认为 0)    database: 0
3)编写 Redis 操作工具类

将 RedisTemplate 实例包装成一个工具类,便于对 redis 进行数据操作。

package com.renda.util;

import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/** * @author Renda Zhang * @since 2020-10-30 1:00 */@Componentpublic class RedisUtils {

    @Autowired    private RedisTemplate redisTemplate;

    /**     * 读取缓存     */    public Object get(final String key) {        return redisTemplate.opsForValue().get(key);    }

    /**     * 写入缓存     */    public boolean set(String key, Object value) {        boolean result = false;        try {            redisTemplate.opsForValue().set(key, value, 1, TimeUnit.DAYS);            result = true;        } catch (Exception e) {            e.printStackTrace();        }        return result;    }

    /**     * 更新缓存     */    public boolean getAndSet(final String key, String value) {        boolean result = false;        try {            redisTemplate.opsForValue().getAndSet(key, value);            result = true;        } catch (Exception e) {            e.printStackTrace();        }        return result;    }

    /**     * 删除缓存     */    public boolean delete(final String key) {        boolean result = false;        try {            redisTemplate.delete(key);            result = true;        } catch (Exception e) {            e.printStackTrace();        }        return result;    }}
4)测试

写一个测试用例类来完成对 redis 的整合。

@RunWith(SpringRunner.class)@SpringBootTestclass SpringbootmybatisApplicationTests {

    // 写入,key:1,value:mysql 数据库中 id 为 1 的 article 记录    @Autowired    private RedisUtils redisUtils;

    @Test    void writeRedis() {        redisUtils.set("1", articleMapper.selectByPrimaryKey(1));        System.out.println("success");    }

    @Test    void readRedis() {        Article article = (Article) redisUtils.get("1");        System.out.println(article);    }

}

SpringBoot 视图技术

支持的视图技术

前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复杂的问题、实现了前后端分离开发。Spring Boot 框架对很多常用的模板引擎技术(如:FreeMarker、Thymeleaf、Mustache 等)提供了整合支持。

Spring Boot 不太支持常用的 JSP 模板,并且没有提供对应的整合配置,这是因为使用嵌入式 Servlet 容器的 Spring Boot 应用程序对于 JSP 模板存在一些限制 :

  • 在 Jetty 和 Tomcat 容器中,Spring Boot 应用被打包成 war 文件可以支持 JSP。但 Spring Boot 默认使用嵌入式 Servlet 容器以 JAR 包方式进行项目打包部署,这种 JAR 包方式不支持 JSP。

  • 如果使用 Undertow 嵌入式容器部署 Spring Boot 项目,也不支持 JSP 模板。(Undertow  是红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器)

  • Spring Boot 默认提供了一个处理请求路径 “/error” 的统一错误处理器,返回具体的异常信息。使用 JSP 模板时,无法对默认的错误处理器进行覆盖,只能根据 Spring Boot 要求在指定位置定制错误页面。

Thymeleaf

Thymeleaf 是一种现代的基于服务器端的 Java 模板引擎技术,也是一个优秀的面向 Java 的 XML、XHTML、HTML5 页面模板,它具有丰富的标签语言、函数和表达式,在使用 Spring Boot 框架进行页面设计时,一般会选择 Thymeleaf 模板。

Thymeleaf 语法

在 HTML 页面上使用 Thymeleaf 标签,Thymeleaf 标签能够动态地替换掉静态内容,使页面动态展示。

为了更直观的认识 Thymeleaf,下面展示一个在 HTML 文件中嵌入了 Thymeleaf 的页面文件,示例代码如下:

html><html lang="en" xmlns:th="http://www.thymeleaf.org">    <head>        <meta charset="UTF-8">        <link rel="stylesheet" type="text/css" media="all"href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />        <title>Thymeleaftitle>    head>    <body>        <p th:text="${hello}">Hello Thymeleafp>    body>html>

上述代码中,“xmlns:th="http://www.thymeleaf.org" 用于引入 Thymeleaf 模板引擎标签,使用关键字 th 标注标签是 Thymeleaf 模板提供的标签,其中,th:href="@{/css/gtvg.css}" 用于引入外联样式文件,th:text="${hello}" 用于动态显示标签文本内容。

常用标签:

  • th:insert - 布局标签,替换内容到引入的文件

  • th:replace - 页面片段包含(类似 JSP 中的 include 标签)

  • th:each - 元素遍历(类似 JSP 中的 c:forEach 标签)

  • th:if - 条件判断,如果为真

  • th:unless - 条件判断,如果为假

  • th:switch - 条件判断,进行选择性匹配

  • th:case - 条件判断,进行选择性匹配

  • th:value - 属性值修改,指定标签属性值

  • th:href - 用于设定链接地址

  • th:src - 用于设定链接地址

  • th:text - 用于指定标签显示的文本内容

标准表达式:

  • 变量表达式-  ${…}

  • 选择变量表达式 - *{…}

  • 消息表达式 - #{…}

  • 链接 URL 表达式 - @{…}

  • 片段表达式 - ~{…}

变量表达式 `${…}`

变量表达式 ${...} 主要用于获取上下文中的变量值,示例代码如下:

<p th:text="${title}">这是标题p>

示例使用了 Thymeleaf 模板的变量表达式 ${...} 用来动态获取 P 标签中的内容,如果当前程序没有启动或者当前上下文中不存在 title 变量,该片段会显示标签默认值“这是标题”;如果当前上下文中存在 title 变量并且程序已经启动,当前 P 标签中的默认文本内容将会被 title 变量的值所替换,从而达到模板引擎页面数据动态替换的效果。

同时,Thymeleaf 为变量所在域提供了一些内置对象,具体如下所示:

#ctx:上下文对象#vars:上下文变量#locale:上下文区域设置#request:(仅限 Web Context)HttpServletRequest 对象#response:(仅限 Web Context)HttpServletResponse 对象#session:(仅限 Web Context)HttpSession 对象#servletContext:(仅限 Web Context)ServletContext 对象

结合上述内置对象的说明,假设要在 Thymeleaf 模板引擎页面中动态获取当前国家信息,可以使用 #locale 内置对象,示例代码如下:

The locale country is: <span th:text="${#locale.country}">Chinaspan>

上述代码中,使用 th:text="${#locale.country}" 动态获取当前用户所在国家信息,其中标签内默认内容为 China,程序启动后通过浏览器查看当前页面时,Thymeleaf 会通过浏览器语言设置来识别当前用户所在国家信息,从而实现动态替换。

选择变量表达式 `*{…}`

选择变量表达式和变量表达式用法类似,一般用于从被选定对象而不是上下文中获取属性值,如果没有选定对象,则和变量表达式一样,示例代码如下:

<div th:object="${book}">    <p>titile: <span th:text="*{title}">标题span>.p>div>

*{title} 选择变量表达式获取当前指定对象 book 的 title 属性值。

消息表达式 `#{…}`

消息表达式 #{...} 主要用于 Thymeleaf 模板页面国际化内容的动态替换和展示,使用消息表达式 #{...} 进行国际化设置时,还需要提供一些国际化配置文件。

链接表达式 `@{…}`

链接表达式 @{...} 一般用于页面跳转或者资源的引入,在 Web 开发中占据着非常重要的地位,并且使用也非常频繁,示例代码如下:

<a th:href="@{http://localhost:8080/order/details(orderId=${o.id})}">viewa> <a th:href="@{/order/details(orderId=${o.id},pid=${p.id})}">viewa>

上述代码中,链接表达式 @{...} 分别编写了绝对链接地址和相对链接地址。在有参表达式中,需要按照 @{路径(参数名称=参数值,参数名称=参数值...)} 的形式编写,同时该参数的值可以使用变量表达式来传递动态参数值。

片段表达式 `~{…}`

片段表达式 ~{...} 用来标记一个片段模板,并根据需要移动或传递给其他模板。其中,最常见的用法是使用 th:insertth:replace 属性插入片段,示例代码如下:

<div th:insert="~{thymeleafDemo::title}">div>

上述代码中,使用 th:insert 属性将 title 片段模板引用到该标签中。thymeleafDemo 为模板名称,Thymeleaf 会自动查找 /resources/templates/ 目录下的 thymeleafDemo 模板,title 为片段名称。

基本使用

1) Thymeleaf 模板基本配置

首先在 Springbootdemo2 项目中使用 Thymeleaf 模板,首先必须保证引入 Thymeleaf 依赖,示例代码如下:

<dependency>    <groupId>org.springframework.bootgroupId>    <artifactId>spring-boot-starter-thymeleafartifactId>dependency>

其次,在全局配置文件中配置 Thymeleaf 模板的一些参数。一般 Web 项目都会使用下列配置,示例代码如:

spring:  thymeleaf:    # 在开发阶段,为了验证建议关闭缓存    cache: true    # 模板编码    encoding: UTF-8    # 应用于模板的模板模式    mode: HTML5    # 指定模板页面存放路径    prefix: classpath:/templates/    # 指定模板页面名称的后缀    suffix: .html

上述配置中,spring.thymeleaf.cache 表示是否开启 Thymeleaf 模板缓存,默认为 true,在开发过程中通常会关闭缓存,保证项目调试过程中数据能够及时响应;spring.thymeleaf.prefix 指定了 Thymeleaf 模板页面的存放路径,默认为classpath:/templates/spring.thymeleaf.suffix 指定了 Thymeleaf 模板页面的名称后缀,默认为 .html

2)静态资源的访问

开发 Web 应用时,难免需要使用静态资源。Spring boot 默认设置了静态资源的访问路径。

使用 Spring Initializr 方式创建的 Spring Boot 项目,默认生成了一个 resources 目录,在 resources 目录中的 public、resources、static 三个子目录下,Spring boot 默认会挨个从public、resources、static 里面查找静态资源。

完成数据的页面展示

1)创建 Spring Boot 项目,引入 Thymeleaf 依赖。

2)编写配置文件。

编辑 application.yml 全局配置文件,在该文件中对 Thymeleaf 模板页面的数据缓存进行设置。

# thymeleaf 页面缓存设置(默认为 true),开发中方便调试应设置为 false,上线稳定后应保持默认 truespring:  thymeleaf:    cache: false

使用 spring.thymeleaf.cache=false 将 Thymeleaf 默认开启的缓存设置为了 false,用来关闭模板页面缓存。

3)创建 web 控制类

在项目中创建名为 com.renda.controller 的包,并在该包下创建一个用于前端模板页面动态数据替换效果测试的访问实体类 LoginController

@Controllerpublic class LoginController {

    @RequestMapping("/toLogin")    public String toLoginView(Model model){        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));        return "login"; // resources/templates/login.html    }

}

toLoginView() 方法用于向登录页面 login.html 跳转,同时携带了当前年份信息 currentYear

4)创建模板页面并引入静态资源文件。

classpath:/templates/ 目录下引入一个用户登录的模板页面 login.html

html><html lang="en" xmlns:th="http://www.thymeleaf.org"><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no">    <title>用户登录界面title>    <link th:href="@{../static/css/bootstrap.min.css}" rel="stylesheet">    <link th:href="@{../static/css/signin.css}" rel="stylesheet">head><body class="text-center">

<form class="form-signin">    <img class="mb-4" th:src="@{../static/img/login.png}" width="72" height="72">    <h1 class="h3 mb-3 font-weight-normal">请登录h1>    <input type="text" class="form-control"th:placeholder="用户名" required="" autofocus="">    <input type="password" class="form-control"th:placeholder="密码" required="">    <div class="checkbox mb-3">        <label>            <input type="checkbox" value="remember-me"> 记住我        label>    div>    <button class="btn btn-lg btn-primary btn-block" type="submit" >登录button>    <p class="mt-5 mb-3 text-muted">© <span th:text="${currentYear}">2019span>-<span th:text="${currentYear}+1">2020span>p>form>body>html>

通过 xmlns:th="http://www.thymeleaf.org 引入了 Thymeleaf 模板标签;

使用 th:hrefth:src 分别引入了两个外联的样式文件和一个图片;

使用 th:text 引入了后台动态传递过来的当前年份 currentYear

5)效果测试

可以看出,登录页面 login.html 显示正常,在页面底部动态显示了当前日期 2020-2021,而不是文件中的静态数字 2019-2020。这进一步说明了 Spring Boot 与 Thymeleaf 整合成功,完成了静态资源的引入和动态数据的显示。

SpringBoot 实战演练

实战技能补充:lombok

<dependency>    <groupId>org.projectlombokgroupId>    <artifactId>lombokartifactId>    <version>1.18.12version>

    <scope>providedscope>dependency>

需求:实现用户的 CRUD 功能

初始化数据库信息:

DROP TABLE IF EXISTS `user`;CREATE TABLE `user`(    id int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',    username varchar(100) DEFAULT NULL COMMENT '用户名',    password varchar(100) DEFAULT NULL COMMENT '密码',    birthday varchar(100) DEFAULT NULL COMMENT '生日',    PRIMARY KEY (id)) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8;INSERT INTO `user` VALUES (1, 'zhangsan', '123', '2020-10-1');INSERT INTO `user` VALUES (2, 'lisi', '123', '2020-10-2');INSERT INTO `user` VALUES (3, 'wangwu', '123', '2020-10-10');INSERT INTO `user` VALUES (4, 'yuanjing', '123', '2020-10-11');

1)创建 springboot 工程

使用 Spring Initializr 新建一个工程 springbootuser,选择依赖:Developer Tools -> Lombok,Web -> Spring Web,SQL -> [MyBatis Framework、MySQL Driver]。

2)编辑 pom.xml

<dependency>    <groupId>com.alibabagroupId>    <artifactId>druidartifactId>    <version>1.1.3version>dependency>

3)User 实体类编写

使用 FreeMyBatis 生成实体类。

使用 FreeMyBatis 生成 UserMapper 相关的代码。

com.renda.pojo.User

@Data // Lombok 自动生成 getter 和 setterpublic class User implements Serializable {    /**     * 用户id     */    private Integer id;

    /**     * 用户名     */    private String username;

    /**     * 密码     */    private String password;

    /**     * 生日     */    private String birthday;

    private static final long serialVersionUID = 1L;}

4)UserMapper 编写及 xml 文件

com.renda.mapper.UserMapper

public interface UserMapper {    int deleteByPrimaryKey(Integer id);

    int insert(User record);

    int insertSelective(User record);

    User selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(User record);

    int updateByPrimaryKey(User record);}

src\main\resources\mapper\UserMapper.xml

<?xml  version="1.0" encoding="UTF-8"?>mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.renda.mapper.UserMapper">  <resultMap id="BaseResultMap" type="com.renda.pojo.User">    <id column="id" jdbcType="INTEGER" property="id" />    <result column="username" jdbcType="VARCHAR" property="username" />    <result column="password" jdbcType="VARCHAR" property="password" />    <result column="birthday" jdbcType="VARCHAR" property="birthday" />  resultMap>  <sql id="Base_Column_List">    id, username, `password`, birthday  sql>  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">    select     <include refid="Base_Column_List" />    from user    where id = #{id,jdbcType=INTEGER}  select>  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">    delete from user    where id = #{id,jdbcType=INTEGER}  delete>  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true">    insert into user (username, `password`, birthday      )    values (#{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR}, #{birthday,jdbcType=VARCHAR}      )  insert>  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.renda.pojo.User" useGeneratedKeys="true">    insert into user    <trim prefix="(" suffix=")" suffixOverrides=",">      <if test="username != null">        username,      if>      <if test="password != null">        `password`,      if>      <if test="birthday != null">        birthday,      if>    trim>    <trim prefix="values (" suffix=")" suffixOverrides=",">      <if test="username != null">        #{username,jdbcType=VARCHAR},      if>      <if test="password != null">        #{password,jdbcType=VARCHAR},      if>      <if test="birthday != null">        #{birthday,jdbcType=VARCHAR},      if>    trim>  insert>  <update id="updateByPrimaryKeySelective" parameterType="com.renda.pojo.User">    update user    <set>      <if test="username != null">        username = #{username,jdbcType=VARCHAR},      if>      <if test="password != null">        `password` = #{password,jdbcType=VARCHAR},      if>      <if test="birthday != null">        birthday = #{birthday,jdbcType=VARCHAR},      if>    set>    where id = #{id,jdbcType=INTEGER}  update>  <update id="updateByPrimaryKey" parameterType="com.renda.pojo.User">    update user    set username = #{username,jdbcType=VARCHAR},      `password` = #{password,jdbcType=VARCHAR},      birthday = #{birthday,jdbcType=VARCHAR}    where id = #{id,jdbcType=INTEGER}  update>mapper>

5)UserService 接口及实现类编写

com.renda.service.UserService

public interface UserService {

    /**     * 查询所有     */    List queryAll();

    /**     * 通过 ID 查询     */    User findById(Integer id);

    /**     * 新增     */    void insert(User user);

    /**     * 通过 ID 删除     */    void deleteById(Integer id);

    /**     * 修改     */    void update(User user);

}

com.renda.service.impl.UserServiceImpl

@Servicepublic class UserServiceImpl implements UserService {

    @Autowired    private UserMapper userMapper;

    @Override    public List queryAll() {        return userMapper.queryAll();    }

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

    @Override    public void insert(User user) {        // 将除 id 外所有的列都拼接入 SQL 语句        // userMapper.insert(user);        // 只将不为空的列才拼接入 SQL 语句(优先使用,减少高并发下数据传输)        userMapper.insertSelective(user);    }

    @Override    public void deleteById(Integer id) {        userMapper.deleteByPrimaryKey(id);    }

    @Override    public void update(User user) {        userMapper.updateByPrimaryKeySelective(user);    }

}

6)UserController 编写

com.renda.controller.UserController

/** * restful 格式进行访问 * 查询:GET * 新增: POST * 更新:PUT * 删除: DELETE * * @author Renda Zhang * @since 2020-10-31 1:36 */@RestController@RequestMapping("/user")public class UserController {

    @Autowired    private UserService userService;

    /**     * 查询所有     */    @GetMapping("/query")    public List queryAll(){        return userService.queryAll();    }

    /**     * 通过 ID 查询     */    @GetMapping("/query/{id}")    public User queryById(@PathVariable Integer id){        return userService.findById(id);    }

    /**     * 删除     */    @DeleteMapping("/delete/{id}")    public String delete(@PathVariable Integer id){        userService.deleteById(id);        return "删除成功";    }

    /**     * 新增     */    @PostMapping("/insert")    public String insert(User user){        userService.insert(user);        return "新增成功";    }

    /**     * 修改     */    @PutMapping("/update")    public String update(User user){        userService.update(user);        return "修改成功";    }

}

7)全局配置文件 application.yml

重命名 application.propertiesapplication.yml

src\main\resources\application.yml

# 服务器配置server:  port: 8090

spring:  # 数据源配置  datasource:    name: druid    type: com.alibaba.druid.pool.DruidDataSource    url: jdbc:mysql://localhost:3306/springbootdata?characterEncoding=utf-8&serverTimezone=UTC    username: root    password: password

# 整合 MyBatismybatis:  # 声明 MyBatis 文件所在的位置  mapper-locations: classpath:mapper/*Mapper.xml

8)启动类

com.renda.SpringbootuserApplication

@SpringBootApplication// 使用的 Mybatis, 扫描 com.renda.mapper@MapperScan("com.renda.mapper")public class SpringbootuserApplication {

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

}

10)使用 Postman 测试

  • GET http://localhost:8090/user/query

  • GET http://localhost:8090/user/query/1

  • POST http://localhost:8090/user/insert?username=renda&password=123456&birthday=1995-12-27

  • PUT http://localhost:8090/user/update?username=RendaZhang&password=00000&birthday=1997-12-27&id=5

  • DELETE http://localhost:8090/user/delete/5

Spring Boot 项目部署

需求:将 Spring Boot 项目使用 maven 指令打成 jar 包并运行测试。

分析

1)添加打包组件将项目中的资源、配置、依赖包打到一个 jar 包中;可以使用 maven 的 package 命令。

2)部署:java -jar 包名

步骤实现

确保 pom.xml 文件中有如下的打包组件:

<build>    <plugins>

        <plugin>            <groupId>org.springframework.bootgroupId>            <artifactId>spring-boot-maven-pluginartifactId>        plugin>    plugins>build>

部署运行:

java -jar springbootuser-0.0.1-SNAPSHOT.jar

springboot pom文件指定jdk_Spring Boot 入门相关推荐

  1. springboot 扫描文件夹_Spring Boot学习07_自定义starter

    查看Spring Boot的源码,starter包并没有真正的代码,代码都在autoconfigurer包中,所以我也可以照葫芦画瓢,创建一个starter项目和一个autoconfigurer项目, ...

  2. springboot pom文件添加mysql组件_SpringBoot整合mybatis-plus+druid组件,实现增删改查

    前言 本篇文章主要介绍的是SpringBoot整合mybatis-plus,实现增删改查. GitHub源码链接位于文章底部. 建库建表 创建springboot数据库,创建t_user表,字段id主 ...

  3. springboot pom文件添加mysql组件_SpringBoot+Mybatis 通过databaseIdProvider支持多数据库

    概述 本人最近接到一个任务,将一个系统改成同时兼容Oracle和PostgreSQL(原来是仅支持Oracle).虽然大部分的sql语句通用,但是还有许多语法存在差异,所以我们可以通过mybatis自 ...

  4. springboot pom文件基本配置

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  5. spring boot入门 -- 介绍和第一个例子

    "越来越多的企业选择使用spring boot 开发系统,spring boot牛在什么地方?难不难学?心动不如行动,让我们一起开始学习吧!" 使用Spring boot ,可以轻 ...

  6. [maven][spring boot] mvn -f 指定pom文件

    前言 spring boot 2.0 + mvn 3.5 项目 测试环境中运行spring boot时,需要指定属性文件为:application-test 使用 方式时,指定属性文件未起效 经过尝试 ...

  7. Spring Boot概述与入门特点配置方式注入方式yim配置文件与多文件配置Spring Boot自动配置原理lombok应用

    1. Spring Boot概述 Spring Boot是Spring项目中的一个子工程,与我们所熟知的Spring-framework 同属于spring的产品: 首页Spring Boot简介可以 ...

  8. springboot系列课程笔记-第一章-Spring Boot入门

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

  9. springboot 创建地址_手把手教你Spring Boot入门开发

    前言:springboot如今在技术领域非常流行,不仅能够简化web项目的开发,提升开发效率,而且也是面试官最喜欢的问题之一,今天笔者对springboot做一个简要入门实例讲解,抛砖引玉. 1. S ...

最新文章

  1. screen史上最全教程
  2. 深度学习模型保存_Web服务部署深度学习模型
  3. 2016年CCF第七次测试 俄罗斯方块
  4. 学习笔记(1):uni-app实战社区交友类app开发-引入自定义图标库
  5. Atitit 动态按钮图片背景颜色与文字组合解决方案
  6. opencv python 生成画布_第16章 坚持一百秒(《Python趣味创意编程》教学视频)
  7. linux c 进程池 简单实现
  8. 概率论与数理统计——Chapter0
  9. 网易邮箱发送显示服务器出错,网易邮件发送不出去的错误代码详解 (MI:SFQ错误等)...
  10. 电脑高手常用技巧应用全接解
  11. python-数据分析-pandas (1)
  12. S32K144调试记录(一)
  13. MATLAB一元线性回归(最小二乘法)小例子
  14. android广播 有序 无序,Android全局广播(无序,有序)
  15. 谁能解释一下,这是什么骚操作???
  16. 如何15天之内发表一篇ei会议英文论文?
  17. 如何生活而不是活着?
  18. 15/18位身份证号码验证
  19. 狼图腾-励志有声小说
  20. php是菲律宾吗,“PHP”是“Philippine Peso”的缩写,意思是“菲律宾比索”

热门文章

  1. leetcode 1008. Construct Binary Search Tree from Preorder Traversal | 1008. 前序遍历构造二叉搜索树(Java)
  2. leetcode 485. 最大连续 1 的个数(Java版)
  3. C语言 数据结构 栈(用链表实现)
  4. 小师妹学JavaIO之:File文件系统
  5. OutOfMemory (OOM)的类型与检测
  6. 社交搜索Graph Search技术解析
  7. 《大数据日知录:架构与算法》前言
  8. 【GIF动画+完整可运行源代码】C++实现 归并排序——十大经典排序算法之五
  9. ConcurrentHashMap源码分析(转载)
  10. linux c语言 信号,linux下基于C语言的信号编程实例